Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7547 lines
264 KiB

/*++
Copyright (c) 1990-2000 Microsoft Corporation
Module Name:
ARP.C - LAN arp module.
Abstract:
This file implements arp framing for IP layer on the upper edge
and interfaces with ndis driver on the lower edge.
Author:
[Environment:]
kernel mode only
[Notes:]
optional-notes
Revision History:
--*/
#include "precomp.h"
//*** arp.c - ARP routines.
//
// This file containes all of the ARP related routines, including
// table lookup, registration, etc.
//
// ARP is architected to support multiple protocols, but for now
// it in only implemented to take one protocol (IP). This is done
// for simplicity and ease of implementation. In the future we may
// split ARP out into a seperate driver.
#include "arp.h"
#include "arpdef.h"
#include "iproute.h"
#include "iprtdef.h"
#include "arpinfo.h"
#include "tcpipbuf.h"
#include "mdlpool.h"
#include "ipifcons.h"
#define NDIS_MAJOR_VERSION 0x4
#define NDIS_MINOR_VERSION 0
#ifndef NDIS_API
#define NDIS_API
#endif
#define PPP_HW_ADDR "DEST"
#define PPP_HW_ADDR_LEN 4
#if DBG
uint fakereset = 0;
#endif
extern void IPReset(void *Context);
UINT cUniAdapters = 0;
extern uint EnableBcastArpReply;
static ulong ARPLookahead = LOOKAHEAD_SIZE;
static const uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06";
static const uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70";
static const uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00";
static const uchar ARCBcst[] = "\x00\x00\xd5";
ulong TRFunctionalMcast = 0;
//canonical or non-canonical?
static uchar TRMcst[] = "\x10\x40\xc0\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x82\x70";
//#define TR_MCAST_FUNCTIONAL_ADDRESS 0xc00000040000
//canonical form
#define TR_MCAST_FUNCTIONAL_ADDRESS 0x030000200000
static uchar TRNetMcst[] = "\x00\x04\x00\x00";
static const uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00";
static const uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00";
static const uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06";
static const uchar ENetPtrnMsk[] = "\x00\x30";
static const uchar ENetSNAPPtrnMsk[] = "\x00\xC0\x3f";
//static const uchar TRPtrnMsk[] = "\x03\x00";
//static const uchar TRSNAPPtrnMsk[] = "\x03\xC0\x3f";
static const uchar TRPtrnMsk[] = "\x00\x00"; //NO AC/FC bits need to be checked
static const uchar TRSNAPPtrnMsk[] = "\x00\xC0\x3f";
static const uchar FDDIPtrnMsk[] = "\x01\x00";
static const uchar FDDISNAPPtrnMsk[] = "\x01\x70\x1f";
static const uchar ARCPtrnMsk[] = "\x01";
static const uchar ARPPtrnMsk[] = "\x80\x00\x00\x0F";
static const uchar ARCARPPtrnMsk[] = "\x80\xC0\x03";
NDIS_STATUS __stdcall DoWakeupPattern(void *Context,
PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc, ushort protoid,
BOOLEAN AddPattern);
NDIS_STATUS ARPWakeupPattern(ARPInterface *Interface, IPAddr Address,
BOOLEAN AddPattern);
NDIS_STATUS AddrNotifyLink(ARPInterface *Interface);
static WCHAR ARPName[] = TCP_NAME;
NDIS_HANDLE ARPHandle; // Our NDIS protocol handle.
uint ArpCacheLife;
extern uint ArpMinValidCacheLife;
uint sArpAlwaysSourceRoute; // True if we always send ARP requests
uint ArpRetryCount; // retries for arp request with source
// route info on token ring.
uint sIPAlwaysSourceRoute;
extern uchar TrRii;
extern PDRIVER_OBJECT IPDriverObject;
extern DisableTaskOffload;
extern NDIS_STATUS __stdcall IPPnPEvent(void *, PNET_PNP_EVENT PnPEvent);
extern NDIS_STATUS GetIPConfigValue(NDIS_HANDLE Handle, PUNICODE_STRING IPConfig);
extern VOID IPUnload(IN PDRIVER_OBJECT DriverObject);
extern BOOLEAN CopyToNdisSafe(
PNDIS_BUFFER DestBuf,
PNDIS_BUFFER *ppNextBuf,
uchar *SrcBuf,
uint Size,
uint *StartOffset);
extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS);
extern void IPULUnloadNotify(void);
extern void NotifyOfUnload(void);
extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE * Handle);
extern int IsLLInterfaceValueNull(NDIS_HANDLE Handle);
extern void CloseIFConfig(NDIS_HANDLE Handle);
BOOLEAN QueryAndSetOffload(ARPInterface *ai);
ARPTableEntry *CreateARPTableEntry(ARPInterface *Interface, IPAddr Destination,
CTELockHandle *Handle, void *UserArp);
NDIS_STATUS NDIS_API
ARPRcvIndicationNew(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header,
uint HeaderSize, void *Data, uint Size, uint TotalSize,
PNDIS_BUFFER pNdisBuffer, PINT pClientCnt);
void
CompleteIPSetNTEAddrRequestDelayed(CTEEvent *WorkerThreadEvent, PVOID Context);
// Tables for bitswapping.
const uchar SwapTableLo[] =
{
0, // 0
0x08, // 1
0x04, // 2
0x0c, // 3
0x02, // 4
0x0a, // 5,
0x06, // 6,
0x0e, // 7,
0x01, // 8,
0x09, // 9,
0x05, // 10,
0x0d, // 11,
0x03, // 12,
0x0b, // 13,
0x07, // 14,
0x0f // 15
};
const uchar SwapTableHi[] =
{
0, // 0
0x80, // 1
0x40, // 2
0xc0, // 3
0x20, // 4
0xa0, // 5,
0x60, // 6,
0xe0, // 7,
0x10, // 8,
0x90, // 9,
0x50, // 10,
0xd0, // 11,
0x30, // 12,
0xb0, // 13,
0x70, // 14,
0xf0 // 15
};
// Table of source route maximum I-field lengths for token ring.
const ushort IFieldSize[] =
{
516,
1500,
2052,
4472,
8191
};
#define LF_BIT_SHIFT 4
#define MAX_LF_BITS 4
//
// Disposable init or paged code.
//
void FreeARPInterface(ARPInterface * Interface);
void ARPOpen(void *Context);
void NotifyConflictProc(CTEEvent * Event, void *Context);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ARPInit)
#pragma alloc_text(PAGE, ARPOpen)
#pragma alloc_text(PAGELK, ARPRegister)
#pragma alloc_text(PAGE, NotifyConflictProc)
#endif // ALLOC_PRAGMA
LIST_ENTRY ArpInterfaceList;
CACHE_LINE_KSPIN_LOCK ArpInterfaceListLock;
HANDLE ArpEnetHeaderPool;
HANDLE ArpAuxHeaderPool;
#define BUFSIZE_ENET_HEADER_POOL sizeof(ENetHeader) + sizeof(ARPHeader)
#define BUFSIZE_AUX_HEADER_POOL ARP_MAX_MEDIA_TR + (2 * sizeof(ARPHeader))
//
// Support Structs for DoNDISRequest (BLOCKING & NON-BLOCKING)
//
typedef struct _RequestBlock {
NDIS_REQUEST Request; // Request structure we'll use
ULONG Blocking; // ? Is this Request Blocking ?
CTEBlockStruc Block; // Structure for blocking on. No longer use
// ai_block since multiple requests can
// occur simultaneously.
// ai_block is now only used for blocking on
// opening and closing the NDIS adapter.
ULONG RefCount; // Reference count (only used for blocking).
// Reference counting is required for Windows ME since KeWaitForSingleObject
// can fail (when the event is NOT set) and we need to protect the memory
// until completion.
} RequestBlock;
// This prototype enables DoNDISRequest to compile without errors
void NDIS_API
ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST pRequest,
NDIS_STATUS Status);
//* FillARPControlBlock
//
// A utility routine to transfer a physical address into an ARPControlBlock,
// taking into account different MAC address formats.
//
// Entry:
// Interface - the ARPInterface which identifies the media
// Entry - the ARP entry containing the MAC address
// ArpContB - the control-block to be filled
//
__inline
NDIS_STATUS
FillARPControlBlock(ARPInterface* Interface, ARPTableEntry* Entry,
ARPControlBlock* ArpContB)
{
ENetHeader *EHdr;
TRHeader *TRHdr;
FDDIHeader *FHdr;
ARCNetHeader *AHdr;
uint Size;
NDIS_STATUS Status;
if (Interface->ai_media == NdisMediumArcnet878_2) {
if (!ArpContB->PhyAddrLen) {
return NDIS_STATUS_BUFFER_OVERFLOW;
}
Status = NDIS_STATUS_SUCCESS;
} else if (ArpContB->PhyAddrLen < ARP_802_ADDR_LENGTH) {
Size = ArpContB->PhyAddrLen;
Status = NDIS_STATUS_BUFFER_OVERFLOW;
} else {
Size = ARP_802_ADDR_LENGTH;
Status = NDIS_STATUS_SUCCESS;
}
switch (Interface->ai_media) {
case NdisMedium802_3:
EHdr = (ENetHeader *) Entry->ate_addr;
RtlCopyMemory(ArpContB->PhyAddr, EHdr->eh_daddr, Size);
ArpContB->PhyAddrLen = Size;
break;
case NdisMedium802_5:
TRHdr = (TRHeader *) Entry->ate_addr;
RtlCopyMemory(ArpContB->PhyAddr, TRHdr->tr_daddr, Size);
ArpContB->PhyAddrLen = Size;
break;
case NdisMediumFddi:
FHdr = (FDDIHeader *) Entry->ate_addr;
RtlCopyMemory(ArpContB->PhyAddr, FHdr->fh_daddr, Size);
ArpContB->PhyAddrLen = Size;
break;
case NdisMediumArcnet878_2:
AHdr = (ARCNetHeader *) Entry->ate_addr;
ArpContB->PhyAddr[0] = AHdr->ah_daddr;
ArpContB->PhyAddrLen = 1;
break;
default:
ASSERT(0);
}
return Status;
}
//* DoNDISRequest - Submit a (NON) BLOCKING request to an NDIS driver
//
// This is a utility routine to submit a general request to an NDIS
// driver. The caller specifes the request code (OID), a buffer and
// a length. This routine allocates a request structure, fills it in, &
// submits the request.
//
// If the call is non-blocking, any memory allocated is deallocated
// in ARPRequestComplete. Also as this callback is shared by both
// DoNDISRequest blocking and non-blocking, we suffix the request
// with a ULONG that tells ARPRequestComplete if this request is a
// blocking request or not. If the request is non blocking, then the
// ARPRequestComplete reclaims the memory allocated on the heap
//
// Important:
// Allocate Info, which points to the Information Buffer passed to
// NdisRequest, on the HEAP, if this request does not block. This
// memory is automatically deallocated by ARPRequestComplete
//
// If the call is blocking, the request memory can be allocated on the
// STACK. When we complete the request, the request on the stack
// will automatically get unwound.
//
// Entry:
// Adapter - A pointer to the ARPInterface adapter structure.
// Request - Type of request to be done (Set or Query)
// OID - Value to be set/queried.
// Info - A pointer to the info buffer
// Length - Length of data in the buffer
// Needed - On return, filled in with bytes needed in buffer
// Blocking - Whether NdisRequest is completed synchronously
//
// Exit:
// Status - BLOCKING req - SUCCESS or some NDIS error code
// NON-BLOCKING - SUCCESS, PENDING or some error
//
NDIS_STATUS
DoNDISRequest(ARPInterface * Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID,
VOID * Info, UINT Length, UINT * Needed, BOOLEAN Blocking)
{
RequestBlock *pReqBlock;
NDIS_STATUS Status;
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST,
(DTEXT("+DoNDISRequest(%x, %x, %x, %x, %d, %x, %x\n"),
Adapter, RT, OID, Info, Length, Needed, Blocking));
if (Adapter->ai_state == INTERFACE_DOWN || Adapter->ai_handle == NULL) {
return NDIS_STATUS_ADAPTER_NOT_READY;
}
//Check if we need to do Task_offload query.
//To prevent recursion, TASK_OFFLOAD_EX is a special local
//define, which is used only from setpower code
if ((OID == OID_TCP_TASK_OFFLOAD_EX) &&
(Length != sizeof(NDIS_TASK_OFFLOAD_HEADER))) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"DoNdisReq: querying h/w offload capabilities\n"));
if (QueryAndSetOffload(Adapter)) {
*(uint *)Info = Adapter->ai_OffloadFlags;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"DoNdisReq: new h/w offload capabilities %x\n",Adapter->ai_OffloadFlags));
return NDIS_STATUS_SUCCESS;
}
return NDIS_STATUS_FAILURE;
}
// Both blocking and non-blocking requests are allocated from NPP. The
// blocking case is to protect against wait failure.
pReqBlock = CTEAllocMemN(sizeof(RequestBlock), 'NiCT');
if (pReqBlock == NULL) {
return NDIS_STATUS_RESOURCES;
}
if (Blocking) {
// Initialize the structure to block on
CTEInitBlockStruc(&pReqBlock->Block);
// Reference count is initialize to two. One for the completion in
// ARPRequestComplete and one for when the CTEBlock completes.
// N.B. This ensures that we don't touch freed memory if
// the CTEBlock fails on Windows ME.
pReqBlock->RefCount = 2;
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_REQUEST,
(DTEXT("DoNDISRequset block: pReqBlock %x OID %x\n"),
pReqBlock, OID));
} else {
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_REQUEST,
(DTEXT("DoNDISRequest async: pReqBlock %x OID %x\n"),
pReqBlock, OID));
}
// Now fill the request's info buffer (same for BLOCKING & NON-BLOCKING)
pReqBlock->Block.cbs_status = NDIS_STATUS_SUCCESS;
pReqBlock->Request.RequestType = RT;
if (RT == NdisRequestSetInformation) {
pReqBlock->Request.DATA.SET_INFORMATION.Oid = OID;
pReqBlock->Request.DATA.SET_INFORMATION.InformationBuffer = Info;
pReqBlock->Request.DATA.SET_INFORMATION.InformationBufferLength = Length;
} else {
pReqBlock->Request.DATA.QUERY_INFORMATION.Oid = OID;
pReqBlock->Request.DATA.QUERY_INFORMATION.InformationBuffer = Info;
pReqBlock->Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;
}
pReqBlock->Blocking = Blocking;
// Submit the request.
if (Adapter->ai_handle != NULL) {
#if MILLEN
// On Millennium, the AOL adapter returns with registers trashed.
// We will work around by saving and restoring registers.
//
_asm {
push esi
push edi
push ebx
}
#endif // MILLEN
NdisRequest(&Status, Adapter->ai_handle, &pReqBlock->Request);
#if MILLEN
_asm {
pop ebx
pop edi
pop esi
}
#endif // MILLEN
} else {
Status = NDIS_STATUS_FAILURE;
}
if (Blocking) {
if (Status == NDIS_STATUS_PENDING) {
Status = (NDIS_STATUS) CTEBlock(&pReqBlock->Block);
#if MILLEN
// If Status == -1, it means the wait failed -- due to system reasons.
// Put in a reasonable failure.
if (Status == -1) {
Status = NDIS_STATUS_FAILURE;
}
#endif // MILLEN
} else {
// Since we aren't blocking, remove refcount for ARPRequestComplete.
InterlockedDecrement(&pReqBlock->RefCount);
}
if (Needed != NULL)
*Needed = pReqBlock->Request.DATA.QUERY_INFORMATION.BytesNeeded;
if (InterlockedDecrement(&pReqBlock->RefCount) == 0) {
CTEFreeMem(pReqBlock);
}
} else {
if (Status != NDIS_STATUS_PENDING) {
if (Needed != NULL)
*Needed = pReqBlock->Request.DATA.QUERY_INFORMATION.BytesNeeded;
ARPRequestComplete(Adapter->ai_handle, &pReqBlock->Request, Status);
}
}
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST,
(DTEXT("-DoNDISRequest [%x]\n"), Status));
return Status;
}
//* FreeARPBuffer - Free a header and buffer descriptor pair.
//
// Called when we're done with a buffer. We'll free the buffer and the
// buffer descriptor pack to the interface.
//
// Entry: Interface - Interface buffer/bd came frome.
// Buffer - NDIS_BUFFER to be freed.
//
// Returns: Nothing.
//
__inline
VOID
FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer)
{
MdpFree(Buffer);
}
//* GetARPBuffer - Get a buffer and descriptor
//
// Returns a pointer to an NDIS_BUFFER and a pointer to a buffer
// of the specified size.
//
// Entry: Interface - Pointer to ARPInterface structure to allocate buffer from.
// BufPtr - Pointer to where to return buf address.
// Size - Size in bytes of buffer needed.
//
// Returns: Pointer to NDIS_BUFFER if successfull, NULL if not
//
PNDIS_BUFFER
GetARPBufferAtDpcLevel(ARPInterface *Interface, uchar **BufPtr, uchar Size)
{
PNDIS_BUFFER Mdl = NULL;
#if DBG
*BufPtr = NULL;
#endif
if (Size <= BUFSIZE_ENET_HEADER_POOL) {
Mdl = MdpAllocateAtDpcLevel(ArpEnetHeaderPool, BufPtr);
} else if (Size <= BUFSIZE_AUX_HEADER_POOL) {
Mdl = MdpAllocateAtDpcLevel(ArpAuxHeaderPool, BufPtr);
}
if (Mdl) {
NdisAdjustBufferLength(Mdl, Size);
}
return Mdl;
}
#if MILLEN
#define GetARPBuffer GetARPBufferAtDpcLevel
#else
__inline
PNDIS_BUFFER
GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar Size)
{
KIRQL OldIrql;
PNDIS_BUFFER Mdl;
OldIrql = KeRaiseIrqlToDpcLevel();
Mdl = GetARPBufferAtDpcLevel(Interface, BufPtr, Size);
KeLowerIrql(OldIrql);
return Mdl;
}
#endif
//* BitSwap - Bit swap two strings.
//
// A routine to bitswap two strings.
//
// Input: Dest - Destination of swap.
// Src - Src string to be swapped.
// Length - Length in bytes to swap.
//
// Returns: Nothing.
//
void
BitSwap(uchar * Dest, uchar * Src, uint Length)
{
uint i;
uchar Temp, TempSrc;
for (i = 0; i < Length; i++, Dest++, Src++) {
TempSrc = *Src;
Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f];
*Dest = Temp;
}
}
//* SendARPPacket - Build a header, and send a packet.
//
// A utility routine to build and ARP header and send a packet. We assume
// the media specific header has been built.
//
// Entry: Interface - Interface for NDIS drive.
// Packet - Pointer to packet to be sent
// Header - Pointer to header to fill in.
// Opcode - Opcode for packet.
// Address - Source HW address.
// SrcAddr - Address to use as our source h/w address.
// Destination - Destination IP address.
// Src - Source IP address.
// HWType - Hardware type.
// CheckIF - TRUE iff we are to check the I/F status before
// sending.
//
// Returns: NDIS_STATUS of send.
//
NDIS_STATUS
SendARPPacket(ARPInterface * Interface, PNDIS_PACKET Packet, ARPHeader * Header, ushort Opcode,
uchar * Address, uchar * SrcAddr, IPAddr Destination, IPAddr Src,
ushort HWType, uint CheckIF)
{
NDIS_STATUS Status;
PNDIS_BUFFER Buffer;
uint PacketDone;
uchar *AddrPtr;
Header->ah_hw = HWType;
Header->ah_pro = net_short(ARP_ETYPE_IP);
Header->ah_hlen = Interface->ai_addrlen;
Header->ah_plen = sizeof(IPAddr);
Header->ah_opcode = Opcode;
AddrPtr = Header->ah_shaddr;
if (SrcAddr == NULL)
SrcAddr = Interface->ai_addr;
RtlCopyMemory(AddrPtr, SrcAddr, Interface->ai_addrlen);
AddrPtr += Interface->ai_addrlen;
*(IPAddr UNALIGNED *) AddrPtr = Src;
AddrPtr += sizeof(IPAddr);
if (Address != (uchar *) NULL)
RtlCopyMemory(AddrPtr, Address, Interface->ai_addrlen);
else
RtlZeroMemory(AddrPtr, Interface->ai_addrlen);
AddrPtr += Interface->ai_addrlen;
*(IPAddr UNALIGNED *) AddrPtr = Destination;
PacketDone = FALSE;
if (!CheckIF || Interface->ai_state == INTERFACE_UP) {
Interface->ai_qlen++;
NdisSend(&Status, Interface->ai_handle, Packet);
if (Status != NDIS_STATUS_PENDING) {
PacketDone = TRUE;
Interface->ai_qlen--;
if (Status == NDIS_STATUS_SUCCESS)
Interface->ai_outoctets += Packet->Private.TotalLength;
else {
if (Status == NDIS_STATUS_RESOURCES)
Interface->ai_outdiscards++;
else
Interface->ai_outerrors++;
}
}
} else {
PacketDone = TRUE;
Status = NDIS_STATUS_ADAPTER_NOT_READY;
}
if (PacketDone) {
NdisUnchainBufferAtFront(Packet, &Buffer);
FreeARPBuffer(Interface, Buffer);
NdisFreePacket(Packet);
}
return Status;
}
//* SendARPRequest - Send an ARP packet
//
// Called when we need to ARP an IP address, or respond to a request. We'll send out
// the packet, and the receiving routines will process the response.
//
// Entry: Interface - Interface to send the request on.
// Destination - The IP address to be ARPed.
// Type - Either RESOLVING_GLOBAL or RESOLVING_LOCAL
// SrcAddr - NULL if we're sending from ourselves, the value
// to use otherwise.
// CheckIF - Flag passed through to SendARPPacket().
//
// Returns: Status of attempt to send ARP request.
//
NDIS_STATUS
SendARPRequest(ARPInterface * Interface, IPAddr Destination, uchar Type,
uchar * SrcAddr, uint CheckIF)
{
uchar *MHeader; // Pointer to media header.
PNDIS_BUFFER Buffer; // NDIS buffer descriptor.
uchar MHeaderSize; // Size of media header.
const uchar *MAddr; // Pointer to media address structure.
uint SAddrOffset; // Offset into media address of source address.
uchar SRFlag = 0; // Source routing flag.
uchar SNAPLength = 0;
const uchar *SNAPAddr; // Address of SNAP header.
PNDIS_PACKET Packet; // Packet for sending.
NDIS_STATUS Status;
ushort HWType;
IPAddr Src;
CTELockHandle Handle;
ARPIPAddr *Addr;
// First, get a source address we can use.
CTEGetLock(&Interface->ai_lock, &Handle);
Addr = &Interface->ai_ipaddr;
Src = NULL_IP_ADDR;
do {
if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
//
// This is a valid address. See if it is the same as the
// target address - i.e. arp'ing for ourselves. If it is,
// we want to use that as our source address.
//
if (IP_ADDR_EQUAL(Addr->aia_addr, Destination)) {
Src = Addr->aia_addr;
break;
}
// See if the target is on this subnet.
if (IP_ADDR_EQUAL(
Addr->aia_addr & Addr->aia_mask,
Destination & Addr->aia_mask
)) {
//
// See if we've already found a suitable candidate on the
// same subnet. If we haven't, we'll use this one.
//
if (!IP_ADDR_EQUAL(
Addr->aia_addr & Addr->aia_mask,
Src & Addr->aia_mask
)) {
Src = Addr->aia_addr;
}
} else {
// He's not on our subnet. If we haven't already found a valid
// address save this one in case we don't find a match for the
// subnet.
if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
Src = Addr->aia_addr;
}
}
}
Addr = Addr->aia_next;
} while (Addr != NULL);
CTEFreeLock(&Interface->ai_lock, Handle);
// If we didn't find a source address, give up.
if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR))
return NDIS_STATUS_SUCCESS;
NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
if (Status != NDIS_STATUS_SUCCESS) {
Interface->ai_outdiscards++;
return Status;
}
((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
(Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
// Figure out what type of media this is, and do the appropriate thing.
switch (Interface->ai_media) {
case NdisMedium802_3:
MHeaderSize = ARP_MAX_MEDIA_ENET;
MAddr = ENetBcst;
if (Interface->ai_snapsize == 0) {
SNAPAddr = (uchar *) NULL;
HWType = net_short(ARP_HW_ENET);
} else {
SNAPLength = sizeof(SNAPHeader);
SNAPAddr = ARPSNAP;
HWType = net_short(ARP_HW_802);
}
SAddrOffset = offsetof(struct ENetHeader, eh_saddr);
break;
case NdisMedium802_5:
// Token ring. We have logic for dealing with the second transmit
// of an arp request.
MAddr = TRBcst;
SAddrOffset = offsetof(struct TRHeader, tr_saddr);
SNAPLength = sizeof(SNAPHeader);
SNAPAddr = ARPSNAP;
MHeaderSize = sizeof(TRHeader);
HWType = net_short(ARP_HW_802);
if (Type == ARP_RESOLVING_GLOBAL) {
MHeaderSize += sizeof(RC);
SRFlag = TR_RII;
}
break;
case NdisMediumFddi:
MHeaderSize = sizeof(FDDIHeader);
MAddr = FDDIBcst;
SNAPAddr = ARPSNAP;
SNAPLength = sizeof(SNAPHeader);
SAddrOffset = offsetof(struct FDDIHeader, fh_saddr);
HWType = net_short(ARP_HW_ENET);
break;
case NdisMediumArcnet878_2:
MHeaderSize = ARP_MAX_MEDIA_ARC;
MAddr = ARCBcst;
SNAPAddr = (uchar *) NULL;
SAddrOffset = offsetof(struct ARCNetHeader, ah_saddr);
HWType = net_short(ARP_HW_ARCNET);
break;
default:
ASSERT(0);
Interface->ai_outerrors++;
return NDIS_STATUS_UNSUPPORTED_MEDIA;
}
if ((Buffer = GetARPBuffer(Interface, &MHeader,
(uchar) (sizeof(ARPHeader) + MHeaderSize + SNAPLength))) == (PNDIS_BUFFER) NULL) {
NdisFreePacket(Packet);
Interface->ai_outdiscards++;
return NDIS_STATUS_RESOURCES;
}
if (Interface->ai_media == NdisMediumArcnet878_2) {
NdisAdjustBufferLength(Buffer, NdisBufferLength(Buffer) - ARCNET_ARPHEADER_ADJUSTMENT);
}
// Copy broadcast address into packet.
RtlCopyMemory(MHeader, MAddr, MHeaderSize);
// Fill in source address.
if (SrcAddr == NULL) {
SrcAddr = Interface->ai_addr;
}
if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize != 0) {
ENetHeader *Hdr = (ENetHeader *) MHeader;
// Using SNAP on ethernet. Adjust the etype to a length.
Hdr->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader));
}
RtlCopyMemory(&MHeader[SAddrOffset], SrcAddr, Interface->ai_addrlen);
if ((Interface->ai_media == NdisMedium802_5) && (Type == ARP_RESOLVING_GLOBAL)) {
// Turn on source routing.
MHeader[SAddrOffset] |= SRFlag;
MHeader[SAddrOffset + Interface->ai_addrlen] |= TrRii;
}
// Copy in SNAP header, if any.
RtlCopyMemory(&MHeader[MHeaderSize], SNAPAddr, SNAPLength);
// Media header is filled in. Now do ARP packet itself.
NdisChainBufferAtFront(Packet, Buffer);
return SendARPPacket(Interface, Packet, (ARPHeader *) & MHeader[MHeaderSize + SNAPLength],
net_short(ARP_REQUEST), (uchar *) NULL, SrcAddr, Destination, Src,
HWType, CheckIF);
}
//* SendARPReply - Reply to an ARP request.
//
// Called by our receive packet handler when we need to reply. We build a packet
// and buffer and call SendARPPacket to send it.
//
// Entry: Interface - Pointer to interface to reply on.
// Destination - IPAddress to reply to.
// Src - Source address to reply from.
// HWAddress - Hardware address to reply to.
// SourceRoute - Source Routing information, if any.
// SourceRouteSize - Size in bytes of soure routing.
// UseSNAP - Whether or not to use SNAP for this reply.
//
// Returns: Nothing.
//
void
SendARPReply(ARPInterface * Interface, IPAddr Destination, IPAddr Src, uchar * HWAddress,
RC UNALIGNED * SourceRoute, uint SourceRouteSize, uint UseSNAP)
{
PNDIS_PACKET Packet; // Buffer and packet to be used.
PNDIS_BUFFER Buffer;
uchar *Header; // Pointer to media header.
NDIS_STATUS Status;
uchar Size = 0; // Size of media header buffer.
ushort HWType;
ENetHeader *EH;
FDDIHeader *FH;
ARCNetHeader *AH;
TRHeader *TRH;
// Allocate a packet for this.
NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
if (Status != NDIS_STATUS_SUCCESS) {
Interface->ai_outdiscards++;
return;
}
((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
(Interface->ai_outpcount[AI_UCAST_INDEX])++;
Size = Interface->ai_hdrsize;
if (UseSNAP)
Size += Interface->ai_snapsize;
if (Interface->ai_media == NdisMedium802_5)
Size += (uchar) SourceRouteSize;
if ((Buffer = GetARPBuffer(Interface, &Header, (uchar) (Size + sizeof(ARPHeader)))) ==
(PNDIS_BUFFER) NULL) {
Interface->ai_outdiscards++;
NdisFreePacket(Packet);
return;
}
// Decide how to build the header based on the media type.
switch (Interface->ai_media) {
case NdisMedium802_3:
EH = (ENetHeader *) Header;
RtlCopyMemory(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
RtlCopyMemory(EH->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
if (!UseSNAP) {
EH->eh_type = net_short(ARP_ETYPE_ARP);
HWType = net_short(ARP_HW_ENET);
} else {
// Using SNAP on ethernet.
EH->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader));
HWType = net_short(ARP_HW_802);
RtlCopyMemory(Header + sizeof(ENetHeader), ARPSNAP,
sizeof(SNAPHeader));
}
break;
case NdisMedium802_5:
TRH = (TRHeader *) Header;
TRH->tr_ac = ARP_AC;
TRH->tr_fc = ARP_FC;
RtlCopyMemory(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH);
RtlCopyMemory(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
if (SourceRouteSize) { // If we have source route info, deal with
// it.
RtlCopyMemory(Header + sizeof(TRHeader), SourceRoute,
SourceRouteSize);
// Convert to directed response.
((RC *) & Header[sizeof(TRHeader)])->rc_blen &= RC_LENMASK;
((RC *) & Header[sizeof(TRHeader)])->rc_dlf ^= RC_DIR;
TRH->tr_saddr[0] |= TR_RII;
}
RtlCopyMemory(Header + sizeof(TRHeader) + SourceRouteSize, ARPSNAP,
sizeof(SNAPHeader));
HWType = net_short(ARP_HW_802);
break;
case NdisMediumFddi:
FH = (FDDIHeader *) Header;
FH->fh_pri = ARP_FDDI_PRI;
RtlCopyMemory(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
RtlCopyMemory(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
RtlCopyMemory(Header + sizeof(FDDIHeader), ARPSNAP, sizeof(SNAPHeader));
HWType = net_short(ARP_HW_ENET);
break;
case NdisMediumArcnet878_2:
AH = (ARCNetHeader *) Header;
AH->ah_saddr = Interface->ai_addr[0];
AH->ah_daddr = *HWAddress;
AH->ah_prot = ARP_ARCPROT_ARP;
NdisAdjustBufferLength(Buffer, NdisBufferLength(Buffer) - ARCNET_ARPHEADER_ADJUSTMENT);
HWType = net_short(ARP_HW_ARCNET);
break;
default:
ASSERT(0);
Interface->ai_outerrors++;
FreeARPBuffer(Interface, Buffer);
NdisFreePacket(Packet);
return;
}
NdisChainBufferAtFront(Packet, Buffer);
SendARPPacket(Interface, Packet, (ARPHeader *) (Header + Size), net_short(ARP_RESPONSE),
HWAddress, NULL, Destination, Src, HWType, TRUE);
}
//* ARPRemoveRCE - Remove an RCE from the ATE list.
//
// This funtion removes a specified RCE from a given ATE. It assumes the ate_lock
// is held by the caller.
//
// Entry: ATE - ATE from which RCE is to be removed.
// RCE - RCE to be removed.
//
// Returns: Nothing
//
void
ARPRemoveRCE(ARPTableEntry * ATE, RouteCacheEntry * RCE)
{
ARPContext *CurrentAC; // Current ARP Context being checked.
#if DBG
uint Found = FALSE;
#endif
CurrentAC = (ARPContext *) (((char *)&ATE->ate_rce) -
offsetof(struct ARPContext, ac_next));
while (CurrentAC->ac_next != (RouteCacheEntry *) NULL)
if (CurrentAC->ac_next == RCE) {
ARPContext *DummyAC = (ARPContext *) RCE->rce_context;
CurrentAC->ac_next = DummyAC->ac_next;
DummyAC->ac_ate = (ARPTableEntry *) NULL;
#if DBG
Found = TRUE;
#endif
break;
} else
CurrentAC = (ARPContext *) CurrentAC->ac_next->rce_context;
ASSERT(Found);
}
//* ARPLookup - Look up an entry in the ARP table.
//
// Called to look up an entry in an interface's ARP table. If we find it, we'll
// lock the entry and return a pointer to it, otherwise we return NULL. We
// assume that the caller has the ARP table locked when we are called.
//
// The ARP table entry is structured as a hash table of pointers to
// ARPTableEntrys.After hashing on the IP address, a linear search is done to
// lookup the entry.
//
// If we find the entry, we lock it for the caller. If we don't find
// the entry, we leave the ARP table locked so that the caller may atomically
// insert a new entry without worrying about a duplicate being inserted between
// the time the table was checked and the time the caller went to insert the
// entry.
//
// Entry: Interface - The interface to be searched upon.
// Address - The IP address we're looking up.
// Handle - Pointer to lock handle to be used to lock entry.
//
// Returns: Pointer to ARPTableEntry if found, or NULL if not.
//
ARPTableEntry *
ARPLookup(ARPInterface * Interface, IPAddr Address, CTELockHandle * Handle)
{
int i = ARP_HASH(Address); // Index into hash table.
ARPTableEntry *Current; // Current ARP Table entry being
// examined.
Current = (*Interface->ai_ARPTbl)[i];
while (Current != (ARPTableEntry *) NULL) {
CTEGetLockAtDPC(&Current->ate_lock, Handle);
if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match.
*Handle = DISPATCH_LEVEL;
return Current;
}
CTEFreeLockFromDPC(&Current->ate_lock, *Handle);
Current = Current->ate_next;
}
// If we got here, we didn't find the entry. Leave the table locked and
// return the handle.
return(ARPTableEntry *) NULL;
}
//* IsBCastOnIF- See it an address is a broadcast address on an interface.
//
// Called to see if a particular address is a broadcast address on an
// interface. We'll check the global, net, and subnet broadcasts. We assume
// the caller holds the lock on the interface.
//
// Entry: Interface - Interface to check.
// Addr - Address to check.
//
// Returns: TRUE if it it a broadcast, FALSE otherwise.
//
uint
IsBCastOnIF(ARPInterface * Interface, IPAddr Addr)
{
IPAddr BCast;
IPMask Mask;
ARPIPAddr *ARPAddr;
IPAddr LocalAddr;
// First get the interface broadcast address.
BCast = Interface->ai_bcast;
// First check for global broadcast.
if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr))
return TRUE;
// Now walk the local addresses, and check for net/subnet bcast on each
// one.
ARPAddr = &Interface->ai_ipaddr;
do {
// See if this one is valid.
LocalAddr = ARPAddr->aia_addr;
if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
// He's valid.
Mask = ARPAddr->aia_mask;
// First check for subnet bcast.
if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
return TRUE;
// Now check all nets broadcast.
Mask = IPNetMask(LocalAddr);
if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
return TRUE;
}
ARPAddr = ARPAddr->aia_next;
} while (ARPAddr != NULL);
// If we're here, it's not a broadcast.
return FALSE;
}
//* ARPSendBCast - See if this is a bcast or mcast frame, and send it.
//
// Called when we have a packet to send and we want to see if it's a broadcast
// or multicast frame on this interface. We'll search the local addresses and
// see if we can determine if it is. If it is, we'll send it here. Otherwise
// we return FALSE, and the caller will try to resolve the address.
//
// Entry: Interface - A pointer to an AI structure.
// Dest - Destination of datagram.
// Packet - Packet to be sent.
// Status - Place to return status of send attempt.
//
// Returns: TRUE if is was a bcast or mcast send, FALSE otherwise.
//
uint
ARPSendBCast(ARPInterface * Interface, IPAddr Dest, PNDIS_PACKET Packet,
PNDIS_STATUS Status)
{
uint IsBCast;
CTELockHandle Handle;
PNDIS_BUFFER ARPBuffer; // ARP Header buffer.
uchar *BufAddr; // Address of NDIS buffer
NDIS_STATUS MyStatus;
ENetHeader *Hdr;
FDDIHeader *FHdr;
TRHeader *TRHdr;
SNAPHeader UNALIGNED *SNAPPtr;
RC UNALIGNED *RCPtr;
ARCNetHeader *AHdr;
uint DataLength;
// Get the lock, and see if it's a broadcast.
CTEGetLock(&Interface->ai_lock, &Handle);
IsBCast = IsBCastOnIF(Interface, Dest);
CTEFreeLock(&Interface->ai_lock, Handle);
if (IsBCast) {
if (Interface->ai_state == INTERFACE_UP) {
uchar Size;
Size = Interface->ai_hdrsize + Interface->ai_snapsize;
if (Interface->ai_media == NdisMedium802_5)
Size += sizeof(RC);
ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size);
if (ARPBuffer != NULL) {
uint UNALIGNED *Temp;
// Got the buffer we need.
switch (Interface->ai_media) {
case NdisMedium802_3:
Hdr = (ENetHeader *) BufAddr;
if (!CLASSD_ADDR(Dest))
RtlCopyMemory(Hdr, ENetBcst, ARP_802_ADDR_LENGTH);
else {
RtlCopyMemory(Hdr, ENetMcst, ARP_802_ADDR_LENGTH);
Temp = (uint UNALIGNED *) & Hdr->eh_daddr[2];
*Temp |= (Dest & ARP_MCAST_MASK);
}
RtlCopyMemory(Hdr->eh_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
if (Interface->ai_snapsize == 0) {
// No snap on this interface, so just use ETypr.
Hdr->eh_type = net_short(ARP_ETYPE_IP);
} else {
ushort ShortDataLength;
// We're using SNAP. Find the size of the packet.
NdisQueryPacket(Packet, NULL, NULL, NULL,
&DataLength);
ShortDataLength = (ushort) (DataLength +
sizeof(SNAPHeader));
Hdr->eh_type = net_short(ShortDataLength);
SNAPPtr = (SNAPHeader UNALIGNED *)
(BufAddr + sizeof(ENetHeader));
RtlCopyMemory(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
}
break;
case NdisMedium802_5:
// This is token ring. We'll have to mess around with
// source routing.
// for multicast - see RFC 1469.
// Handle RFC 1469.
if (!CLASSD_ADDR(Dest) || (!TRFunctionalMcast)) {
TRHdr = (TRHeader *) BufAddr;
RtlCopyMemory(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr));
RtlCopyMemory(TRHdr->tr_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
} else {
TRHdr = (TRHeader *) BufAddr;
RtlCopyMemory(TRHdr, TRMcst, offsetof(TRHeader, tr_saddr));
RtlCopyMemory(TRHdr->tr_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
}
if (sIPAlwaysSourceRoute) {
TRHdr->tr_saddr[0] |= TR_RII;
RCPtr = (RC UNALIGNED *) ((uchar *) TRHdr + sizeof(TRHeader));
RCPtr->rc_blen = TrRii | RC_LEN;
RCPtr->rc_dlf = RC_BCST_LEN;
SNAPPtr = (SNAPHeader UNALIGNED *) ((uchar *) RCPtr + sizeof(RC));
} else {
//
// Adjust the size of the buffer to account for the
// fact that we don't have the RC field.
//
NdisAdjustBufferLength(ARPBuffer, (Size - sizeof(RC)));
SNAPPtr = (SNAPHeader UNALIGNED *) ((uchar *) TRHdr + sizeof(TRHeader));
}
RtlCopyMemory(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
break;
case NdisMediumFddi:
FHdr = (FDDIHeader *) BufAddr;
if (!CLASSD_ADDR(Dest))
RtlCopyMemory(FHdr, FDDIBcst,
offsetof(FDDIHeader, fh_saddr));
else {
RtlCopyMemory(FHdr, FDDIMcst,
offsetof(FDDIHeader, fh_saddr));
Temp = (uint UNALIGNED *) & FHdr->fh_daddr[2];
*Temp |= (Dest & ARP_MCAST_MASK);
}
RtlCopyMemory(FHdr->fh_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
SNAPPtr = (SNAPHeader UNALIGNED *) (BufAddr + sizeof(FDDIHeader));
RtlCopyMemory(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
break;
case NdisMediumArcnet878_2:
AHdr = (ARCNetHeader *) BufAddr;
AHdr->ah_saddr = Interface->ai_addr[0];
AHdr->ah_daddr = 0;
AHdr->ah_prot = ARP_ARCPROT_IP;
break;
default:
ASSERT(0);
*Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
FreeARPBuffer(Interface, ARPBuffer);
return FALSE;
}
(Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
Interface->ai_qlen++;
NdisChainBufferAtFront(Packet, ARPBuffer);
NdisSend(&MyStatus, Interface->ai_handle, Packet);
*Status = MyStatus;
if (MyStatus != NDIS_STATUS_PENDING) { // Send finished
// immediately.
if (MyStatus == NDIS_STATUS_SUCCESS) {
Interface->ai_outoctets += Packet->Private.TotalLength;
} else {
if (MyStatus == NDIS_STATUS_RESOURCES)
Interface->ai_outdiscards++;
else
Interface->ai_outerrors++;
}
Interface->ai_qlen--;
NdisUnchainBufferAtFront(Packet, &ARPBuffer);
FreeARPBuffer(Interface, ARPBuffer);
}
} else
*Status = NDIS_STATUS_RESOURCES;
} else
*Status = NDIS_STATUS_ADAPTER_NOT_READY;
return TRUE;
} else
return FALSE;
}
//* ARPResolveIP - resolves IP address
//
// Called by IP layer when it needs to find physical address of the host
// given the interface and dest IP address
// Entry: Interface - A pointer to the AI structure.
// ArpControlBlock - A pointer to the BufDesc chain to be sent.
//
// Returns: Status.
//
NDIS_STATUS
ARPResolveIP(void *Context, IPAddr Destination, void *ArpControlBlock)
{
ARPInterface *ai = (ARPInterface *) Context; // Set up as AI pointer.
ARPControlBlock *ArpContB = (ARPControlBlock *) ArpControlBlock;
ARPTableEntry *entry; // Pointer to ARP tbl. entry
CTELockHandle lhandle, tlhandle; // Lock handle
NDIS_STATUS Status;
uchar ate_state;
CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
// Check if we already got the mapping.
if ((entry = ARPLookup(ai, Destination, &lhandle)) != NULL) {
// Found a matching entry. ARPLookup returns with the ATE lock held.
if (entry->ate_state != ARP_GOOD) {
Status = NDIS_STATUS_FAILURE;
} else {
Status = FillARPControlBlock(ai, entry, ArpContB);
}
CTEFreeLockFromDPC(&entry->ate_lock, lhandle);
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
return Status;
}
// We need to send arp request.
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
entry = CreateARPTableEntry(ai, Destination, &lhandle, ArpContB);
if (entry != NULL) {
if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry.
// Someone else could have raced in and created the entry between
// the time we free the lock and the time we called
// CreateARPTableEntry(). We check this by looking at the packet
// on the entry. If there is no old packet we'll ARP. If there is,
// we'll call ARPSendData to figure out what to do.
if (entry->ate_packet == NULL) {
ate_state = entry->ate_state;
CTEFreeLock(&entry->ate_lock, lhandle);
SendARPRequest(ai, Destination, ate_state, NULL, TRUE);
// We don't know the state of the entry - we've freed the lock
// and yielded, and it could conceivably have timed out by now,
// or SendARPRequest could have failed, etc. We could take the
// lock, check the status from SendARPRequest, see if it's
// still the same packet, and then make a decision on the
// return value, but it's easiest just to return pending. If
// SendARPRequest failed, the entry will time out anyway.
return NDIS_STATUS_PENDING;
} else {
CTEFreeLock(&entry->ate_lock, lhandle);
return NDIS_STATUS_PENDING;
}
} else if (entry->ate_state == ARP_GOOD) { // Yow! A valid entry.
Status = FillARPControlBlock(ai, entry, ArpContB);
//remove ArpContB from ate_resolveonly queue.
if (entry->ate_resolveonly) {
ARPControlBlock *TmpArpContB, *PrvArpContB = NULL;
TmpArpContB = entry->ate_resolveonly;
while (TmpArpContB && (ArpContB != TmpArpContB)) {
PrvArpContB = TmpArpContB;
TmpArpContB = TmpArpContB->next;
}
if (TmpArpContB == ArpContB) {
if (PrvArpContB) {
PrvArpContB->next = ArpContB->next;
} else {
entry->ate_resolveonly = NULL;
}
}
}
CTEFreeLock(&entry->ate_lock, lhandle);
return Status;
} else { // An invalid entry!
CTEFreeLock(&entry->ate_lock, lhandle);
return NDIS_STATUS_RESOURCES;
}
} else { // Couldn't create an entry.
return NDIS_STATUS_RESOURCES;
}
}
//* ARPSendData - Send a frame to a specific destination address.
//
// Called when we need to send a frame to a particular address, after the
// ATE has been looked up. We take in an ATE and a packet, validate the state of the
// ATE, and either send or ARP for the address if it's not done resolving. We assume
// the lock on the ATE is held where we're called, and we'll free it before returning.
//
// Entry: Interface - A pointer to the AI structure.
// Packet - A pointer to the BufDesc chain to be sent.
// entry - A pointer to the ATE for the send.
// lhandle - Pointer to a lock handle for the ATE.
//
// Returns: Status of the transmit - success, an error, or pending.
//
NDIS_STATUS
ARPSendData(ARPInterface * Interface, PNDIS_PACKET Packet, ARPTableEntry * entry,
CTELockHandle lhandle)
{
PNDIS_BUFFER ARPBuffer = NULL; // ARP Header buffer.
uchar *BufAddr; // Address of NDIS buffer
NDIS_STATUS Status; // Status of send.
#if BACK_FILL
PMDL TmpMdl = NULL;
#endif
if (Interface->ai_state == INTERFACE_UP) {
if (entry->ate_state == ARP_GOOD) { // Entry is valid
entry->ate_useticks = ArpCacheLife;
#if BACK_FILL
if (Interface->ai_media == NdisMedium802_3) {
NdisQueryPacket(Packet, NULL, NULL, &TmpMdl, NULL);
if (TmpMdl->MdlFlags & MDL_NETWORK_HEADER) {
(ULONG_PTR) TmpMdl->MappedSystemVa -= entry->ate_addrlength;
TmpMdl->ByteOffset -= entry->ate_addrlength;
TmpMdl->ByteCount += entry->ate_addrlength;
ARPBuffer = (PNDIS_BUFFER) TmpMdl;
BufAddr = TmpMdl->MappedSystemVa;
} else {
TmpMdl = NULL;
}
}
if (ARPBuffer == (PNDIS_BUFFER) NULL) {
ARPBuffer = GetARPBufferAtDpcLevel(Interface, &BufAddr,
entry->ate_addrlength);
}
#else
ARPBuffer = GetARPBufferAtDpcLevel(Interface, &BufAddr,
entry->ate_addrlength);
#endif
if (ARPBuffer != (PNDIS_BUFFER) NULL) {
// Everything's in good shape, copy header and send packet.
(Interface->ai_outpcount[AI_UCAST_INDEX])++;
Interface->ai_qlen++;
RtlCopyMemory(BufAddr, entry->ate_addr, entry->ate_addrlength);
// If we're on Ethernet, see if we're using SNAP here.
if (Interface->ai_media == NdisMedium802_3 &&
entry->ate_addrlength != sizeof(ENetHeader)) {
ENetHeader *Header;
uint DataSize;
ushort ShortDataSize;
// We're apparently using SNAP on Ethernet. Query the
// packet for the size, and set the length properly.
NdisQueryPacket(Packet, NULL, NULL, NULL, &DataSize);
#if BACK_FILL
if (!TmpMdl) {
ShortDataSize = (ushort) (DataSize + sizeof(SNAPHeader));
} else {
ShortDataSize = (ushort) (DataSize - entry->ate_addrlength + sizeof(SNAPHeader));
}
#else // BACK_FILL
ShortDataSize = (ushort) (DataSize + sizeof(SNAPHeader));
#endif // !BACK_FILL
Header = (ENetHeader *) BufAddr;
Header->eh_type = net_short(ShortDataSize);
// In case backfill is enabled, we need to remember that
// a SNAP header was appended to the Ethernet header
// so we can restore the correct offsets in the MDL.
((PacketContext*)
Packet->ProtocolReserved)->pc_common.pc_flags |=
PACKET_FLAG_SNAP;
} else
((PacketContext*)
Packet->ProtocolReserved)->pc_common.pc_flags &=
~PACKET_FLAG_SNAP;
CTEFreeLock(&entry->ate_lock, lhandle);
#if BACK_FILL
if (TmpMdl == NULL) {
NdisChainBufferAtFront(Packet, ARPBuffer);
}
#else
NdisChainBufferAtFront(Packet, ARPBuffer);
#endif
NdisSend(&Status, Interface->ai_handle, Packet);
if (Status != NDIS_STATUS_PENDING) { // Send finished
// immediately.
if (Status == NDIS_STATUS_SUCCESS) {
Interface->ai_outoctets += Packet->Private.TotalLength;
} else {
if (Status == NDIS_STATUS_RESOURCES)
Interface->ai_outdiscards++;
else
Interface->ai_outerrors++;
}
Interface->ai_qlen--;
#if BACK_FILL
if (TmpMdl == NULL) {
NdisUnchainBufferAtFront(Packet, &ARPBuffer);
FreeARPBuffer(Interface, ARPBuffer);
} else {
uint HdrSize;
HdrSize = sizeof(ENetHeader);
if (((PacketContext *)
Packet->ProtocolReserved)->pc_common.pc_flags &
PACKET_FLAG_SNAP)
HdrSize += Interface->ai_snapsize;
(ULONG_PTR) TmpMdl->MappedSystemVa += HdrSize;
TmpMdl->ByteOffset += HdrSize;
TmpMdl->ByteCount -= HdrSize;
}
#else
NdisUnchainBufferAtFront(Packet, &ARPBuffer);
FreeARPBuffer(Interface, ARPBuffer);
#endif
}
return Status;
} else { // No buffer, free lock and return.
CTEFreeLock(&entry->ate_lock, lhandle);
Interface->ai_outdiscards++;
return NDIS_STATUS_RESOURCES;
}
}
// The IP addresses match, but the state of the ARP entry indicates
// it's not valid. If the address is marked as resolving, we'll replace
// the current cached packet with this one. If it's been more than
// ARP_FLOOD_RATE ms. since we last sent an ARP request, we'll send
// another one now.
if (entry->ate_state <= ARP_RESOLVING) {
PNDIS_PACKET OldPacket = entry->ate_packet;
ulong Now = CTESystemUpTime();
entry->ate_packet = Packet;
if ((Now - entry->ate_valid) > ARP_FLOOD_RATE) {
IPAddr Dest = entry->ate_dest;
entry->ate_valid = Now;
entry->ate_state = ARP_RESOLVING_GLOBAL; // We've done this
// at least once.
CTEFreeLock(&entry->ate_lock, lhandle);
SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
NULL, TRUE); // Send a request.
} else
CTEFreeLock(&entry->ate_lock, lhandle);
if (OldPacket)
IPSendComplete(Interface->ai_context, OldPacket,
NDIS_STATUS_SUCCESS);
return NDIS_STATUS_PENDING;
} else {
ASSERT(0);
CTEFreeLock(&entry->ate_lock, lhandle);
Interface->ai_outerrors++;
return NDIS_STATUS_INVALID_PACKET;
}
} else {
// Adapter is down. Just return the error.
CTEFreeLock(&entry->ate_lock, lhandle);
return NDIS_STATUS_ADAPTER_NOT_READY;
}
}
//* CreateARPTableEntry - Create a new entry in the ARP table.
//
// A function to put an entry into the ARP table. We allocate memory if we
// need to.
//
// The first thing to do is get the lock on the ARP table, and see if the
// entry already exists. If it does, we're done. Otherwise we need to
// allocate memory and create a new entry.
//
// Entry: Interface - Interface for ARP table.
// Destination - Destination address to be mapped.
// Handle - Pointer to lock handle for entry.
//
// Returns: Pointer to newly created entry.
//
ARPTableEntry *
CreateARPTableEntry(ARPInterface * Interface, IPAddr Destination,
CTELockHandle * Handle, void *UserArp)
{
ARPTableEntry *NewEntry, *Entry;
CTELockHandle TableHandle;
int i = ARP_HASH(Destination);
int Size;
// First look for it, and if we don't find it return try to create one.
CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
if ((Entry = ARPLookup(Interface, Destination, Handle)) != NULL) {
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock, *Handle);
*Handle = TableHandle;
// if we are using arp api entry, turn off the
// userarp flag so that handle arp need not free it.
if (!UserArp && Entry->ate_userarp) {
Entry->ate_userarp = 0;
}
if (UserArp) {
if (Entry->ate_resolveonly) {
// chain the current request at the end of the new
// before using the new request as the head.
//
((ARPControlBlock *)UserArp)->next = Entry->ate_resolveonly;
}
// link the new request.
//
Entry->ate_resolveonly = (ARPControlBlock *)UserArp;
}
return Entry;
}
// Allocate memory for the entry. If we can't, fail the request.
Size = sizeof(ARPTableEntry) - 1 +
(Interface->ai_media == NdisMedium802_5 ?
ARP_MAX_MEDIA_TR : (Interface->ai_hdrsize +
Interface->ai_snapsize));
if ((NewEntry = CTEAllocMemN(Size, 'QiCT')) == (ARPTableEntry *) NULL) {
CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
return (ARPTableEntry *) NULL;
}
RtlZeroMemory(NewEntry, Size);
NewEntry->ate_dest = Destination;
if (Interface->ai_media != NdisMedium802_5 || sArpAlwaysSourceRoute) {
NewEntry->ate_state = ARP_RESOLVING_GLOBAL;
} else {
NewEntry->ate_state = ARP_RESOLVING_LOCAL;
}
if (UserArp) {
NewEntry->ate_userarp = 1;
}
NewEntry->ate_resolveonly = (ARPControlBlock *)UserArp;
NewEntry->ate_valid = CTESystemUpTime();
NewEntry->ate_useticks = ArpCacheLife;
CTEInitLock(&NewEntry->ate_lock);
// Entry does not exist. Insert the new entry into the table at the
// appropriate spot.
//
NewEntry->ate_next = (*Interface->ai_ARPTbl)[i];
(*Interface->ai_ARPTbl)[i] = NewEntry;
Interface->ai_count++;
CTEGetLockAtDPC(&NewEntry->ate_lock, Handle);
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock, *Handle);
*Handle = TableHandle;
return NewEntry;
}
//* ARPTransmit - Send a frame.
//
// The main ARP transmit routine, called by the upper layer. This routine
// takes as input a buf desc chain, RCE, and size. We validate the cached
// information in the RCE. If it is valid, we use it to send the frame.
// Otherwise we do a table lookup. If we find it in the table, we'll update
// the RCE and continue. Otherwise we'll queue the packet and start an ARP
// resolution.
//
// Entry: Context - A pointer to the AI structure.
// Packet - A pointer to the BufDesc chain to be sent.
// Destination - IP address of destination we're trying to reach,
// RCE - A pointer to an RCE which may have cached information.
//
// Returns: Status of the transmit - success, an error, or pending.
//
NDIS_STATUS
__stdcall
ARPTransmit(void *Context, PNDIS_PACKET * PacketArray, uint NumberOfPackets,
IPAddr Destination, RouteCacheEntry * RCE, void *LinkCtxt)
{
ARPInterface *ai = (ARPInterface *) Context; // Set up as AI pointer.
ARPContext *ac; // ARP context pointer.
ARPTableEntry *entry; // Pointer to ARP tbl. entry
CTELockHandle lhandle; // Lock handle
CTELockHandle tlhandle; // Lock handle for ARP table.
NDIS_STATUS Status;
PNDIS_PACKET Packet = *PacketArray;
//
// For now, we get only one packet...
//
ASSERT(NumberOfPackets == 1);
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_TX,
(DTEXT("+ARPTransmit(%x, %x, %d, %x, %x, %x)\n"),
Context, PacketArray, NumberOfPackets,
Destination, RCE, LinkCtxt));
if (ai->ai_state != INTERFACE_UP) {
return NDIS_STATUS_ADAPTER_NOT_READY;
}
CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
if (RCE != (RouteCacheEntry *) NULL) { // Have a valid RCE.
ac = (ARPContext *) RCE->rce_context; // Get pointer to context
entry = ac->ac_ate;
if (entry != (ARPTableEntry *) NULL) { // Have a valid ATE.
CTEGetLockAtDPC(&entry->ate_lock, &lhandle); // Lock this structure
if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) {
uint refresh,status;
uchar state = entry->ate_state;
refresh= entry->ate_refresh;
CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
status = ARPSendData(ai, Packet, entry, tlhandle); // Send the data
if (refresh) {
SendARPRequest(ai, Destination, state, NULL, TRUE);
}
return status;
}
// We have an RCE that identifies the wrong ATE. We'll free it from
// this list and try and find an ATE that is valid.
ARPRemoveRCE(entry, RCE);
CTEFreeLockFromDPC(&entry->ate_lock, lhandle);
// Fall through to 'no valid entry' code.
}
}
// Here we have no valid ATE, either because the RCE is NULL or the ATE
// specified by the RCE was invalid. We'll try and find one in the table. If
// we find one, we'll fill in this RCE and send the packet. Otherwise we'll
// try to create one. At this point we hold the lock on the ARP table.
if ((entry = ARPLookup(ai, Destination, &lhandle)) != (ARPTableEntry *) NULL) {
// Found a matching entry. ARPLookup returns with the ATE lock held.
if (RCE != (RouteCacheEntry *) NULL) {
ac->ac_next = entry->ate_rce; // Fill in context for next time.
entry->ate_rce = RCE;
ac->ac_ate = entry;
}
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX,
(DTEXT("ARPTx: ATE %x - calling ARPSendData\n"), entry));
CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
return ARPSendData(ai, Packet, entry, tlhandle);
}
// No valid entry in the ARP table. First we'll see if we're sending to a
// broadcast address or multicast address. If not, we'll try to create
// an entry in the table and get an ARP resolution going. ARPLookup returns
// with the table lock held when it fails, we'll free it here.
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
if (ARPSendBCast(ai, Destination, Packet, &Status)) {
return Status;
}
entry = CreateARPTableEntry(ai, Destination, &lhandle, 0);
if (entry != NULL) {
if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry.
uchar state = entry->ate_state;
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX,
(DTEXT("ARPTx: Created ATE %x\n"), entry));
// Someone else could have raced in and created the entry between
// the time we free the lock and the time we called
// CreateARPTableEntry(). We check this by looking at the packet
// on the entry. If there is no old packet we'll ARP. If there is,
// we'll call ARPSendData to figure out what to do.
if (entry->ate_packet == NULL) {
entry->ate_packet = Packet;
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX,
(DTEXT("ARPTx: ATE %x - calling SendARPRequest\n"), entry));
CTEFreeLock(&entry->ate_lock, lhandle);
SendARPRequest(ai, Destination, state, NULL, TRUE);
// We don't know the state of the entry - we've freed the lock
// and yielded, and it could conceivably have timed out by now,
// or SendARPRequest could have failed, etc. We could take the
// lock, check the status from SendARPRequest, see if it's
// still the same packet, and then make a decision on the
// return value, but it's easiest just to return pending. If
// SendARPRequest failed, the entry will time out anyway.
return NDIS_STATUS_PENDING;
} else {
return ARPSendData(ai, Packet, entry, lhandle);
}
} else if (entry->ate_state == ARP_GOOD) { // Yow! A valid entry.
return ARPSendData(ai, Packet, entry, lhandle);
} else { // An invalid entry!
CTEFreeLock(&entry->ate_lock, lhandle);
return NDIS_STATUS_RESOURCES;
}
} else { // Couldn't create an entry.
DEBUGMSG(DBG_ERROR && DBG_ARP,
(DTEXT("ARPTx: Failed to create ATE.\n")));
return NDIS_STATUS_RESOURCES;
}
}
//* RemoveARPTableEntry - Delete an entry from the ARP table.
//
// This is a simple utility function to delete an entry from the ATP table. We
// assume locks are held on both the table and the entry.
//
// Entry: Previous - The entry immediately before the one to be deleted.
// Entry - The entry to be deleted.
//
// Returns: Nothing.
//
void
RemoveARPTableEntry(ARPTableEntry * Previous, ARPTableEntry * Entry)
{
RouteCacheEntry *RCE; // Pointer to route cache entry
ARPContext *AC;
RCE = Entry->ate_rce;
// Loop through and invalidate all RCEs on this ATE.
while (RCE != (RouteCacheEntry *) NULL) {
AC = (ARPContext *) RCE->rce_context;
AC->ac_ate = (ARPTableEntry *) NULL;
RCE = AC->ac_next;
}
// Splice this guy out of the list.
Previous->ate_next = Entry->ate_next;
}
//* ARPFlushATE - removes ARP Table entry for given dest address
//
// Called by IP layer when it needs to flush the link layer address from arp
// cache
// Entry: Interface - A pointer to the AI structure.
// Destination - Destination Address whose Xlation needs to be removed
//
// Returns: TRUE if the entry was found and flushed, FALSE otherwise
//
BOOLEAN
ARPFlushATE(void *Context, IPAddr Address)
{
ARPInterface *ai = (ARPInterface *) Context;
ARPTableEntry *entry;
CTELockHandle lhandle, tlhandle;
ARPTable *Table;
ARPTableEntry *Current, *Previous;
int i = ARP_HASH(Address);
PNDIS_PACKET OldPacket = NULL;
CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
Table = ai->ai_ARPTbl;
Current = (*Table)[i];
Previous = (ARPTableEntry *) ((uchar *) & ((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next));
while (Current != (ARPTableEntry *) NULL) {
CTEGetLock(&Current->ate_lock, &lhandle);
if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match.
if (Current->ate_resolveonly) {
ARPControlBlock *ArpContB, *TmpArpContB;
ArpContB = Current->ate_resolveonly;
while (ArpContB) {
ArpRtn rtn;
rtn = (ArpRtn) ArpContB->CompletionRtn;
ArpContB->status = STATUS_UNSUCCESSFUL;
TmpArpContB = ArpContB->next;
(*rtn) (ArpContB, STATUS_UNSUCCESSFUL);
ArpContB = TmpArpContB;
}
Current->ate_resolveonly = NULL;
}
RemoveARPTableEntry(Previous, Current);
CTEFreeLock(&Current->ate_lock, lhandle);
OldPacket = Current->ate_packet;
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
if (OldPacket) {
IPSendComplete(ai->ai_context, OldPacket, NDIS_STATUS_SUCCESS);
}
CTEFreeMem(Current);
return TRUE;
}
CTEFreeLock(&Current->ate_lock, lhandle);
Previous = Current;
Current = Current->ate_next;
}
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
return FALSE;
}
//* ARPFlushAllATE - removes all ARP Table entries.
//
// Entry: Interface - A pointer to the AI structure.
//
// Returns: None
//
void
ARPFlushAllATE(void *Context)
{
ARPInterface *ai = (ARPInterface *) Context;
CTELockHandle tlhandle;
ARPTable *Table;
int i;
ARPTableEntry *ATE;
PNDIS_PACKET PList = (PNDIS_PACKET) NULL;
CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
Table = ai->ai_ARPTbl;
if (Table != NULL) {
for (i = 0; i < ARP_TABLE_SIZE; i++) {
while ((*Table)[i] != NULL) {
ATE = (*Table)[i];
if (ATE->ate_resolveonly) {
ARPControlBlock *ArpContB, *TmpArpContB;
ArpContB = ATE->ate_resolveonly;
while (ArpContB) {
ArpRtn rtn;
rtn = (ArpRtn) ArpContB->CompletionRtn;
ArpContB->status = STATUS_UNSUCCESSFUL;
TmpArpContB = ArpContB->next;
(*rtn) (ArpContB, STATUS_UNSUCCESSFUL);
ArpContB = TmpArpContB;
}
ATE->ate_resolveonly = NULL;
}
RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]), ate_next),
ATE);
if (ATE->ate_packet) {
((PacketContext *) ATE->ate_packet->ProtocolReserved)->pc_common.pc_link = PList;
PList = ATE->ate_packet;
}
CTEFreeMem(ATE);
}
}
}
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
while (PList != (PNDIS_PACKET) NULL) {
PNDIS_PACKET Packet = PList;
PList = ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link;
IPSendComplete(ai->ai_context, Packet, NDIS_STATUS_SUCCESS);
}
}
//* ARPXferData - Transfer data on behalf on an upper later protocol.
//
// This routine is called by the upper layer when it needs to transfer data
// from an NDIS driver. We just map his call down.
//
// Entry: Context - Context value we gave to IP (really a pointer to an AI).
// MACContext - Context value MAC gave us on a receive.
// MyOffset - Packet offset we gave to the protocol earlier.
// ByteOffset - Byte offset into packet protocol wants transferred.
// BytesWanted - Number of bytes to transfer.
// Packet - Pointer to packet to be used for transferring.
// Transferred - Pointer to where to return bytes transferred.
//
// Returns: NDIS_STATUS of command.
//
NDIS_STATUS
__stdcall
ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset,
uint ByteOffset, uint BytesWanted, PNDIS_PACKET Packet, uint * Transferred)
{
ARPInterface *Interface = (ARPInterface *) Context;
NDIS_STATUS Status;
NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset + MyOffset,
BytesWanted, Packet, Transferred);
return Status;
}
//* ARPClose - Close an adapter.
//
// Called by IP when it wants to close an adapter, presumably due to an error condition.
// We'll close the adapter, but we won't free any memory.
//
// Entry: Context - Context value we gave him earlier.
//
// Returns: Nothing.
//
void
__stdcall
ARPClose(void *Context)
{
ARPInterface *Interface = (ARPInterface *) Context;
NDIS_STATUS Status;
CTELockHandle LockHandle;
NDIS_HANDLE Handle;
Interface->ai_operstate = IF_OPER_STATUS_NON_OPERATIONAL;
Interface->ai_lastchange = GetTimeTicks();
Interface->ai_state = INTERFACE_DOWN;
CTEInitBlockStruc(&Interface->ai_block);
CTEGetLock(&Interface->ai_lock, &LockHandle);
if (Interface->ai_handle != (NDIS_HANDLE) NULL) {
Handle = Interface->ai_handle;
CTEFreeLock(&Interface->ai_lock, LockHandle);
NdisCloseAdapter(&Status, Handle);
if (Status == NDIS_STATUS_PENDING) {
Status = CTEBlock(&Interface->ai_block);
}
Interface->ai_handle = NULL;
} else {
CTEFreeLock(&Interface->ai_lock, LockHandle);
}
}
//* ARPInvalidate - Notification that an RCE is invalid.
//
// Called by IP when an RCE is closed or otherwise invalidated. We look up
// the ATE for the specified RCE, and then remove the RCE from the ATE list.
//
// Entry: Context - Context value we gave him earlier.
// RCE - RCE to be invalidated
//
// Returns: Nothing.
//
void
__stdcall
ARPInvalidate(void *Context, RouteCacheEntry *RCE)
{
ARPInterface *Interface = (ARPInterface *) Context;
ARPTableEntry *ATE;
CTELockHandle Handle, ATEHandle;
ARPContext *AC = (ARPContext *) RCE->rce_context;
CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
#if DBG
if (!(RCE->rce_flags & RCE_CONNECTED)) {
ARPTableEntry *tmpATE;
ATE = ARPLookup(Interface, RCE->rce_dest, &ATEHandle);
if (ATE != NULL) {
tmpATE = ATE;
while (ATE) {
if (ATE->ate_rce == RCE) {
DbgBreakPoint();
}
ATE = ATE->ate_next;
}
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock, ATEHandle);
CTEFreeLock(&tmpATE->ate_lock, Handle);
return;
}
}
#endif
if ((ATE = AC->ac_ate) == (ARPTableEntry *) NULL) {
CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE.
return;
}
CTEGetLockAtDPC(&ATE->ate_lock, &ATEHandle);
ARPRemoveRCE(ATE, RCE);
RtlZeroMemory(RCE->rce_context, RCE_CONTEXT_SIZE);
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock, ATEHandle);
CTEFreeLock(&ATE->ate_lock, Handle);
}
//* ARPSetMCastList - Set the multicast address list for the adapter.
//
// Called to try and set the multicast reception list for the adapter.
// We allocate a buffer big enough to hold the new address list, and format
// the address list into the buffer. Then we submit the NDIS request to set
// the list. If we can't set the list because the multicast address list is
// full we'll put the card into all multicast mode.
//
// Input: Interface - Interface on which to set list.
//
// Returns: NDIS_STATUS of attempt.
//
NDIS_STATUS
ARPSetMCastList(ARPInterface * Interface)
{
CTELockHandle Handle;
uchar *MCastBuffer, *CurrentPtr;
uint MCastSize;
NDIS_STATUS Status;
uint i;
ARPMCastAddr *AddrPtr;
IPAddr UNALIGNED *Temp;
CTEGetLock(&Interface->ai_lock, &Handle);
MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH;
if (MCastSize != 0)
MCastBuffer = CTEAllocMemN(MCastSize, 'RiCT');
else
MCastBuffer = NULL;
if (MCastBuffer != NULL || MCastSize == 0) {
// Got the buffer. Loop through, building the list.
AddrPtr = Interface->ai_mcast;
CurrentPtr = MCastBuffer;
for (i = 0; i < Interface->ai_mcastcnt; i++) {
ASSERT(AddrPtr != NULL);
if (Interface->ai_media == NdisMedium802_3) {
RtlCopyMemory(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH);
Temp = (IPAddr UNALIGNED *) (CurrentPtr + 2);
*Temp |= AddrPtr->ama_addr;
} else if ((Interface->ai_media == NdisMedium802_5) & TRFunctionalMcast) {
RtlCopyMemory(CurrentPtr, TRNetMcst, ARP_802_ADDR_LENGTH - 2);
MCastSize = 4;
} else if (Interface->ai_media == NdisMediumFddi) {
RtlCopyMemory(CurrentPtr, ((FDDIHeader *) FDDIMcst)->fh_daddr,
ARP_802_ADDR_LENGTH);
Temp = (IPAddr UNALIGNED *) (CurrentPtr + 2);
*Temp |= AddrPtr->ama_addr;
} else
ASSERT(0);
CurrentPtr += ARP_802_ADDR_LENGTH;
AddrPtr = AddrPtr->ama_next;
}
CTEFreeLock(&Interface->ai_lock, Handle);
// We're built the list. Now give it to the driver to handle.
if (Interface->ai_media == NdisMedium802_3) {
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_802_3_MULTICAST_LIST, MCastBuffer, MCastSize, NULL, TRUE);
} else if ((Interface->ai_media == NdisMedium802_5) & TRFunctionalMcast) {
if (!(Interface->ai_pfilter & NDIS_PACKET_TYPE_FUNCTIONAL)) {
Interface->ai_pfilter |= NDIS_PACKET_TYPE_FUNCTIONAL;
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER,
&Interface->ai_pfilter,
sizeof(uint), NULL, TRUE);
}
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_802_5_CURRENT_FUNCTIONAL, MCastBuffer, MCastSize, NULL,
TRUE);
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
"SetMcast after OID-- TR mcast address on %x status %x\n",
Interface, Status));
} else if (Interface->ai_media == NdisMediumFddi) {
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL,
TRUE);
} else
ASSERT(0);
if (MCastBuffer != NULL) {
CTEFreeMem(MCastBuffer);
}
if (Status == NDIS_STATUS_MULTICAST_FULL) {
// Multicast list is full. Try to set the filter to all multicasts.
Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter,
sizeof(uint), NULL, TRUE);
}
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
Status = NDIS_STATUS_RESOURCES;
}
return Status;
}
//* ARPFindMCast - Find a multicast address structure on our list.
//
// Called as a utility to find a multicast address structure. If we find
// it, we return a pointer to it and it's predecessor. Otherwise we return
// NULL. We assume the caller holds the lock on the interface already.
//
// Input: Interface - Interface to search.
// Addr - Addr to find.
// Prev - Where to return previous pointer.
//
// Returns: Pointer if we find one, NULL otherwise.
//
ARPMCastAddr *
ARPFindMCast(ARPInterface * Interface, IPAddr Addr, ARPMCastAddr ** Prev)
{
ARPMCastAddr *AddrPtr, *PrevPtr;
PrevPtr = STRUCT_OF(ARPMCastAddr, &Interface->ai_mcast, ama_next);
AddrPtr = PrevPtr->ama_next;
while (AddrPtr != NULL) {
if (IP_ADDR_EQUAL(AddrPtr->ama_addr, Addr))
break;
else {
PrevPtr = AddrPtr;
AddrPtr = PrevPtr->ama_next;
}
}
*Prev = PrevPtr;
return AddrPtr;
}
//* ARPDelMCast - Delete a multicast address.
//
// Called when we want to delete a multicast address. We look for a matching
// (masked) address. If we find one, we'll dec. the reference count and if
// it goes to 0 we'll pull him from the list and reset the multicast list.
//
// Input: Interface - Interface on which to act.
// Addr - Address to be deleted.
//
// Returns: TRUE if it worked, FALSE otherwise.
//
uint
ARPDelMCast(ARPInterface * Interface, IPAddr Addr)
{
ARPMCastAddr *AddrPtr, *PrevPtr;
CTELockHandle Handle;
uint Status = TRUE;
// When we support TR (RFC 1469) fully we'll need to change this.
if (Interface->ai_media == NdisMedium802_3 ||
Interface->ai_media == NdisMediumFddi ||
(Interface->ai_media == NdisMedium802_5 && TRFunctionalMcast)) {
// This is an interface that supports mcast addresses.
Addr &= ARP_MCAST_MASK;
CTEGetLock(&Interface->ai_lock, &Handle);
AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
if (AddrPtr != NULL) {
// We found one. Dec. his refcnt, and if it's 0 delete him.
(AddrPtr->ama_refcnt)--;
if (AddrPtr->ama_refcnt == 0) {
// He's done.
PrevPtr->ama_next = AddrPtr->ama_next;
(Interface->ai_mcastcnt)--;
CTEFreeLock(&Interface->ai_lock, Handle);
CTEFreeMem(AddrPtr);
ARPSetMCastList(Interface);
CTEGetLock(&Interface->ai_lock, &Handle);
}
} else
Status = FALSE;
CTEFreeLock(&Interface->ai_lock, Handle);
}
return Status;
}
//* ARPAddMCast - Add a multicast address.
//
// Called when we want to start receiving a multicast address. We'll mask
// the address and look it up in our address list. If we find it, we'll just
// bump the reference count. Otherwise we'll try to create one and put him
// on the list. In that case we'll need to set the multicast address list for
// the adapter.
//
// Input: Interface - Interface to set on.
// Addr - Address to set.
//
// Returns: TRUE if we succeed, FALSE if we fail.
//
uint
ARPAddMCast(ARPInterface * Interface, IPAddr Addr)
{
ARPMCastAddr *AddrPtr, *PrevPtr;
CTELockHandle Handle;
uint Status = TRUE;
if (Interface->ai_state != INTERFACE_UP)
return FALSE;
// Currently we don't do anything with token ring, since we send
// all mcasts as TR broadcasts. When we comply with RFC 1469 we'll need to
// fix this.
if ((Interface->ai_media == NdisMedium802_3) ||
(Interface->ai_media == NdisMediumFddi) ||
((Interface->ai_media == NdisMedium802_5) && TRFunctionalMcast)) {
Addr &= ARP_MCAST_MASK;
CTEGetLock(&Interface->ai_lock, &Handle);
AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
if (AddrPtr != NULL) {
// We found one, just bump refcnt.
(AddrPtr->ama_refcnt)++;
} else {
// Didn't find one. Allocate space for one, link him in, and
// try to set the list.
AddrPtr = CTEAllocMemN(sizeof(ARPMCastAddr), 'SiCT');
if (AddrPtr != NULL) {
// Got one. Link him in.
AddrPtr->ama_addr = Addr;
AddrPtr->ama_refcnt = 1;
AddrPtr->ama_next = Interface->ai_mcast;
Interface->ai_mcast = AddrPtr;
(Interface->ai_mcastcnt)++;
CTEFreeLock(&Interface->ai_lock, Handle);
// Now try to set the list.
if (ARPSetMCastList(Interface) != NDIS_STATUS_SUCCESS) {
// Couldn't set the list. Call the delete routine to delete
// the address we just tried to set.
Status = ARPDelMCast(Interface, Addr);
ASSERT(Status);
Status = FALSE;
}
CTEGetLock(&Interface->ai_lock, &Handle);
} else
Status = FALSE; // Couldn't get memory.
}
// We've done out best. Free the lock and return.
CTEFreeLock(&Interface->ai_lock, Handle);
}
return Status;
}
//* ARPAddAddr - Add an address to the ARP table.
//
// This routine is called by IP to add an address as a local address, or
// or specify the broadcast address for this interface.
//
// Entry: Context - Context we gave IP earlier (really an ARPInterface ptr)
// Type - Type of address (local, p-arp, multicast, or
// broadcast).
// Address - Broadcast IP address to be added.
// Mask - Mask for address.
//
// Returns: 0 if we failed, non-zero otherwise
//
uint
__stdcall
ARPAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
{
ARPInterface *Interface = (ARPInterface *) Context;
CTELockHandle Handle;
if (Type != LLIP_ADDR_LOCAL && Type != LLIP_ADDR_PARP) {
// Not a local address, must be broadcast or multicast.
if (Type == LLIP_ADDR_BCAST) {
Interface->ai_bcast = Address;
return TRUE;
} else if (Type == LLIP_ADDR_MCAST) {
return ARPAddMCast(Interface, Address);
} else
return FALSE;
} else { // This is a local address.
CTEGetLock(&Interface->ai_lock, &Handle);
if (Type != LLIP_ADDR_PARP) {
uint RetStatus = FALSE;
uint ArpForSelf = FALSE;
if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, 0)) {
Interface->ai_ipaddr.aia_addr = Address;
Interface->ai_ipaddr.aia_mask = Mask;
Interface->ai_ipaddr.aia_age = ArpRetryCount;
if (Interface->ai_state == INTERFACE_UP) {
// When ArpRetryCount is 0, we'll return immediately
// below, so don't save completion context
Interface->ai_ipaddr.aia_context = (ArpRetryCount > 0)?
Context2 : NULL;
ArpForSelf = TRUE;
} else {
Interface->ai_ipaddr.aia_context = NULL;
}
RetStatus = TRUE;
} else {
ARPIPAddr *NewAddr;
NewAddr = CTEAllocMemNBoot(sizeof(ARPIPAddr), 'TiCT');
if (NewAddr != (ARPIPAddr *) NULL) {
NewAddr->aia_addr = Address;
NewAddr->aia_mask = Mask;
NewAddr->aia_age = ArpRetryCount;
NewAddr->aia_next = Interface->ai_ipaddr.aia_next;
if (Interface->ai_state == INTERFACE_UP) {
// When ArpRetryCount is 0, we'll return immediately
// below, so don't save completion context
NewAddr->aia_context = (ArpRetryCount > 0)?
Context2 : NULL;
ArpForSelf = TRUE;
} else {
NewAddr->aia_context = NULL;
}
Interface->ai_ipaddr.aia_next = NewAddr;
RetStatus = TRUE;
}
}
if (RetStatus) {
Interface->ai_ipaddrcnt++;
if (Interface->ai_telladdrchng) {
CTEFreeLock(&Interface->ai_lock, Handle);
AddrNotifyLink(Interface);
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
}
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
}
// add wakeup pattern for this address, if the address is in
// conflict ip will turn around and delete the address thus
// deleting the wakeup pattern.
ARPWakeupPattern(Interface, Address, TRUE);
// ARP for the address we've added, to see it it already exists.
if (RetStatus == TRUE && ArpForSelf == TRUE) {
if (ArpRetryCount) {
SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL,
NULL, TRUE);
return IP_PENDING;
} else {
return TRUE;
}
}
return RetStatus;
} else if (Type == LLIP_ADDR_PARP) {
ARPPArpAddr *NewPArp, *TmpPArp;
// He's adding a proxy arp address.
// Don't allow to add duplicate proxy arp entries
TmpPArp = Interface->ai_parpaddr;
while (TmpPArp) {
if (IP_ADDR_EQUAL(TmpPArp->apa_addr, Address) && IP_ADDR_EQUAL(TmpPArp->apa_mask, Mask)) {
CTEFreeLock(&Interface->ai_lock, Handle);
return FALSE;
}
TmpPArp = TmpPArp->apa_next;
}
NewPArp = CTEAllocMemN(sizeof(ARPPArpAddr), 'UiCT');
if (NewPArp != NULL) {
NewPArp->apa_addr = Address;
NewPArp->apa_mask = Mask;
NewPArp->apa_next = Interface->ai_parpaddr;
Interface->ai_parpaddr = NewPArp;
Interface->ai_parpcount++;
CTEFreeLock(&Interface->ai_lock, Handle);
return TRUE;
}
CTEFreeLock(&Interface->ai_lock, Handle);
}
return FALSE;
}
}
//* ARPDeleteAddr - Delete a local or proxy address.
//
// Called to delete a local or proxy address.
//
// Entry: Context - An ARPInterface pointer.
// Type - Type of address (local or p-arp).
// Address - IP address to be deleted.
// Mask - Mask for address. Used only for deleting proxy-ARP
// entries.
//
// Returns: 0 if we failed, non-zero otherwise
//
uint
__stdcall
ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
{
ARPInterface *Interface = (ARPInterface *) Context;
CTELockHandle Handle;
ARPIPAddr *DelAddr, *PrevAddr;
ARPPArpAddr *DelPAddr, *PrevPAddr;
if (Type == LLIP_ADDR_LOCAL) {
CTEGetLock(&Interface->ai_lock, &Handle);
if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) {
SetAddrControl *SAC;
AddAddrNotifyEvent *DelayedEvent;
IPAddr IpAddress;
ARPIPAddr *Addr;
Addr = &Interface->ai_ipaddr;
IpAddress = Addr->aia_addr;
Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR;
Interface->ai_ipaddrcnt--;
if (Interface->ai_telladdrchng) {
CTEFreeLock(&Interface->ai_lock, Handle);
AddrNotifyLink(Interface);
CTEGetLock(&Interface->ai_lock, &Handle);
}
// if the address is deleted before the add completes, complete the add here
// Doing this will complete the irp and also decrements the refcount on the interface
if (Addr->aia_context != NULL) {
SAC = (SetAddrControl *) Addr->aia_context;
Addr->aia_context = NULL;
CTEFreeLock(&Interface->ai_lock, Handle);
// We cannot call completion routine at timer DPC
// because completion routine will need to notify
// TDI clients and that could take long time.
DelayedEvent = CTEAllocMemNBoot(sizeof(AddAddrNotifyEvent), 'ViCT');
if (DelayedEvent) {
DelayedEvent->SAC = SAC;
DelayedEvent->Address = IpAddress;
DelayedEvent->Status = IP_SUCCESS;
CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed);
CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent);
} else {
ASSERT(FALSE);
return FALSE;
}
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
}
ARPWakeupPattern(Interface, Address, FALSE);
return TRUE;
} else {
PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next);
DelAddr = PrevAddr->aia_next;
while (DelAddr != NULL)
if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address))
break;
else {
PrevAddr = DelAddr;
DelAddr = DelAddr->aia_next;
}
if (DelAddr != NULL) {
PrevAddr->aia_next = DelAddr->aia_next;
CTEFreeMem(DelAddr);
Interface->ai_ipaddrcnt--;
if (Interface->ai_telladdrchng) {
CTEFreeLock(&Interface->ai_lock, Handle);
AddrNotifyLink(Interface);
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
}
ARPWakeupPattern(Interface, Address, FALSE);
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
}
return(DelAddr != NULL);
}
} else if (Type == LLIP_ADDR_PARP) {
CTEGetLock(&Interface->ai_lock, &Handle);
PrevPAddr = STRUCT_OF(ARPPArpAddr, &Interface->ai_parpaddr, apa_next);
DelPAddr = PrevPAddr->apa_next;
while (DelPAddr != NULL)
if (IP_ADDR_EQUAL(DelPAddr->apa_addr, Address) &&
DelPAddr->apa_mask == Mask)
break;
else {
PrevPAddr = DelPAddr;
DelPAddr = DelPAddr->apa_next;
}
if (DelPAddr != NULL) {
PrevPAddr->apa_next = DelPAddr->apa_next;
Interface->ai_parpcount--;
CTEFreeMem(DelPAddr);
}
CTEFreeLock(&Interface->ai_lock, Handle);
return(DelPAddr != NULL);
} else if (Type == LLIP_ADDR_MCAST)
return ARPDelMCast(Interface, Address);
else
return FALSE;
}
//*AddrNotifyLink - Notify link layer of Network Address changes
//
// Called when address are added/deleted on an interface
//
// Entry: Interface - ARPinterface pointer
//
// returns: NDIS_STATUS.Also sets ai_telladdrchng if status is failure
// when this happens caller can check and see if next addr notification
// need to be done or not.
//
NDIS_STATUS
AddrNotifyLink(ARPInterface * Interface)
{
PNETWORK_ADDRESS_LIST AddressList;
NETWORK_ADDRESS UNALIGNED *Address;
int i = 0, size, count;
ARPIPAddr *addrlist;
NDIS_STATUS status = NDIS_STATUS_FAILURE;
CTELockHandle Handle;
CTEGetLock(&Interface->ai_lock, &Handle);
size = Interface->ai_ipaddrcnt * (sizeof(NETWORK_ADDRESS_IP) +
FIELD_OFFSET(NETWORK_ADDRESS, Address)) +
FIELD_OFFSET(NETWORK_ADDRESS_LIST, Address);
AddressList = CTEAllocMemN(size, 'WiCT');
if (AddressList) {
addrlist = &Interface->ai_ipaddr;
count = Interface->ai_ipaddrcnt;
AddressList->AddressType = NDIS_PROTOCOL_ID_TCP_IP;
while (addrlist && count) {
NETWORK_ADDRESS_IP UNALIGNED *tmpIPAddr;
uchar *Address0;
Address0 = (uchar *) & AddressList->Address[0];
Address = (PNETWORK_ADDRESS) (Address0 + i * (FIELD_OFFSET(NETWORK_ADDRESS, Address) + sizeof(NETWORK_ADDRESS_IP)));
tmpIPAddr = (PNETWORK_ADDRESS_IP) & Address->Address[0];
Address->AddressLength = sizeof(NETWORK_ADDRESS_IP);
Address->AddressType = NDIS_PROTOCOL_ID_TCP_IP;
RtlCopyMemory(&tmpIPAddr->in_addr, &addrlist->aia_addr, sizeof(IPAddr));
count--;
addrlist = addrlist->aia_next;
i++;
}
CTEFreeLock(&Interface->ai_lock, Handle);
AddressList->AddressCount = i;
status = DoNDISRequest(Interface,
NdisRequestSetInformation,
OID_GEN_NETWORK_LAYER_ADDRESSES,
AddressList,
size,
NULL, TRUE);
if (status != NDIS_STATUS_SUCCESS) {
CTEGetLock(&Interface->ai_lock, &Handle);
Interface->ai_telladdrchng = 0;
CTEFreeLock(&Interface->ai_lock, Handle);
}
CTEFreeMem(AddressList);
} else {
CTEFreeLock(&Interface->ai_lock, Handle);
status = NDIS_STATUS_RESOURCES;
}
return status;
}
#if !MILLEN
//* ARPCancelPackets
//
// Entry: Context - Pointer to the ARPInterface
// ID - Pattern that need to be passed down to ndis
//
// Returns: Nothing
//
VOID
__stdcall
ARPCancelPackets(void *Context, void *ID)
{
ARPInterface *Interface = (ARPInterface *) Context;
NdisCancelSendPackets(Interface->ai_handle,ID);
}
#endif
//* DoWakeupPattern - Adds and removes wakeup pattern.
//
// Entry: Context - Pointer to the ARPInterface
// PtrnDesc - Pattern buffer(s) of high level protocol
// protoid - the proto type used in ethernet or snap type fields.
// AddPattern - TRUE if pattern is to be added, FALSE if it is to be removed.
//
// Returns: Nothing.
//
NDIS_STATUS
__stdcall
DoWakeupPattern(void *Context, PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc, ushort protoid, BOOLEAN AddPattern)
{
ARPInterface *Interface = (ARPInterface *) Context;
uint PtrnLen;
uint PtrnBufferLen;
uint MaskLen;
PNET_PM_WAKEUP_PATTERN_DESC CurPtrnDesc;
uchar *NextMask, *NextPtrn;
const uchar *MMask;
uint MMaskLength;
uchar NextMaskBit;
uchar *Buffer;
PNDIS_PM_PACKET_PATTERN PtrnBuffer;
NDIS_STATUS Status;
//
// First find the total length of the pattern.
// Pattern starts right at MacHeader.
//
// First add the media portion of the header.
//
PtrnLen = Interface->ai_hdrsize + Interface->ai_snapsize;
// now add the high level proto pattern size.
CurPtrnDesc = PtrnDesc;
while (CurPtrnDesc != (PNET_PM_WAKEUP_PATTERN_DESC) NULL) {
PtrnLen += CurPtrnDesc->PtrnLen;
CurPtrnDesc = CurPtrnDesc->Next;
}
// length of the mask: every byte of pattern requires
// one bit of the mask.
MaskLen = GetWakeupPatternMaskLength(PtrnLen);
// total length of the pattern buffer to be given to ndis.
PtrnBufferLen = sizeof(NDIS_PM_PACKET_PATTERN) + PtrnLen + MaskLen;
if ((Buffer = CTEAllocMemN(PtrnBufferLen, 'XiCT')) == (uchar *) NULL) {
return NDIS_STATUS_RESOURCES;
}
RtlZeroMemory(Buffer, PtrnBufferLen);
PtrnBuffer = (PNDIS_PM_PACKET_PATTERN) Buffer;
PtrnBuffer->PatternSize = PtrnLen;
NextMask = Buffer + sizeof(NDIS_PM_PACKET_PATTERN);
NextPtrn = NextMask + MaskLen;
PtrnBuffer->MaskSize = MaskLen;
PtrnBuffer->PatternOffset =
(ULONG) ((ULONG_PTR) NextPtrn - (ULONG_PTR) PtrnBuffer);
// Figure out what type of media this is, and do the appropriate thing.
switch (Interface->ai_media) {
case NdisMedium802_3:
if (Interface->ai_snapsize == 0) {
ENetHeader UNALIGNED *Hdr = (ENetHeader UNALIGNED *) NextPtrn;
Hdr->eh_type = net_short(protoid);
MMask = ENetPtrnMsk;
} else {
MMask = ENetSNAPPtrnMsk;
}
break;
case NdisMedium802_5:
if (Interface->ai_snapsize == 0) {
MMask = TRPtrnMsk;
} else {
MMask = TRSNAPPtrnMsk;
}
break;
case NdisMediumFddi:
if (Interface->ai_snapsize == 0) {
MMask = FDDIPtrnMsk;
} else {
MMask = FDDISNAPPtrnMsk;
}
break;
case NdisMediumArcnet878_2:
MMask = ARCPtrnMsk;
break;
default:
ASSERT(0);
Interface->ai_outerrors++;
CTEFreeMem(Buffer);
return NDIS_STATUS_UNSUPPORTED_MEDIA;
}
NextPtrn += Interface->ai_hdrsize;
// Copy in SNAP header, if any.
if (Interface->ai_snapsize) {
SNAPHeader UNALIGNED *SNAPPtr = (SNAPHeader UNALIGNED *) NextPtrn;
RtlCopyMemory(SNAPPtr, ARPSNAP, Interface->ai_snapsize);
SNAPPtr->sh_etype = net_short(protoid);
NextPtrn += Interface->ai_snapsize;
}
//
MMaskLength = (Interface->ai_snapsize + Interface->ai_hdrsize - 1) / 8 + 1;
// copy the mask for media part
RtlCopyMemory(NextMask, MMask, MMaskLength);
NextMaskBit = (Interface->ai_hdrsize + Interface->ai_snapsize) % 8;
NextMask = NextMask + (Interface->ai_hdrsize + Interface->ai_snapsize) / 8;
// copy the pattern and mask of high level proto.
CurPtrnDesc = PtrnDesc;
while (CurPtrnDesc) {
uint CopyBits = CurPtrnDesc->PtrnLen;
uchar *SrcMask = CurPtrnDesc->Mask;
uchar SrcMaskBit = 0;
RtlCopyMemory(NextPtrn, CurPtrnDesc->Ptrn, CurPtrnDesc->PtrnLen);
NextPtrn += CurPtrnDesc->PtrnLen;
while (CopyBits--) {
*NextMask |= ((*SrcMask & (0x1 << SrcMaskBit)) ? (0x1 << NextMaskBit) : 0);
if ((NextMaskBit = ((NextMaskBit + 1) % 8)) == 0) {
NextMask++;
}
if ((SrcMaskBit = ((SrcMaskBit + 1) % 8)) == 0) {
SrcMask++;
}
}
CurPtrnDesc = CurPtrnDesc->Next;
}
// now tell ndis to set or remove the pattern.
Status = DoNDISRequest(
Interface,
NdisRequestSetInformation,
AddPattern ? OID_PNP_ADD_WAKE_UP_PATTERN : OID_PNP_REMOVE_WAKE_UP_PATTERN,
PtrnBuffer,
PtrnBufferLen,
NULL, TRUE);
CTEFreeMem(Buffer);
return Status;
}
//* ARPWakeupPattern - add or remove ARP wakeup pattern.
//
// Entry: Interface - Pointer to the ARPInterface
// Addr - IPAddr for which we need to set ARP pattern filter.
//
// Returns: Nothing.
//
NDIS_STATUS
ARPWakeupPattern(ARPInterface * Interface, IPAddr Addr, BOOLEAN AddPattern)
{
PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc;
uint PtrnLen;
uint MaskLen;
const uchar *PtrnMask;
NDIS_STATUS Status;
//
// create high level proto (ARP here) pattern descriptor.
//
// len of pattern.
PtrnLen = sizeof(ARPHeader);
// adjust for Arcnet.
if (Interface->ai_media == NdisMediumArcnet878_2) {
PtrnLen -= ARCNET_ARPHEADER_ADJUSTMENT;
PtrnMask = ARCARPPtrnMsk;
} else {
PtrnMask = ARPPtrnMsk;
}
// masklen = 1 bit per every byte of pattern.
MaskLen = GetWakeupPatternMaskLength(PtrnLen);
if ((PtrnDesc = CTEAllocMemN(sizeof(NET_PM_WAKEUP_PATTERN_DESC) + PtrnLen + MaskLen, 'YiCT')) != (PNET_PM_WAKEUP_PATTERN_DESC) NULL) {
ARPHeader UNALIGNED *Hdr;
uchar *IPAddrPtr;
RtlZeroMemory(PtrnDesc, sizeof(NET_PM_WAKEUP_PATTERN_DESC) + PtrnLen + MaskLen);
// set the ptrn and mask pointers in the buffer.
PtrnDesc->PtrnLen = (USHORT) PtrnLen;
PtrnDesc->Ptrn = (uchar *) PtrnDesc + sizeof(NET_PM_WAKEUP_PATTERN_DESC);
PtrnDesc->Mask = (uchar *) PtrnDesc + sizeof(NET_PM_WAKEUP_PATTERN_DESC) + PtrnLen;
// we need to wakeup on ARP request for our IPAddr.
// so set the opcode and dest ip addr fields of ARP.
Hdr = (ARPHeader UNALIGNED *) PtrnDesc->Ptrn;
Hdr->ah_opcode = net_short(ARP_REQUEST);
IPAddrPtr = Hdr->ah_shaddr + Interface->ai_addrlen + sizeof(IPAddr) + Interface->ai_addrlen;
*(IPAddr UNALIGNED *) IPAddrPtr = Addr;
RtlCopyMemory(PtrnDesc->Mask, PtrnMask, MaskLen);
// give it to ndis.
Status = DoWakeupPattern(
Interface,
PtrnDesc,
ARP_ETYPE_ARP,
AddPattern);
// free the ptrn desc.
CTEFreeMem(PtrnDesc);
//now add wakeup pattren for directed mac address
{
uint PtrnBufferLen;
PNDIS_PM_PACKET_PATTERN PtrnBuffer;
uchar *Buffer;
PtrnLen = ARP_802_ADDR_LENGTH; //eth dest address
MaskLen = 1; //1 byte, needs 6 bits, 1 bit/byte
PtrnBufferLen = sizeof(NDIS_PM_PACKET_PATTERN) + PtrnLen + MaskLen;
if (Buffer = CTEAllocMem(PtrnBufferLen)) {
RtlZeroMemory(Buffer, PtrnBufferLen);
PtrnBuffer = (PNDIS_PM_PACKET_PATTERN) Buffer;
PtrnBuffer->PatternSize = PtrnLen;
PtrnBuffer->MaskSize = MaskLen;
PtrnBuffer->PatternOffset = sizeof(NDIS_PM_PACKET_PATTERN) + 1;
*(Buffer + sizeof(NDIS_PM_PACKET_PATTERN)) = 0x3F;
RtlCopyMemory(Buffer + sizeof(NDIS_PM_PACKET_PATTERN) + 1, Interface->ai_addr, ARP_802_ADDR_LENGTH);
Status = DoNDISRequest(
Interface,
NdisRequestSetInformation,
AddPattern ? OID_PNP_ADD_WAKE_UP_PATTERN : OID_PNP_REMOVE_WAKE_UP_PATTERN,
PtrnBuffer,
PtrnBufferLen,
NULL, TRUE);
CTEFreeMem(Buffer);
}
}
return Status;
}
return IP_NO_RESOURCES;
}
//** CompleteIPSetNTEAddrRequestDelayed -
//
// calls CompleteIPSetNTEAddrRequest on a delayed worker thread
//
// Entry:
// Context - pointer to the control block
// Exit:
// None.
//
void
CompleteIPSetNTEAddrRequestDelayed(CTEEvent * WorkerThreadEvent, PVOID Context)
{
AddAddrNotifyEvent *DelayedEvent;
SetAddrControl *SAC;
SetAddrRtn Rtn;
IPAddr Address;
IP_STATUS Status;
DelayedEvent = (AddAddrNotifyEvent *) Context;
SAC = DelayedEvent->SAC; // the client context block;
Address = DelayedEvent->Address; // The address for which SetNTEAddr was called for.
Status = DelayedEvent->Status;
// Free the worker thread event.
CTEFreeMem(Context);
IPAddAddrComplete(Address, SAC, Status);
}
#if FFP_SUPPORT
//* ARPReclaimRequestMem - Post processing upon request completion
//
// Called upon completion of NDIS requests that originate at ARP
//
// Input: pRequestInfo - Points to request IP sends ARP
//
// Returns: None
//
void
ARPReclaimRequestMem(PVOID pRequestInfo)
{
// Decrement ref count, and reclaim memory if it drops to zero
if (InterlockedDecrement(&((ReqInfoBlock *) pRequestInfo)->RequestRefs) == 0) {
// TCPTRACE(("ARPReclaimRequestMem: Freeing mem at pReqInfo = %08X\n",
// pRequestInfo));
CTEFreeMem(pRequestInfo);
}
}
#endif // if FFP_SUPPORT
//* ARPTimeout - ARP timeout routine.
//
// This is the timeout routine that is called periodically. We scan the ARP table, looking
// for invalid entries that can be removed.
//
// Entry: Timer - Pointer to the timer that just fired.
// Context - Pointer to the interface to be timed out.
//
// Returns: Nothing.
//
void
ARPTimeout(CTEEvent * Timer, void *Context)
{
ARPInterface *Interface = (ARPInterface *) Context; // Our interface.
ARPTable *Table;
ARPTableEntry *Current, *Previous;
int i; // Index variable.
ulong Now = CTESystemUpTime(), ValidTime;
CTELockHandle tblhandle, entryhandle;
uchar Deleted;
PNDIS_PACKET PList = (PNDIS_PACKET) NULL;
ARPIPAddr *Addr;
// Walk down the list of addresses, decrementing the age.
CTEGetLock(&Interface->ai_lock, &tblhandle);
if (Interface->ai_conflict && !(--Interface->ai_delay)) {
ARPNotifyStruct *NotifyStruct = Interface->ai_conflict;
CTEScheduleDelayedEvent(&NotifyStruct->ans_event, NotifyStruct);
Interface->ai_conflict = NULL;
}
Addr = &Interface->ai_ipaddr;
do {
if (Addr->aia_age != ARPADDR_OLD_LOCAL) {
(Addr->aia_age)--;
if (Addr->aia_age == ARPADDR_OLD_LOCAL) {
if (Addr->aia_context != NULL) {
SetAddrControl *SAC;
AddAddrNotifyEvent *DelayedEvent;
IPAddr IpAddress;
SAC = (SetAddrControl *) Addr->aia_context;
Addr->aia_context = NULL;
IpAddress = Addr->aia_addr;
CTEFreeLock(&Interface->ai_lock, tblhandle);
// We cannot call completion routine at timer DPC
// because completion routine will need to notify
// TDI clients and that could take long time.
DelayedEvent = CTEAllocMemNBoot(sizeof(AddAddrNotifyEvent), 'ZiCT');
if (DelayedEvent) {
DelayedEvent->SAC = SAC;
DelayedEvent->Address = IpAddress;
DelayedEvent->Status = IP_SUCCESS;
CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed);
CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent);
}
CTEGetLock(&Interface->ai_lock, &tblhandle);
}
} else {
CTEFreeLock(&Interface->ai_lock, tblhandle);
SendARPRequest(Interface, Addr->aia_addr, ARP_RESOLVING_GLOBAL,
NULL, TRUE);
CTEGetLock(&Interface->ai_lock, &tblhandle);
}
}
Addr = Addr->aia_next;
} while (Addr != NULL);
CTEFreeLock(&Interface->ai_lock, tblhandle);
// Loop through the ARP table for this interface, and delete stale entries.
CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
Table = Interface->ai_ARPTbl;
for (i = 0; i < ARP_TABLE_SIZE; i++) {
Previous = (ARPTableEntry *) ((uchar *) & ((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next));
Current = (*Table)[i];
while (Current != (ARPTableEntry *) NULL) {
CTEGetLock(&Current->ate_lock, &entryhandle);
Deleted = 0;
//Delete the entry if it was used for api purpose
if (Current->ate_resolveonly) {
ARPControlBlock *ArpContB, *tmpArpContB;
PNDIS_PACKET Packet = Current->ate_packet;
ArpContB = Current->ate_resolveonly;
ASSERT(Current->ate_resolveonly != NULL);
while (ArpContB) {
ArpRtn rtn;
//Complete the pending request
rtn = (ArpRtn) ArpContB->CompletionRtn;
ArpContB->status = 0;
tmpArpContB = ArpContB->next;
(*rtn) (ArpContB, STATUS_UNSUCCESSFUL);
ArpContB = tmpArpContB;
}
Current->ate_resolveonly = NULL;
if (Packet != (PNDIS_PACKET) NULL) {
((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link = PList;
PList = Packet;
}
RemoveARPTableEntry(Previous, Current);
Interface->ai_count--;
Deleted = 1;
goto doneapi;
}
if (Current->ate_state == ARP_GOOD) {
//
// The ARP entry is valid for ARP_VALID_TIMEOUT by default.
// If a cache life greater than ARP_VALID_TIMEOUT has been
// configured, we'll make the entry valid for that time.
//
ValidTime = ArpCacheLife * ARP_TIMER_TIME;
if (ValidTime < (ArpMinValidCacheLife * 1000)) {
ValidTime = (ArpMinValidCacheLife * 1000);
}
} else {
ValidTime = ARP_RESOLVE_TIMEOUT;
}
if (Current->ate_valid != ALWAYS_VALID &&
(((Now - Current->ate_valid) > ValidTime) ||
(Current->ate_state == ARP_GOOD &&
!(--(Current->ate_useticks))))) {
if (Current->ate_state != ARP_RESOLVING_LOCAL) {
// Really need to delete this guy.
PNDIS_PACKET Packet = Current->ate_packet;
if (((Now - Current->ate_valid) > ValidTime) && Current->ate_refresh) {
DEBUGMSG(DBG_INFO && DBG_ARP,
(DTEXT("ARPTimeout: Expiring ATE %x\n"), Current));
if (Packet != (PNDIS_PACKET) NULL) {
((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link
= PList;
PList = Packet;
}
RemoveARPTableEntry(Previous, Current);
Interface->ai_count--;
Deleted = 1;
} else {
//Just try to validate this again.
Current->ate_valid = Now + ARP_REFRESH_TIME;
Current->ate_refresh=TRUE;
}
} else {
IPAddr Dest = Current->ate_dest;
// This entry is only resoving locally, presumably this is
// token ring. We'll need to transmit a 'global' resolution
// now.
ASSERT(Interface->ai_media == NdisMedium802_5);
Now = CTESystemUpTime();
Current->ate_valid = Now;
Current->ate_state = ARP_RESOLVING_GLOBAL;
CTEFreeLock(&Current->ate_lock, entryhandle);
CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
// Send a global request.
SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
NULL, TRUE);
CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
// Since we've freed the locks, we need to start over from
// the start of this chain.
Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]),
ate_next);
Current = (*Table)[i];
continue;
}
}
doneapi:
// If we deleted the entry, leave the previous pointer alone,
// advance the current pointer, and free the memory. Otherwise
// move both pointers forward. We can free the entry lock now
// because the next pointers are protected by the table lock, and
// we've removed it from the list so nobody else should
// find it anyway.
CTEFreeLock(&Current->ate_lock, entryhandle);
if (Deleted) {
ARPTableEntry *Temp = Current;
Current = Current->ate_next;
CTEFreeMem(Temp);
} else {
Previous = Current;
Current = Current->ate_next;
}
}
}
CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
while (PList != (PNDIS_PACKET) NULL) {
PNDIS_PACKET Packet = PList;
PList = ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link;
IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
}
//
// Dont requeue if interface is going down and we need to stop the timer
//
if (Interface->ai_stoptimer) {
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP interface %lx is down - dont requeue the timer - signal the waiter\n", Interface));
Interface->ai_timerstarted = FALSE;
CTESignal(&Interface->ai_timerblock, NDIS_STATUS_SUCCESS);
} else {
CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface);
}
#if FFP_SUPPORT
// Flush Processing - This can be done after starting the timer
CTEGetLock(&Interface->ai_lock, &tblhandle);
// If FFP supported on this interface & it is time to do a flush
if ((Interface->ai_ffpversion) &&
(++Interface->ai_ffplastflush >= FFP_ARP_FLUSH_INTERVAL)) {
ReqInfoBlock *pRequestInfo;
FFPFlushParams *pFlushInfo;
TCPTRACE(("ARPTimeout: Sending a FFP flush to ARPInterface %08X\n",
Interface));
// Allocate the request block - For General and Request Specific Parts
pRequestInfo = CTEAllocMemN(sizeof(ReqInfoBlock) + sizeof(FFPFlushParams), '0ICT');
// TCPTRACE(("ARPTimeout: Allocated mem at pReqInfo = %08X\n",
// pRequestInfo));
if (pRequestInfo != NULL) {
// Prepare the params for the request [Part common to all requests]
pRequestInfo->RequestType = OID_FFP_FLUSH;
pRequestInfo->ReqCompleteCallback = ARPReclaimRequestMem;
// Prepare the params for the request [Part specific to this request]
pRequestInfo->RequestLength = sizeof(FFPFlushParams);
// Flush all caches that FFP keeps - just a safe reset of FFP state
pFlushInfo = (FFPFlushParams *) pRequestInfo->RequestInfo;
pFlushInfo->NdisProtocolType = NDIS_PROTOCOL_ID_TCP_IP;
// Assign the ref count to 1 => Used for just a single request
pRequestInfo->RequestRefs = 1;
DoNDISRequest(Interface, NdisRequestSetInformation, OID_FFP_FLUSH,
pFlushInfo, sizeof(FFPFlushParams), NULL, FALSE);
// Reset the number of timer ticks since the last FFP request
Interface->ai_ffplastflush = 0;
} else {
TCPTRACE(("Error: Unable to allocate memory for NdisRequest\n"));
}
}
#if DBG
if (fakereset) {
NDIS_STATUS Status;
NdisReset(&Status, Interface->ai_handle);
KdPrint(("fakereset: %x\n", Status));
}
#endif
CTEFreeLock(&Interface->ai_lock, tblhandle);
#endif // if FFP_SUPPORT
}
//* IsLocalAddr - Return info. about local status of address.
//
// Called when we need info. about whether or not a particular address is
// local. We return info about whether or not it is, and if it is how old
// it is.
//
// Entry: Interface - Pointer to interface structure to be searched.
// Address - Address in question.
//
// Returns: ARPADDR_*, for how old it is.
//
//
uint
IsLocalAddr(ARPInterface * Interface, IPAddr Address)
{
CTELockHandle Handle;
ARPIPAddr *CurrentAddr;
uint Age;
// If we are asking about the null ip address, we don't want to consider
// it as a true local address.
//
if (IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) {
return ARPADDR_NOT_LOCAL;
}
CTEGetLock(&Interface->ai_lock, &Handle);
CurrentAddr = &Interface->ai_ipaddr;
Age = ARPADDR_NOT_LOCAL;
do {
if (CurrentAddr->aia_addr == Address) {
Age = CurrentAddr->aia_age;
break;
}
CurrentAddr = CurrentAddr->aia_next;
} while (CurrentAddr != NULL);
CTEFreeLock(&Interface->ai_lock, Handle);
return Age;
}
//* ARPLocalAddr - Determine whether or not a given address if local.
//
// This routine is called when we receive an incoming packet and need to
// determine whether or not it's local. We look up the provided address on
// the specified interface.
//
// Entry: Interface - Pointer to interface structure to be searched.
// Address - Address in question.
//
// Returns: TRUE if it is a local address, FALSE if it's not.
//
uchar
ARPLocalAddr(ARPInterface * Interface, IPAddr Address)
{
CTELockHandle Handle;
ARPPArpAddr *CurrentPArp;
IPMask Mask, NetMask;
IPAddr MatchAddress;
// First, see if he's a local (not-proxy) address.
if (IsLocalAddr(Interface, Address) != ARPADDR_NOT_LOCAL)
return TRUE;
CTEGetLock(&Interface->ai_lock, &Handle);
// Didn't find him in out local address list. See if he exists on our
// proxy ARP list.
for (CurrentPArp = Interface->ai_parpaddr; CurrentPArp != NULL;
CurrentPArp = CurrentPArp->apa_next) {
// See if this guy matches.
Mask = CurrentPArp->apa_mask;
MatchAddress = Address & Mask;
if (IP_ADDR_EQUAL(CurrentPArp->apa_addr, MatchAddress)) {
// He matches. We need to make a few more checks to make sure
// we don't reply to a broadcast address.
if (Mask == HOST_MASK) {
// We're matching the whole address, so it's OK.
CTEFreeLock(&Interface->ai_lock, Handle);
return TRUE;
}
// See if the non-mask part it all-zeros. Since the mask presumably
// covers a subnet, this trick will prevent us from replying to
// a zero host part.
if (IP_ADDR_EQUAL(MatchAddress, Address))
continue;
// See if the host part is all ones.
if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~Mask)))
continue;
// If the mask we were given is not the net mask for this address,
// we'll need to repeat the above checks.
NetMask = IPNetMask(Address);
if (NetMask != Mask) {
MatchAddress = Address & NetMask;
if (IP_ADDR_EQUAL(MatchAddress, Address))
continue;
if (IP_ADDR_EQUAL(Address, MatchAddress |
(IP_LOCAL_BCST & ~NetMask)))
continue;
}
// If we get to this point we've passed all the tests, so it's
// local.
CTEFreeLock(&Interface->ai_lock, Handle);
return TRUE;
}
}
CTEFreeLock(&Interface->ai_lock, Handle);
return FALSE;
}
//* NotifyConflictProc - Notify the user of an address conflict.
//
// Called when we need to notify the user of an address conflict. The
// exact mechanism is system dependent, but generally involves a popup.
//
// Input: Event - Event that fired.
// Context - Pointer to ARPNotifyStructure.
//
// Returns: Nothing.
//
void
NotifyConflictProc(CTEEvent * Event, void *Context)
{
#if MILLEN
//
// Call into VIP to VIP_NotifyConflicProc. This will schedule an Appy
// event, etc. This is a little sleazy, but we do an INT 20, give the
// appropriate index into service table and VIP VxD ID.
//
// void VIP_NotifyConflictProc(CTEEvent *Event, void *Context);
// Event is unused.
//
_asm {
push Context
push Context
_emit 0xcd
_emit 0x20
_emit 0x15 // VIP_NotifyConflictProc (Low)
_emit 0x00 // VIP_NotifyConflictProc (High)
_emit 0x89 // VIP VxD ID (Low)
_emit 0x04 // VIP VxD ID (High)
add esp,8
}
#else // MILLEN
ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *) Context;
PWCHAR stringList[2];
uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)];
WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
uint i;
uint IPAddrCharCount;
UNICODE_STRING unicodeString;
ANSI_STRING ansiString;
PAGED_CODE();
//
// Convert the IP address into a string.
//
IPAddrCharCount = 0;
for (i = 0; i < sizeof(IPAddr); i++) {
uint CurrentByte;
CurrentByte = NotifyStruct->ans_addr & 0xff;
if (CurrentByte > 99) {
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
CurrentByte %= 100;
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
CurrentByte %= 10;
} else if (CurrentByte > 9) {
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
CurrentByte %= 10;
}
IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
if (i != (sizeof(IPAddr) - 1))
IPAddrBuffer[IPAddrCharCount++] = '.';
NotifyStruct->ans_addr >>= 8;
}
//
// Convert the hardware address into a string.
//
for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
uchar CurrentHalf;
CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
HWAddrBuffer[i * 3] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' :
(CurrentHalf - 10) + 'A');
CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
HWAddrBuffer[(i * 3) + 1] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' :
(CurrentHalf - 10) + 'A');
if (i != (NotifyStruct->ans_hwaddrlen - 1))
HWAddrBuffer[(i * 3) + 2] = ':';
}
//
// Unicode the strings.
//
*unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL;
unicodeString.Buffer = unicodeIPAddrBuffer;
unicodeString.Length = 0;
unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1);
ansiString.Buffer = IPAddrBuffer;
ansiString.Length = (USHORT) IPAddrCharCount;
ansiString.MaximumLength = (USHORT) IPAddrCharCount;
RtlAnsiStringToUnicodeString(
&unicodeString,
&ansiString,
FALSE
);
stringList[0] = unicodeIPAddrBuffer;
unicodeString.Buffer = unicodeHWAddrBuffer;
unicodeString.Length = 0;
unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3);
ansiString.Buffer = HWAddrBuffer;
ansiString.Length = (NotifyStruct->ans_hwaddrlen * 3) - 1;
ansiString.MaximumLength = NotifyStruct->ans_hwaddrlen * 3;
RtlAnsiStringToUnicodeString(
&unicodeString,
&ansiString,
FALSE
);
stringList[1] = unicodeHWAddrBuffer;
//
// Kick off a popup and log an event.
//
if (NotifyStruct->ans_shutoff) {
CTELogEvent(
IPDriverObject,
EVENT_TCPIP_ADDRESS_CONFLICT1,
0,
2,
stringList,
0,
NULL
);
IoRaiseInformationalHardError(
STATUS_IP_ADDRESS_CONFLICT1,
NULL,
NULL
);
} else {
CTELogEvent(
IPDriverObject,
EVENT_TCPIP_ADDRESS_CONFLICT2,
0,
2,
stringList,
0,
NULL
);
IoRaiseInformationalHardError(
STATUS_IP_ADDRESS_CONFLICT2,
NULL,
NULL
);
}
CTEFreeMem(NotifyStruct);
#endif // !MILLEN
return;
}
//* DebugConflictProc - Prints some debugging info in case of addr conflicts
// Prints the ip and hw addr of the guy causing the conflict
// Context - Pointer to ARPNotifyStructure.
//
// Returns: Nothing.
//
void
DebugConflictProc(void *Context, BOOLEAN Bugcheck)
{
ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *) Context;
uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
uint i;
uint IPAddrCharCount;
IPAddr ans_addr;
//
// Save the IP address in case we need it later, then convert into
// a string.
//
ans_addr = NotifyStruct->ans_addr;
IPAddrCharCount = 0;
for (i = 0; i < sizeof(IPAddr); i++) {
uint CurrentByte;
CurrentByte = NotifyStruct->ans_addr & 0xff;
if (CurrentByte > 99) {
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
CurrentByte %= 100;
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
CurrentByte %= 10;
} else if (CurrentByte > 9) {
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
CurrentByte %= 10;
}
IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
if (i != (sizeof(IPAddr) - 1))
IPAddrBuffer[IPAddrCharCount++] = '.';
NotifyStruct->ans_addr >>= 8;
}
IPAddrBuffer[IPAddrCharCount] = '\0';
//
// Convert the hardware address into a string.
//
for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
uchar CurrentHalf;
CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
HWAddrBuffer[i * 3] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' :
(CurrentHalf - 10) + 'A');
CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
HWAddrBuffer[(i * 3) + 1] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' :
(CurrentHalf - 10) + 'A');
if (i != (NotifyStruct->ans_hwaddrlen - 1))
HWAddrBuffer[(i * 3) + 2] = ':';
}
HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0';
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
"TCPIP: Address Conflict: IPAddr %s HWAddr %s \n",
IPAddrBuffer, HWAddrBuffer));
//
// If told to bugcheck, then do so.
//
if (Bugcheck) {
ULONG addressBytes[3];
uint currentAddressByte, currentAddressShift;
//
// Copy hardware address bytes to the DWORDs, as much as possible.
//
addressBytes[0] = 0;
addressBytes[1] = 0;
addressBytes[2] = 0;
currentAddressByte = 0;
currentAddressShift = 24;
for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
addressBytes[currentAddressByte] |=
NotifyStruct->ans_hwaddr[i] << currentAddressShift;
if (currentAddressShift == 0) {
if (currentAddressByte == 2) {
break;
}
++currentAddressByte;
currentAddressShift = 24;
} else {
currentAddressShift -= 8;
}
}
KeBugCheckEx(NETWORK_BOOT_DUPLICATE_ADDRESS,
ans_addr,
addressBytes[0],
addressBytes[1],
addressBytes[2]);
}
return;
}
//* HandleARPPacket - Process an incoming ARP packet.
//
// This is the main routine to process an incoming ARP packet. We look at
// all ARP frames, and update our cache entry for the source address if one
// exists. Else, if we are the target we create an entry if one doesn't
// exist. Finally, we'll handle the opcode, responding if this is a request
// or sending pending packets if this is a response.
//
// Entry: Interface - Pointer to interface structure for this adapter.
// Header - Pointer to header buffer.
// HeaderSize - Size of header buffer.
// ARPHdr - ARP packet header.
// ARPHdrSize - Size of ARP header.
// ProtOffset - Offset into original data field of arp header.
// Will be non-zero if we're using SNAP.
//
// Returns: An NDIS_STATUS value to be returned to the NDIS driver.
//
NDIS_STATUS
HandleARPPacket(ARPInterface * Interface, void *Header, uint HeaderSize,
ARPHeader UNALIGNED * ARPHdr, uint ARPHdrSize, uint ProtOffset)
{
ARPTableEntry *Entry; // Entry in ARP table
CTELockHandle LHandle, TableHandle;
RC UNALIGNED *SourceRoute = (RC UNALIGNED *) NULL; // Pointer to Source Route info, if any.
uint SourceRouteSize = 0;
ulong Now = CTESystemUpTime();
uchar LocalAddr;
uint LocalAddrAge;
uchar *SHAddr, *DHAddr;
IPAddr UNALIGNED *SPAddr, *DPAddr;
ENetHeader *ENetHdr;
TRHeader *TRHdr;
FDDIHeader *FHdr;
ARCNetHeader *AHdr;
ushort MaxMTU;
uint UseSNAP;
SetAddrControl *SAC=NULL;
ARPIPAddr *CurrentAddr;
AddAddrNotifyEvent *DelayedEvent;
uint NUCast;
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_RX,
(DTEXT("+HandleARPPacket(%x, %x, %d, %x, %d, %d)\n"),
Interface, Header, HeaderSize, ARPHdr, ARPHdrSize, ProtOffset));
// Validate the opcode
//
if ((ARPHdr->ah_opcode != net_short(ARP_REQUEST)) &&
(ARPHdr->ah_opcode != net_short(ARP_RESPONSE))) {
return NDIS_STATUS_NOT_RECOGNIZED;
}
// We examine all ARP frames. If we find the source address in the ARP table, we'll
// update the hardware address and set the state to valid. If we're the
// target and he's not in the table, we'll add him. Otherwise if we're the
// target and this is a response we'll send any pending packets to him.
if (Interface->ai_media != NdisMediumArcnet878_2) {
if (ARPHdrSize < sizeof(ARPHeader))
return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small.
if (ARPHdr->ah_hw != net_short(ARP_HW_ENET) &&
ARPHdr->ah_hw != net_short(ARP_HW_802))
return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type
if (ARPHdr->ah_hlen != ARP_802_ADDR_LENGTH)
return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length.
if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize == 0)
UseSNAP = FALSE;
else
UseSNAP = (ProtOffset != 0);
// Figure out SR size on TR.
if (Interface->ai_media == NdisMedium802_5) {
// Check for source route information. SR is present if the header
// size is greater than the standard TR header size. If the SR is
// only an RC field, we ignore it because it came from the same
// ring which is the same as no SR.
if ((HeaderSize - sizeof(TRHeader)) > sizeof(RC)) {
SourceRouteSize = HeaderSize - sizeof(TRHeader);
SourceRoute = (RC UNALIGNED *) ((uchar *) Header +
sizeof(TRHeader));
}
}
SHAddr = ARPHdr->ah_shaddr;
SPAddr = (IPAddr UNALIGNED *) & ARPHdr->ah_spaddr;
DHAddr = ARPHdr->ah_dhaddr;
DPAddr = (IPAddr UNALIGNED *) & ARPHdr->ah_dpaddr;
} else {
if (ARPHdrSize < (sizeof(ARPHeader) - ARCNET_ARPHEADER_ADJUSTMENT))
return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small.
if (ARPHdr->ah_hw != net_short(ARP_HW_ARCNET))
return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type
if (ARPHdr->ah_hlen != 1)
return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length.
UseSNAP = FALSE;
SHAddr = ARPHdr->ah_shaddr;
SPAddr = (IPAddr UNALIGNED *) (SHAddr + 1);
DHAddr = (uchar *) SPAddr + sizeof(IPAddr);
DPAddr = (IPAddr UNALIGNED *) (DHAddr + 1);
}
if (ARPHdr->ah_pro != net_short(ARP_ETYPE_IP))
return NDIS_STATUS_NOT_RECOGNIZED; // Unsupported protocol type.
if (ARPHdr->ah_plen != sizeof(IPAddr))
return NDIS_STATUS_NOT_RECOGNIZED;
LocalAddrAge = ARPADDR_NOT_LOCAL;
// First, let's see if we have an address conflict.
//
LocalAddrAge = IsLocalAddr(Interface, *SPAddr);
if (LocalAddrAge != ARPADDR_NOT_LOCAL) {
// The source IP address is one of ours. See if the source h/w address
// is ours also.
if (ARPHdr->ah_hlen != Interface->ai_addrlen ||
CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) {
uint Shutoff;
ARPNotifyStruct *NotifyStruct;
// This isn't from us; we must have an address conflict somewhere.
// We always log an error about this. If what triggered this is a
// response and the address in conflict is young, we'll turn off
// the interface.
if (LocalAddrAge != ARPADDR_OLD_LOCAL &&
ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) {
// Send an arp request with the owner's address to reset the
// caches.
CTEGetLock(&Interface->ai_lock, &LHandle);
// now find the address that is in conflict and get the
// corresponding client context.
CurrentAddr = &Interface->ai_ipaddr;
do {
if (CurrentAddr->aia_addr == *SPAddr) {
SAC = (SetAddrControl *) CurrentAddr->aia_context;
CurrentAddr->aia_context = NULL;
break;
}
CurrentAddr = CurrentAddr->aia_next;
} while (CurrentAddr != NULL);
ASSERT(CurrentAddr);
CTEFreeLock(&Interface->ai_lock, LHandle);
SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL,
SHAddr, FALSE); // Send a request.
Shutoff = TRUE;
// Display the debug information for remote boot/install.
// This code should be kept.
{
ARPNotifyStruct *DebugNotifyStruct;
DebugNotifyStruct = CTEAllocMemN(offsetof(ARPNotifyStruct, ans_hwaddr) +
ARPHdr->ah_hlen, '1ICT');
if (DebugNotifyStruct != NULL) {
BOOLEAN bugcheck;
DebugNotifyStruct->ans_addr = *SPAddr;
DebugNotifyStruct->ans_shutoff = Shutoff;
DebugNotifyStruct->ans_hwaddrlen = (uint) ARPHdr->ah_hlen;
RtlCopyMemory(DebugNotifyStruct->ans_hwaddr, SHAddr,
ARPHdr->ah_hlen);
if (SAC == NULL) {
bugcheck = FALSE;
} else {
bugcheck = SAC->bugcheck_on_duplicate;
}
DebugConflictProc(DebugNotifyStruct, bugcheck);
CTEFreeMem(DebugNotifyStruct);
}
}
// We cannot call completion routine at this time
// because completion routine calls back into arp to
// reset the address and that may go down into ndis.
DelayedEvent = CTEAllocMemN(sizeof(AddAddrNotifyEvent), '2ICT');
if (DelayedEvent) {
DelayedEvent->SAC = SAC;
DelayedEvent->Address = *SPAddr;
DelayedEvent->Status = IP_DUPLICATE_ADDRESS;
CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed);
CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent);
} else {
ASSERT(FALSE);
}
if ((SAC != NULL) && !SAC->StaticAddr) {
// this is a dhcp adapter.
// don't display a warning dialog in this case - DHCP will
// alert the user
//
goto no_dialog;
}
} else {
if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) &&
(IsLocalAddr(Interface, *DPAddr) == ARPADDR_OLD_LOCAL)) {
// Send a response for gratuitous ARP.
SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr,
SourceRoute, SourceRouteSize, UseSNAP);
Shutoff = FALSE;
} else if (LocalAddrAge != ARPADDR_OLD_LOCAL) {
// our address is still young. we dont need to put the
// warning popup as it will be done by the code that
// checks for arp response in above if portion of the code.
goto no_dialog;
}
// Else. We have an old local address and received an ARP for
// a third address. Fall through and indicate address
// conflict.
}
// Now allocate a structure, and schedule an event to notify
// the user.
NotifyStruct = CTEAllocMemN(offsetof(ARPNotifyStruct, ans_hwaddr) +
ARPHdr->ah_hlen, '3ICT');
if (NotifyStruct != NULL) {
NotifyStruct->ans_addr = *SPAddr;
NotifyStruct->ans_shutoff = Shutoff;
NotifyStruct->ans_hwaddrlen = (uint) ARPHdr->ah_hlen;
RtlCopyMemory(NotifyStruct->ans_hwaddr, SHAddr,
ARPHdr->ah_hlen);
CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc);
if (Shutoff) {
// Delay notification for few seconds.
Interface->ai_conflict = NotifyStruct;
#if MILLEN
Interface->ai_delay = 5;
#else
Interface->ai_delay = 90; // delay 3 seconds.
#endif
} else
CTEScheduleDelayedEvent(&NotifyStruct->ans_event, NotifyStruct);
}
no_dialog:
;
}
return NDIS_STATUS_NOT_RECOGNIZED;
}
if (!EnableBcastArpReply) {
// Check for bogus arp entry
NUCast = ((*(SHAddr) &
Interface->ai_bcastmask) == Interface->ai_bcastval) ?
AI_NONUCAST_INDEX : AI_UCAST_INDEX;
if (NUCast == AI_NONUCAST_INDEX) {
return NDIS_STATUS_NOT_RECOGNIZED;
}
}
CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
MaxMTU = Interface->ai_mtu;
LocalAddr = ARPLocalAddr(Interface, *DPAddr);
// If the sender's address is not remote (i.e. multicast, broadcast,
// local, or just invalid), We don't want to create an entry for it or
// bother looking it up.
//
if ((DEST_REMOTE == GetAddrType(*SPAddr))) {
Entry = ARPLookup(Interface, *SPAddr, &LHandle);
if (Entry == (ARPTableEntry *) NULL) {
// Didn't find him, create one if it's for us. The call to ARPLookup
// returned with the ARPTblLock held, so we need to free it.
CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
if (LocalAddr) {
// If this was an ARP request, we need to create a new
// entry for the source info. If this was a reply, it was
// unsolicited and we don't create an entry.
//
if (ARPHdr->ah_opcode != net_short(ARP_RESPONSE)) {
Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle, 0);
}
} else {
return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us.
}
} else {
//if this is for userarp, make sure that it is out of the table
//while we still have the arp table lock.
if (Entry->ate_userarp) {
ARPTable *Table;
ARPTableEntry *PrevATE, *CurrentATE;
uint Index = ARP_HASH(*SPAddr);
CTELockHandle EntryHandle;
Table = Interface->ai_ARPTbl;
PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next);
CurrentATE = PrevATE;
while (CurrentATE != (ARPTableEntry *) NULL) {
if (CurrentATE == Entry) {
break;
}
PrevATE = CurrentATE;
CurrentATE = CurrentATE->ate_next;
}
if (CurrentATE != NULL) {
RemoveARPTableEntry(PrevATE, CurrentATE);
Interface->ai_count--;
}
}
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock, LHandle);
LHandle = TableHandle;
}
} else { // Source address was invalid for an Arp table entry.
CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
Entry = NULL;
}
// At this point, entry should be valid and we hold the lock on the entry
// in LHandle or entry is NULL.
if (Entry != (ARPTableEntry *) NULL) {
PNDIS_PACKET Packet; // Packet to be sent.
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_RX,
(DTEXT("HandleARPPacket: resolving addr for ATE %x\n"), Entry));
Entry->ate_refresh = FALSE;
// If the entry is already static, we'll want to leave it as static.
if (Entry->ate_valid != ALWAYS_VALID) {
// OK, we have an entry to use, and hold the lock on it. Fill in the
// required fields.
switch (Interface->ai_media) {
case NdisMedium802_3:
// This is an Ethernet.
ENetHdr = (ENetHeader *) Entry->ate_addr;
RtlCopyMemory(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
RtlCopyMemory(ENetHdr->eh_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
ENetHdr->eh_type = net_short(ARP_ETYPE_IP);
// If we're using SNAP on this entry, copy in the SNAP header.
if (UseSNAP) {
RtlCopyMemory(&Entry->ate_addr[sizeof(ENetHeader)], ARPSNAP,
sizeof(SNAPHeader));
Entry->ate_addrlength = (uchar) (sizeof(ENetHeader) +
sizeof(SNAPHeader));
*(ushort UNALIGNED *) & Entry->ate_addr[Entry->ate_addrlength - 2] =
net_short(ARP_ETYPE_IP);
} else
Entry->ate_addrlength = sizeof(ENetHeader);
Entry->ate_state = ARP_GOOD;
Entry->ate_valid = Now; // Mark last time he was
// valid.
Entry->ate_useticks = ArpCacheLife;
break;
case NdisMedium802_5:
// This is TR.
// For token ring we have to deal with source routing. There's
// a special case to handle multiple responses for an all-routes
// request - if the entry is currently good and we knew it was
// valid recently, we won't update the entry.
if (Entry->ate_state != ARP_GOOD ||
(Now - Entry->ate_valid) > ARP_RESOLVE_TIMEOUT) {
TRHdr = (TRHeader *) Entry->ate_addr;
// We need to update a TR entry.
TRHdr->tr_ac = ARP_AC;
TRHdr->tr_fc = ARP_FC;
RtlCopyMemory(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH);
RtlCopyMemory(TRHdr->tr_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
if (SourceRoute != (RC UNALIGNED *) NULL) {
uchar MaxIFieldBits;
// We have source routing information.
RtlCopyMemory(&Entry->ate_addr[sizeof(TRHeader)],
SourceRoute, SourceRouteSize);
MaxIFieldBits = (SourceRoute->rc_dlf & RC_LF_MASK) >>
LF_BIT_SHIFT;
MaxIFieldBits = MIN(MaxIFieldBits, MAX_LF_BITS);
MaxMTU = IFieldSize[MaxIFieldBits];
// The new MTU we've computed is the max I-field size,
// which doesn't include source routing info but
// does include SNAP info. Subtract off the SNAP size.
MaxMTU -= sizeof(SNAPHeader);
TRHdr->tr_saddr[0] |= TR_RII;
(*(RC UNALIGNED *) & Entry->ate_addr[sizeof(TRHeader)]).rc_dlf ^=
RC_DIR;
// Make sure it's non-broadcast.
(*(RC UNALIGNED *) & Entry->ate_addr[sizeof(TRHeader)]).rc_blen &=
RC_LENMASK;
}
RtlCopyMemory(&Entry->ate_addr[sizeof(TRHeader) + SourceRouteSize],
ARPSNAP, sizeof(SNAPHeader));
Entry->ate_state = ARP_GOOD;
Entry->ate_valid = Now;
Entry->ate_useticks = ArpCacheLife;
Entry->ate_addrlength = (uchar) (sizeof(TRHeader) +
SourceRouteSize + sizeof(SNAPHeader));
*(ushort *) & Entry->ate_addr[Entry->ate_addrlength - 2] =
net_short(ARP_ETYPE_IP);
}
break;
case NdisMediumFddi:
FHdr = (FDDIHeader *) Entry->ate_addr;
FHdr->fh_pri = ARP_FDDI_PRI;
RtlCopyMemory(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
RtlCopyMemory(FHdr->fh_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
RtlCopyMemory(&Entry->ate_addr[sizeof(FDDIHeader)], ARPSNAP,
sizeof(SNAPHeader));
Entry->ate_addrlength = (uchar) (sizeof(FDDIHeader) +
sizeof(SNAPHeader));
*(ushort UNALIGNED *) & Entry->ate_addr[Entry->ate_addrlength - 2] =
net_short(ARP_ETYPE_IP);
Entry->ate_state = ARP_GOOD;
Entry->ate_valid = Now; // Mark last time he was
// valid.
Entry->ate_useticks = ArpCacheLife;
break;
case NdisMediumArcnet878_2:
AHdr = (ARCNetHeader *) Entry->ate_addr;
AHdr->ah_saddr = Interface->ai_addr[0];
AHdr->ah_daddr = *SHAddr;
AHdr->ah_prot = ARP_ARCPROT_IP;
Entry->ate_addrlength = sizeof(ARCNetHeader);
Entry->ate_state = ARP_GOOD;
Entry->ate_valid = Now; // Mark last time he was
// valid.
break;
default:
ASSERT(0);
break;
}
}
if (Entry->ate_resolveonly) {
CTELockHandle EntryHandle;
uint Index = ARP_HASH(*SPAddr);
ARPTableEntry *PrevATE, *CurrentATE;
ARPTable *Table;
ARPControlBlock *ArpContB, *TmpArpContB;
ArpContB = Entry->ate_resolveonly;
ASSERT(Entry->ate_resolveonly != NULL);
while (ArpContB) {
ArpRtn rtn;
rtn = (ArpRtn) ArpContB->CompletionRtn;
ArpContB->status = FillARPControlBlock(Interface, Entry,
ArpContB);
TmpArpContB = ArpContB->next;
(*rtn) (ArpContB, STATUS_SUCCESS);
ArpContB = TmpArpContB;
}
Entry->ate_resolveonly = NULL;
if (Entry->ate_userarp) {
PNDIS_PACKET OldPacket = NULL;
OldPacket = Entry->ate_packet;
CTEFreeLock(&Entry->ate_lock, LHandle);
CTEFreeMem(Entry);
if (OldPacket) {
IPSendComplete(Interface->ai_context, OldPacket,
NDIS_STATUS_SUCCESS);
}
} else {
CTEFreeLock(&Entry->ate_lock, LHandle);
}
return NDIS_STATUS_SUCCESS;
}
// At this point we've updated the entry, and we still hold the lock
// on it. If we have a packet that was pending to be sent, send it now.
// Otherwise just free the lock.
Packet = Entry->ate_packet;
if (Packet != NULL) {
// We have a packet to send.
ASSERT(Entry->ate_state == ARP_GOOD);
Entry->ate_packet = NULL;
DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX,
(DTEXT("ARPHandlePacket: Sending packet %x after resolving ATE %x\n"),
Packet, Entry));
if (ARPSendData(Interface, Packet, Entry, LHandle) != NDIS_STATUS_PENDING) {
IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
}
} else {
CTEFreeLock(&Entry->ate_lock, LHandle);
}
}
// See if the MTU is less than our local one. This should only happen
// in the case of token ring source routing.
if (MaxMTU < Interface->ai_mtu) {
LLIPAddrMTUChange LAM;
LAM.lam_mtu = MaxMTU;
LAM.lam_addr = *SPAddr;
// It is less. Notify IP.
ASSERT(Interface->ai_media == NdisMedium802_5);
IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE,
&LAM, sizeof(LLIPAddrMTUChange), NULL);
}
// At this point we've updated the entry (if we had one), and we've freed
// all locks. If it's for a local address and it's a request, reply to
// it.
if (LocalAddr) { // It's for us.
if (ARPHdr->ah_opcode == net_short(ARP_REQUEST)) {
// It's a request, and we need to respond.
SendARPReply(Interface, *SPAddr, *DPAddr,
SHAddr, SourceRoute, SourceRouteSize, UseSNAP);
}
}
return NDIS_STATUS_SUCCESS;
}
//* InitAdapter - Initialize an adapter.
//
// Called when an adapter is open to finish initialization. We set
// up our lookahead size and packet filter, and we're ready to go.
//
// Entry:
// adapter - Pointer to an adapter structure for the adapter to be
// initialized.
//
// Exit: Nothing
//
void
InitAdapter(ARPInterface * Adapter)
{
NDIS_STATUS Status;
CTELockHandle Handle;
ARPIPAddr *Addr, *OldAddr;
if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
OID_GEN_CURRENT_LOOKAHEAD, &ARPLookahead, sizeof(ARPLookahead),
NULL, TRUE)) != NDIS_STATUS_SUCCESS) {
Adapter->ai_state = INTERFACE_DOWN;
return;
}
if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint),
NULL, TRUE)) == NDIS_STATUS_SUCCESS) {
uint MediaStatus;
Adapter->ai_adminstate = IF_STATUS_UP;
Adapter->ai_operstate = IF_OPER_STATUS_OPERATIONAL;
Adapter->ai_lastchange = GetTimeTicks();
if ((Status = DoNDISRequest(Adapter, NdisRequestQueryInformation,
OID_GEN_MEDIA_CONNECT_STATUS, &MediaStatus, sizeof(MediaStatus),
NULL, TRUE)) == NDIS_STATUS_SUCCESS) {
if (MediaStatus == NdisMediaStateDisconnected) {
Adapter->ai_operstate = IF_OPER_STATUS_NON_OPERATIONAL;
Adapter->ai_lastchange = GetTimeTicks();
}
}
Adapter->ai_state = INTERFACE_UP;
// Now walk through any addresses we have and ARP for them , only when ArpRetryCount != 0.
if (ArpRetryCount) {
CTEGetLock(&Adapter->ai_lock, &Handle);
OldAddr = NULL;
Addr = &Adapter->ai_ipaddr;
do {
if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
IPAddr Address = Addr->aia_addr;
Addr->aia_age = ArpRetryCount;
CTEFreeLock(&Adapter->ai_lock, Handle);
OldAddr = Addr;
SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL,
NULL, TRUE);
CTEGetLock(&Adapter->ai_lock, &Handle);
}
Addr = &Adapter->ai_ipaddr;
while (Addr != OldAddr && Addr != NULL) {
Addr = Addr->aia_next;
}
if (Addr != NULL) {
Addr = Addr->aia_next;
}
} while (Addr != NULL);
CTEFreeLock(&Adapter->ai_lock, Handle);
}
} else {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_ERROR_LEVEL,
"**InitAdapter setting FAILED\n"));
Adapter->ai_state = INTERFACE_DOWN;
}
}
//** ARPOAComplete - ARP Open adapter complete handler.
//
// This routine is called by the NDIS driver when an open adapter
// call completes. Presumably somebody is blocked waiting for this, so
// we'll wake him up now.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Status - Final status of command.
// ErrorStatus - Final error status.
//
// Exit: Nothing.
//
void NDIS_API
ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus)
{
ARPInterface *ai = (ARPInterface *) Handle; // For compiler.
CTESignal(&ai->ai_block, (uint) Status); // Wake him up, and return status.
}
//** ARPCAComplete - ARP close adapter complete handler.
//
// This routine is called by the NDIS driver when a close adapter
// call completes.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Status - Final status of command.
//
// Exit: Nothing.
//
void NDIS_API
ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
{
ARPInterface *ai = (ARPInterface *) Handle; // For compiler.
CTESignal(&ai->ai_block, (uint) Status); // Wake him up, and return status.
}
//** ARPSendComplete - ARP send complete handler.
//
// This routine is called by the NDIS driver when a send completes.
// This is a pretty time critical operation, we need to get through here
// quickly. We'll strip our buffer off and put it back, and call the upper
// later send complete handler.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Packet - A pointer to the packet that was sent.
// Status - Final status of command.
//
// Exit: Nothing.
//
void NDIS_API
ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status)
{
ARPInterface *Interface = (ARPInterface *) Handle;
PacketContext *PC = (PacketContext *) Packet->ProtocolReserved;
PNDIS_BUFFER Buffer;
uint DataLength;
Interface->ai_qlen--;
if (Status == NDIS_STATUS_SUCCESS) {
DataLength = Packet->Private.TotalLength;
if (!(Packet->Private.ValidCounts)) {
NdisQueryPacket(Packet, NULL, NULL, NULL,&DataLength);
}
Interface->ai_outoctets += DataLength;
} else {
if (Status == NDIS_STATUS_RESOURCES)
Interface->ai_outdiscards++;
else
Interface->ai_outerrors++;
}
#if BACK_FILL
// Get first buffer on packet.
if (Interface->ai_media == NdisMedium802_3) {
PMDL TmpMdl = NULL;
uint HdrSize;
NdisQueryPacket(Packet, NULL, NULL, &TmpMdl, NULL);
if (TmpMdl->MdlFlags & MDL_NETWORK_HEADER) {
HdrSize = sizeof(ENetHeader);
if (((PacketContext*)
Packet->ProtocolReserved)->pc_common.pc_flags &
PACKET_FLAG_SNAP)
HdrSize += Interface->ai_snapsize;
(ULONG_PTR) TmpMdl->MappedSystemVa += HdrSize;
TmpMdl->ByteOffset += HdrSize;
TmpMdl->ByteCount -= HdrSize;
} else {
NdisUnchainBufferAtFront(Packet, &Buffer);
FreeARPBuffer(Interface, Buffer); // Free it up.
}
} else {
NdisUnchainBufferAtFront(Packet, &Buffer);
FreeARPBuffer(Interface, Buffer); // Free it up.
}
#else
// Get first buffer on packet.
NdisUnchainBufferAtFront(Packet, &Buffer);
ASSERT(Buffer);
FreeARPBuffer(Interface, Buffer); // Free it up.
#endif
if (PC->pc_common.pc_owner != PACKET_OWNER_LINK) { // We don't own this one.
IPSendComplete(Interface->ai_context, Packet, Status);
return;
}
// This packet belongs to us, so free it.
NdisFreePacket(Packet);
}
//** ARPTDComplete - ARP transfer data complete handler.
//
// This routine is called by the NDIS driver when a transfer data
// call completes. Since we never transfer data ourselves, this must be
// from the upper layer. We'll just call his routine and let him deal
// with it.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Packet - A pointer to the packet used for the TD.
// Status - Final status of command.
// BytesCopied - Count of bytes copied.
//
// Exit: Nothing.
//
void NDIS_API
ARPTDComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status,
uint BytesCopied)
{
ARPInterface *ai = (ARPInterface *) Handle;
IPTDComplete(ai->ai_context, Packet, Status, BytesCopied);
}
//** ARPResetComplete - ARP reset complete handler.
//
// This routine is called by the NDIS driver when a reset completes.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Status - Final status of command.
//
// Exit: Nothing.
//
void NDIS_API
ARPResetComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
{
ARPInterface *ai = (ARPInterface *) Handle;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ArpResetComplete on %x\n", ai->ai_context));
IPReset(ai->ai_context);
}
//** ARPRequestComplete - ARP request complete handler.
//
// This routine is called by the NDIS driver when a general request
// completes. If ARP blocks on a request, we'll just give a wake up
// to whoever's blocked on this request. Else if it is a non-blocking
// request, we extract the request complete callback fn in the request
// call it, and then deallocate the request block (that is on the heap)
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Request - A pointer to the request that completed.
// Status - Final status of command.
//
// Exit: Nothing.
//
void NDIS_API
ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST pRequest,
NDIS_STATUS Status)
{
ARPInterface *ai = (ARPInterface *) Handle;
RequestBlock *rb = STRUCT_OF(RequestBlock, pRequest, Request);
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST,
(DTEXT("+ARPRequestComplete(%x, %x, %x) RequestBlock %x\n"),
Handle, pRequest, Status, rb));
if (rb->Blocking) {
// Request through BLOCKING DoNDISRequest
// Signal the blocked guy here
CTESignal(&rb->Block, (uint) Status);
if (InterlockedDecrement(&rb->RefCount) == 0) {
CTEFreeMem(rb);
}
} else {
ReqInfoBlock *rib;
RCCALL reqcallback;
// Request through NON-BLOCKING DoNDISRequest
// Extract the callback fn pointer & params
if (pRequest->RequestType == NdisRequestSetInformation)
rib = STRUCT_OF(ReqInfoBlock,
pRequest->DATA.SET_INFORMATION.InformationBuffer,
RequestInfo);
else
rib = STRUCT_OF(ReqInfoBlock,
pRequest->DATA.QUERY_INFORMATION.InformationBuffer,
RequestInfo);
reqcallback = rib->ReqCompleteCallback;
if (reqcallback)
reqcallback(rib);
// Free ARP memory associated with request
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPRequestComplete: Freeing mem at pRequest = %08X\n", rb));
CTEFreeMem(rb);
}
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST,
(DTEXT("-ARPRequestComplete [%x]\n"), Status));
}
//** ARPRcv - ARP receive data handler.
//
// This routine is called when data arrives from the NDIS driver.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Context - NDIS context to be used for TD.
// Header - Pointer to header
// HeaderSize - Size of header
// Data - Pointer to buffer of received data
// Size - Byte count of data in buffer.
// TotalSize - Byte count of total packet size.
//
// Exit: Status indicating whether or not we took the packet.
//
NDIS_STATUS NDIS_API
ARPRcv(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize,
void *Data, uint Size, uint TotalSize)
{
ARPInterface *Interface = Handle;
NDIS_STATUS status;
PINT OrigPacket = NULL;
//get the original packet (if any)
//this is required to make task offload work
//note: We shall hack the pClientCount Field
//to point to the packet as a short term solution
//to avoid changing all atm - ip interface changes
if (Interface->ai_OffloadFlags) {
OrigPacket = (PINT) NdisGetReceivedPacket(Interface->ai_handle, Context);
}
//Call the new interface with null mdl and context pointers
status = ARPRcvIndicationNew(Handle, Context, Header, HeaderSize,
Data, Size, TotalSize, NULL, OrigPacket);
return status;
}
//** ARPRcvPacket - ARP receive data handler.
//
// This routine is called when data arrives from the NDIS driver.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Packet - Contains the incoming frame
//
// Returns number of upper layer folks latching on to this frame
//
//
INT
ARPRcvPacket(NDIS_HANDLE Handle, PNDIS_PACKET Packet)
{
UINT HeaderBufferSize = NDIS_GET_PACKET_HEADER_SIZE(Packet);
UINT firstbufferLength, bufferLength, LookAheadBufferSize;
PNDIS_BUFFER pFirstBuffer;
PUCHAR headerBuffer;
NTSTATUS ntStatus;
INT ClientCnt = 0;
//
// Query the number of buffers, the first MDL's descriptor and the packet length
//
NdisGetFirstBufferFromPacket(Packet, // packet
&pFirstBuffer, // first buffer descriptor
&headerBuffer, // ptr to the start of packet
&firstbufferLength, // length of the header+lookahead
&bufferLength); // length of the bytes in the buffers
//
// ReceiveContext is the packet itself
//
LookAheadBufferSize = firstbufferLength - HeaderBufferSize;
ntStatus = ARPRcvIndicationNew(Handle, Packet, headerBuffer,
HeaderBufferSize,
headerBuffer + HeaderBufferSize, // LookaheadBuffer
LookAheadBufferSize, // LookaheadBufferSize
bufferLength - HeaderBufferSize, // PacketSize - since
// the whole packet is
// indicated
pFirstBuffer, // pMdl
&ClientCnt // tdi client count
);
return ClientCnt;
}
//** ARPRcvIndicationNew - ARP receive data handler.
//
// This routine is called when data arrives from the NDIS driver.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// Context - NDIS context to be used for TD.
// Header - Pointer to header
// HeaderSize - Size of header
// Data - Pointer to buffer of received data
// Size - Byte count of data in buffer.
// TotalSize - Byte count of total packet size.
// pMdl - NDIS_BUFFER of incoming frame
// pClientCnt address to return the clinet counts
//
// Exit: Status indicating whether or not we took the packet.
//
NDIS_STATUS NDIS_API
ARPRcvIndicationNew(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header,
uint HeaderSize, void *Data, uint Size, uint TotalSize,
PNDIS_BUFFER pNdisBuffer, PINT pClientCnt)
{
ARPInterface *Interface = Handle; // Interface for this driver.
ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *) Header;
SNAPHeader UNALIGNED *SNAPHdr;
ushort type; // Protocol type
uint ProtOffset; // Offset in Data to non-media info.
uint NUCast; // TRUE if the frame is not a unicast frame.
if (Interface->ai_state == INTERFACE_UP &&
HeaderSize >= (uint) Interface->ai_hdrsize) {
Interface->ai_inoctets += TotalSize;
NUCast = ((*((uchar UNALIGNED *) EHdr + Interface->ai_bcastoff) &
Interface->ai_bcastmask) == Interface->ai_bcastval) ?
AI_NONUCAST_INDEX : AI_UCAST_INDEX;
if ((Interface->ai_promiscuous) && (!NUCast)) { // AI_UCAST_INDEX = 0
switch (Interface->ai_media) {
case NdisMedium802_3:{
// Enet
if (Interface->ai_addrlen != ARP_802_ADDR_LENGTH ||
CTEMemCmp(EHdr->eh_daddr, Interface->ai_addr, ARP_802_ADDR_LENGTH) != 0) {
NUCast = AI_PROMIS_INDEX;
}
break;
}
case NdisMedium802_5:{
// token ring
TRHeader UNALIGNED *THdr = (TRHeader UNALIGNED *) Header;
if (Interface->ai_addrlen != ARP_802_ADDR_LENGTH ||
CTEMemCmp(THdr->tr_daddr, Interface->ai_addr, ARP_802_ADDR_LENGTH) != 0) {
NUCast = AI_PROMIS_INDEX;
}
break;
}
case NdisMediumFddi:{
// FDDI
FDDIHeader UNALIGNED *FHdr = (FDDIHeader UNALIGNED *) Header;
if (Interface->ai_addrlen != ARP_802_ADDR_LENGTH ||
CTEMemCmp(FHdr->fh_daddr, Interface->ai_addr, ARP_802_ADDR_LENGTH) != 0) {
NUCast = AI_PROMIS_INDEX;
}
break;
}
case NdisMediumArcnet878_2:{
// ArcNet
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_RX,
(DTEXT("-ARPRcvIndicationNew [NOT_RECOGNIZED]\n")));
return NDIS_STATUS_NOT_RECOGNIZED;
break;
}
default:
ASSERT(0);
Interface->ai_outerrors++;
DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_RX,
(DTEXT("-ARPRcvIndicationNew [UNSUPPORTED_MEDIA]\n")));
return NDIS_STATUS_UNSUPPORTED_MEDIA;
}
}
if ((Interface->ai_media == NdisMedium802_3) &&
(type = net_short(EHdr->eh_type)) >= MIN_ETYPE) {
ProtOffset = 0;
} else if (Interface->ai_media != NdisMediumArcnet878_2) {
SNAPHdr = (SNAPHeader UNALIGNED *) Data;
if (Size >= sizeof(SNAPHeader) &&
SNAPHdr->sh_dsap == SNAP_SAP &&
SNAPHdr->sh_ssap == SNAP_SAP &&
SNAPHdr->sh_ctl == SNAP_UI) {
type = net_short(SNAPHdr->sh_etype);
ProtOffset = sizeof(SNAPHeader);
} else {
//handle XID/TEST here.
Interface->ai_uknprotos++;
return NDIS_STATUS_NOT_RECOGNIZED;
}
} else {
ARCNetHeader UNALIGNED *AH = (ARCNetHeader UNALIGNED *) Header;
ProtOffset = 0;
if (AH->ah_prot == ARP_ARCPROT_IP)
type = ARP_ETYPE_IP;
else if (AH->ah_prot == ARP_ARCPROT_ARP)
type = ARP_ETYPE_ARP;
else
type = 0;
}
if (type == ARP_ETYPE_IP) {
(Interface->ai_inpcount[NUCast])++;
ASSERT (KeGetCurrentIrql() <= DISPATCH_LEVEL);
IPRcvPacket(Interface->ai_context, (uchar *) Data + ProtOffset,
Size - ProtOffset, TotalSize - ProtOffset, Context, ProtOffset,
NUCast, HeaderSize, pNdisBuffer, pClientCnt, NULL);
return NDIS_STATUS_SUCCESS;
} else {
if (type == ARP_ETYPE_ARP) {
(Interface->ai_inpcount[NUCast])++;
return HandleARPPacket(Interface, Header, HeaderSize,
(ARPHeader *) ((uchar *) Data + ProtOffset), Size - ProtOffset,
ProtOffset);
} else {
Interface->ai_uknprotos++;
return NDIS_STATUS_NOT_RECOGNIZED;
}
}
} else {
// Interface is marked as down.
return NDIS_STATUS_NOT_RECOGNIZED;
}
}
//** ARPRcvComplete - ARP receive complete handler.
//
// This routine is called by the NDIS driver after some number of
// receives. In some sense, it indicates 'idle time'.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
//
// Exit: Nothing.
//
void NDIS_API
ARPRcvComplete(NDIS_HANDLE Handle)
{
IPRcvComplete();
}
//** ARPStatus - ARP status handler.
//
// Called by the NDIS driver when some sort of status change occurs.
// We take action depending on the type of status.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// GStatus - General type of status that caused the call.
// Status - Pointer to a buffer of status specific information.
// StatusSize - Size of the status buffer.
//
// Exit: Nothing.
//
void NDIS_API
ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint
StatusSize)
{
ARPInterface *ai = (ARPInterface *) Handle;
//
// ndis calls this sometimes even before ip interface is created.
//
if ((ai->ai_context) && (ai->ai_state == INTERFACE_UP)) {
IPStatus(ai->ai_context, GStatus, Status, StatusSize, NULL);
switch (GStatus) {
//reflect media connect/disconnect status in
//operstate for query purpose
case NDIS_STATUS_MEDIA_CONNECT:
ai->ai_operstate = IF_OPER_STATUS_OPERATIONAL;
ai->ai_lastchange = GetTimeTicks();
break;
case NDIS_STATUS_MEDIA_DISCONNECT:
ai->ai_operstate = IF_OPER_STATUS_NON_OPERATIONAL;
ai->ai_lastchange = GetTimeTicks();
break;
default:
break;
}
}
}
//** ARPStatusComplete - ARP status complete handler.
//
// A routine called by the NDIS driver so that we can do postprocessing
// after a status event.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
//
// Exit: Nothing.
//
void NDIS_API
ARPStatusComplete(NDIS_HANDLE Handle)
{
}
//** ARPPnPEvent - ARP PnPEvent handler.
//
// Called by the NDIS driver when PnP or PM events occurs.
//
// Entry:
// Handle - The binding handle we specified (really a pointer to an AI).
// NetPnPEvent - This is a pointer to a NET_PNP_EVENT that describes
// the PnP indication.
//
// Exit:
// Just call into IP and return status.
//
NDIS_STATUS
ARPPnPEvent(NDIS_HANDLE Handle, PNET_PNP_EVENT NetPnPEvent)
{
ARPInterface *ai = (ARPInterface *) Handle;
//
// ndis can calls this sometimes even before ip interface is created.
//
if (ai && !ai->ai_context) {
return STATUS_SUCCESS;
} else {
return IPPnPEvent(ai ? ai->ai_context : NULL, NetPnPEvent);
}
}
//** ARPSetNdisRequest - ARP Ndisrequest handler.
//
// Called by the upper driver to set the packet filter for the interface.
//
// Entry:
// Context - Context value we gave to IP (really a pointer to an AI).
// OID - Object ID to set/unset
// On - Set_if, clear_if or clear_card
//
// Exit:
// returns status.
//
NDIS_STATUS
__stdcall
ARPSetNdisRequest(void *Context, NDIS_OID OID, uint On)
{
int Status;
ARPInterface *Interface = (ARPInterface *) Context;
if (On == SET_IF) {
Interface->ai_pfilter |= OID;
if (OID == NDIS_PACKET_TYPE_PROMISCUOUS) {
Interface->ai_promiscuous = 1;
}
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter,
sizeof(uint), NULL, TRUE);
} else { // turn off
Interface->ai_pfilter &= ~(OID);
if (OID == NDIS_PACKET_TYPE_PROMISCUOUS) {
Interface->ai_promiscuous = 0;
}
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter,
sizeof(uint), NULL, TRUE);
}
return Status;
}
//** ARPPnPComplete - ARP PnP complete handler.
//
// Called by the upper driver to do the post processing of pnp event.
//
// Entry:
// Context - Context value we gave to IP (really a pointer to an AI).
// Status - Status code of the pnp operation.
// NetPnPEvent - This is a pointer to a NET_PNP_EVENT that describes
// the PnP indication.
//
// Exit:
// returns nothing.
//
void
__stdcall
ARPPnPComplete(void *Context, NDIS_STATUS Status, PNET_PNP_EVENT NetPnPEvent)
{
ARPInterface *Interface = (ARPInterface *) Context;
NdisCompletePnPEvent(Status, (Interface ? Interface->ai_handle : NULL), NetPnPEvent);
}
extern void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus,
NDIS_HANDLE BindContext,
PNDIS_STRING AdapterName,
PVOID SS1, PVOID SS2);
extern void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus,
NDIS_HANDLE ProtBindContext,
NDIS_HANDLE UnbindContext);
extern void NDIS_API ARPUnloadProtocol(void);
extern void ArpUnload(PDRIVER_OBJECT);
//* ARPReadNext - Read the next entry in the ARP table.
//
// Called by the GetInfo code to read the next ATE in the table. We assume
// the context passed in is valid, and the caller has the ARP TableLock.
//
// Input: Context - Pointer to a IPNMEContext.
// Interface - Pointer to interface for table to read on.
// Buffer - Pointer to an IPNetToMediaEntry structure.
//
// Returns: TRUE if more data is available to be read, FALSE is not.
//
uint
ARPReadNext(void *Context, ARPInterface * Interface, void *Buffer)
{
IPNMEContext *NMContext = (IPNMEContext *) Context;
IPNetToMediaEntry *IPNMEntry = (IPNetToMediaEntry *) Buffer;
CTELockHandle Handle;
ARPTableEntry *CurrentATE;
uint i;
ARPTable *Table = Interface->ai_ARPTbl;
uint AddrOffset;
CurrentATE = NMContext->inc_entry;
// Fill in the buffer.
CTEGetLock(&CurrentATE->ate_lock, &Handle);
IPNMEntry->inme_index = Interface->ai_index;
IPNMEntry->inme_physaddrlen = Interface->ai_addrlen;
switch (Interface->ai_media) {
case NdisMedium802_3:
AddrOffset = 0;
break;
case NdisMedium802_5:
AddrOffset = offsetof(struct TRHeader, tr_daddr);
break;
case NdisMediumFddi:
AddrOffset = offsetof(struct FDDIHeader, fh_daddr);
break;
case NdisMediumArcnet878_2:
AddrOffset = offsetof(struct ARCNetHeader, ah_daddr);
break;
default:
AddrOffset = 0;
break;
}
RtlCopyMemory(IPNMEntry->inme_physaddr, &CurrentATE->ate_addr[AddrOffset],
Interface->ai_addrlen);
IPNMEntry->inme_addr = CurrentATE->ate_dest;
if (CurrentATE->ate_state == ARP_GOOD)
IPNMEntry->inme_type = (CurrentATE->ate_valid == ALWAYS_VALID ?
INME_TYPE_STATIC : INME_TYPE_DYNAMIC);
else
IPNMEntry->inme_type = INME_TYPE_INVALID;
CTEFreeLock(&CurrentATE->ate_lock, Handle);
// We've filled it in. Now update the context.
if (CurrentATE->ate_next != NULL) {
NMContext->inc_entry = CurrentATE->ate_next;
return TRUE;
} else {
// The next ATE is NULL. Loop through the ARP Table looking for a new
// one.
i = NMContext->inc_index + 1;
while (i < ARP_TABLE_SIZE) {
if ((*Table)[i] != NULL) {
NMContext->inc_entry = (*Table)[i];
NMContext->inc_index = i;
return TRUE;
break;
} else
i++;
}
NMContext->inc_index = 0;
NMContext->inc_entry = NULL;
return FALSE;
}
}
//* ARPValidateContext - Validate the context for reading an ARP table.
//
// Called to start reading an ARP table sequentially. We take in
// a context, and if the values are 0 we return information about the
// first route in the table. Otherwise we make sure that the context value
// is valid, and if it is we return TRUE.
// We assume the caller holds the ARPInterface lock.
//
// Input: Context - Pointer to a RouteEntryContext.
// Interface - Pointer to an interface
// Valid - Where to return information about context being
// valid.
//
// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set
// to TRUE if input context is valid
//
uint
ARPValidateContext(void *Context, ARPInterface * Interface, uint * Valid)
{
IPNMEContext *NMContext = (IPNMEContext *) Context;
uint i;
ARPTableEntry *TargetATE;
ARPTableEntry *CurrentATE;
ARPTable *Table = Interface->ai_ARPTbl;
i = NMContext->inc_index;
TargetATE = NMContext->inc_entry;
// If the context values are 0 and NULL, we're starting from the beginning.
if (i == 0 && TargetATE == NULL) {
*Valid = TRUE;
do {
if ((CurrentATE = (*Table)[i]) != NULL) {
break;
}
i++;
} while (i < ARP_TABLE_SIZE);
if (CurrentATE != NULL) {
NMContext->inc_index = i;
NMContext->inc_entry = CurrentATE;
return TRUE;
} else
return FALSE;
} else {
// We've been given a context. We just need to make sure that it's
// valid.
if (i < ARP_TABLE_SIZE) {
CurrentATE = (*Table)[i];
while (CurrentATE != NULL) {
if (CurrentATE == TargetATE) {
*Valid = TRUE;
return TRUE;
break;
} else {
CurrentATE = CurrentATE->ate_next;
}
}
}
// If we get here, we didn't find the matching ATE.
*Valid = FALSE;
return FALSE;
}
}
#define IFE_FIXED_SIZE offsetof(struct IFEntry, if_descr)
//* ARPQueryInfo - ARP query information handler.
//
// Called to query information about the ARP table or statistics about the
// actual interface.
//
// Input: IFContext - Interface context (pointer to an ARPInterface).
// ID - TDIObjectID for object.
// Buffer - Buffer to put data into.
// Size - Pointer to size of buffer. On return, filled with
// bytes copied.
// Context - Pointer to context block.
//
// Returns: Status of attempt to query information.
//
int
__stdcall
ARPQueryInfo(void *IFContext, TDIObjectID * ID, PNDIS_BUFFER Buffer, uint * Size,
void *Context)
{
ARPInterface *AI = (ARPInterface *) IFContext;
uint Offset = 0;
uint BufferSize = *Size;
CTELockHandle Handle;
uint ContextValid, DataLeft;
uint BytesCopied = 0;
uchar InfoBuff[sizeof(IFEntry)];
uint Entity;
uint Instance;
BOOLEAN fStatus;
DEBUGMSG(DBG_TRACE && DBG_QUERYINFO,
(DTEXT("+ARPQueryInfo(%x, %x, %x, %x, %x)\n"),
IFContext, ID, Buffer, Size, Context));
Entity = ID->toi_entity.tei_entity;
Instance = ID->toi_entity.tei_instance;
// TCPTRACE(("ARPQueryInfo: AI %lx, Instance %lx, ai_atinst %lx, ai_ifinst %lx\n",
// AI, Instance, AI->ai_atinst, AI->ai_ifinst ));
// First, make sure it's possibly an ID we can handle.
if ((Entity != AT_ENTITY || Instance != AI->ai_atinst) &&
(Entity != IF_ENTITY || Instance != AI->ai_ifinst)) {
return TDI_INVALID_REQUEST;
}
*Size = 0; // In case of an error.
if (ID->toi_type != INFO_TYPE_PROVIDER)
return TDI_INVALID_PARAMETER;
if (ID->toi_class == INFO_CLASS_GENERIC) {
if (ID->toi_id == ENTITY_TYPE_ID) {
// He's trying to see what type we are.
if (BufferSize >= sizeof(uint)) {
*(uint *) & InfoBuff[0] = (Entity == AT_ENTITY) ? AT_ARP :
IF_MIB;
fStatus = CopyToNdisSafe(Buffer, NULL, InfoBuff, sizeof(uint), &Offset);
if (fStatus == FALSE) {
return TDI_NO_RESOURCES;
}
*Size = sizeof(uint);
return TDI_SUCCESS;
} else
return TDI_BUFFER_TOO_SMALL;
}
return TDI_INVALID_PARAMETER;
}
// Might be able to handle this.
if (Entity == AT_ENTITY) {
// It's an address translation object. It could be a MIB object or
// an implementation specific object (the generic objects were handled
// above).
if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
ARPPArpAddr *PArpAddr;
// It's an implementation specific ID. The only ones we handle
// are the PARP_COUNT_ID and the PARP_ENTRY ID.
if (ID->toi_id == AT_ARP_PARP_COUNT_ID) {
// He wants to know the count. Just return that to him.
if (BufferSize >= sizeof(uint)) {
CTEGetLock(&AI->ai_lock, &Handle);
fStatus = CopyToNdisSafe(Buffer, NULL, (uchar *) & AI->ai_parpcount,
sizeof(uint), &Offset);
CTEFreeLock(&AI->ai_lock, Handle);
if (fStatus == FALSE) {
return TDI_NO_RESOURCES;
}
*Size = sizeof(uint);
return TDI_SUCCESS;
} else
return TDI_BUFFER_TOO_SMALL;
}
if (ID->toi_id != AT_ARP_PARP_ENTRY_ID)
return TDI_INVALID_PARAMETER;
// It's for Proxy ARP entries. The context should be either NULL
// or a pointer to the next one to be read.
CTEGetLock(&AI->ai_lock, &Handle);
PArpAddr = *(ARPPArpAddr **) Context;
if (PArpAddr != NULL) {
ARPPArpAddr *CurrentPARP;
// Loop through the P-ARP addresses on the interface, and
// see if we can find this one.
CurrentPARP = AI->ai_parpaddr;
while (CurrentPARP != NULL) {
if (CurrentPARP == PArpAddr)
break;
else
CurrentPARP = CurrentPARP->apa_next;
}
// If we found a match, PARPAddr points to where to begin
// reading. Otherwise, fail the request.
if (CurrentPARP == NULL) {
// Didn't find a match, so fail the request.
CTEFreeLock(&AI->ai_lock, Handle);
return TDI_INVALID_PARAMETER;
}
} else
PArpAddr = AI->ai_parpaddr;
// PARPAddr points to the next entry to put in the buffer, if
// there is one.
while (PArpAddr != NULL) {
if ((int)(BufferSize - BytesCopied) >=
(int)sizeof(ProxyArpEntry)) {
ProxyArpEntry *TempPArp;
TempPArp = (ProxyArpEntry *) InfoBuff;
TempPArp->pae_status = PAE_STATUS_VALID;
TempPArp->pae_addr = PArpAddr->apa_addr;
TempPArp->pae_mask = PArpAddr->apa_mask;
BytesCopied += sizeof(ProxyArpEntry);
fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *) TempPArp,
sizeof(ProxyArpEntry), &Offset);
if (fStatus == FALSE) {
CTEFreeLock(&AI->ai_lock, Handle);
return TDI_NO_RESOURCES;
}
PArpAddr = PArpAddr->apa_next;
} else
break;
}
// We're done copying. Free the lock and return the correct
// status.
CTEFreeLock(&AI->ai_lock, Handle);
*Size = BytesCopied;
**(ARPPArpAddr ***) & Context = PArpAddr;
return(PArpAddr == NULL) ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW;
}
if (ID->toi_id == AT_MIB_ADDRXLAT_INFO_ID) {
AddrXlatInfo *AXI;
// It's for the count. Just return the number of entries in the
// table.
if (BufferSize >= sizeof(AddrXlatInfo)) {
*Size = sizeof(AddrXlatInfo);
AXI = (AddrXlatInfo *) InfoBuff;
AXI->axi_count = AI->ai_count;
AXI->axi_index = AI->ai_index;
fStatus = CopyToNdisSafe(Buffer, NULL, (uchar *) AXI, sizeof(AddrXlatInfo),
&Offset);
if (fStatus == FALSE) {
return TDI_NO_RESOURCES;
}
*Size = sizeof(AddrXlatInfo);
return TDI_SUCCESS;
} else
return TDI_BUFFER_TOO_SMALL;
}
if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID) {
// He's trying to read the table.
// Make sure we have a valid context.
CTEGetLock(&AI->ai_ARPTblLock, &Handle);
DataLeft = ARPValidateContext(Context, AI, &ContextValid);
// If the context is valid, we'll continue trying to read.
if (!ContextValid) {
CTEFreeLock(&AI->ai_ARPTblLock, Handle);
return TDI_INVALID_PARAMETER;
}
while (DataLeft) {
// The invariant here is that there is data in the table to
// read. We may or may not have room for it. So DataLeft
// is TRUE, and BufferSize - BytesCopied is the room left
// in the buffer.
if ((int)(BufferSize - BytesCopied) >=
(int)sizeof(IPNetToMediaEntry)) {
DataLeft = ARPReadNext(Context, AI, InfoBuff);
BytesCopied += sizeof(IPNetToMediaEntry);
fStatus = CopyToNdisSafe(Buffer, &Buffer, InfoBuff,
sizeof(IPNetToMediaEntry), &Offset);
if (fStatus == FALSE) {
CTEFreeLock(&AI->ai_ARPTblLock, Handle);
return(TDI_NO_RESOURCES);
}
} else
break;
}
*Size = BytesCopied;
CTEFreeLock(&AI->ai_ARPTblLock, Handle);
return(!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
}
return TDI_INVALID_PARAMETER;
}
if (ID->toi_class != INFO_CLASS_PROTOCOL)
return TDI_INVALID_PARAMETER;
// He must be asking for interface level information. See if we support
// what he's asking for.
if (ID->toi_id == IF_MIB_STATS_ID) {
IFEntry *IFE = (IFEntry *) InfoBuff;
uint speed;
// He's asking for statistics. Make sure his buffer is at least big
// enough to hold the fixed part.
if (BufferSize < IFE_FIXED_SIZE) {
return TDI_BUFFER_TOO_SMALL;
}
// He's got enough to hold the fixed part. Build the IFEntry structure,
// and copy it to his buffer.
IFE->if_index = AI->ai_index;
switch (AI->ai_media) {
case NdisMedium802_3:
IFE->if_type = IF_TYPE_ETHERNET_CSMACD;
break;
case NdisMedium802_5:
IFE->if_type = IF_TYPE_ISO88025_TOKENRING;
break;
case NdisMediumFddi:
IFE->if_type = IF_TYPE_FDDI;
break;
case NdisMediumArcnet878_2:
default:
IFE->if_type = IF_TYPE_OTHER;
break;
}
IFE->if_mtu = AI->ai_mtu;
// Some adapters support dynamic speed settings and causes this
// query to return a different speed from the Networks Connection
// folder. Therefore, we will requery the speed of the
// interface. Should we update the ai_speed? Anf if so, do we update
// if_speed as well?
IFE->if_speed = AI->ai_speed;
if (AI->ai_operstate == IF_OPER_STATUS_OPERATIONAL){
if (DoNDISRequest(
AI,
NdisRequestQueryInformation,
OID_GEN_LINK_SPEED,
&speed,
sizeof(speed),
NULL,
TRUE) == NDIS_STATUS_SUCCESS) {
// Update to real value we want to return.
speed *= 100L;
IFE->if_speed = speed;
} else {
// Should we fail, or just update with known speed.
IFE->if_speed = AI->ai_speed;
}
}
IFE->if_physaddrlen = AI->ai_addrlen;
RtlCopyMemory(IFE->if_physaddr, AI->ai_addr, AI->ai_addrlen);
IFE->if_adminstatus = (uint) AI->ai_adminstate;
IFE->if_operstatus = (uint) AI->ai_operstate;
IFE->if_lastchange = AI->ai_lastchange;
IFE->if_inoctets = AI->ai_inoctets;
IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX];
IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX];
IFE->if_indiscards = AI->ai_indiscards;
IFE->if_inerrors = AI->ai_inerrors;
IFE->if_inunknownprotos = AI->ai_uknprotos;
IFE->if_outoctets = AI->ai_outoctets;
IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX];
IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX];
IFE->if_outdiscards = AI->ai_outdiscards;
IFE->if_outerrors = AI->ai_outerrors;
IFE->if_outqlen = AI->ai_qlen;
IFE->if_descrlen = AI->ai_desclen;
#if FFP_SUPPORT
// If FFP enabled on this interface, adjust IF stats for FFP'd packets
if (AI->ai_ffpversion) {
FFPAdapterStats IFStatsInfo =
{
NDIS_PROTOCOL_ID_TCP_IP,
0, 0, 0, 0, 0, 0, 0, 0
};
// Update ARP SNMP vars to account for FFP'd packets
if (DoNDISRequest(AI, NdisRequestQueryInformation, OID_FFP_ADAPTER_STATS,
&IFStatsInfo, sizeof(FFPAdapterStats), NULL, TRUE)
== NDIS_STATUS_SUCCESS) {
// Compensate 'inoctets' for packets not seen due to FFP
IFE->if_inoctets += IFStatsInfo.InOctetsForwarded;
IFE->if_inoctets += IFStatsInfo.InOctetsDiscarded;
// Compensate 'inucastpkts' for packets not seen due to FFP
// Assume all FFP fwded/dropped pkts came in as Eth Unicasts
// A check to see if it is a ucast or an mcast would slow FFP
IFE->if_inucastpkts += IFStatsInfo.InPacketsForwarded;
IFE->if_inucastpkts += IFStatsInfo.InPacketsDiscarded;
// Compensate 'outoctets' for packets not seen due to FFP
IFE->if_outoctets += IFStatsInfo.OutOctetsForwarded;
// Compensate 'outucastpkts' for packets not seen due to FFP
// Assume all FFP fwded are sent as Ethernet Unicasts
// A check to see if it is a ucast or an mcast would slow FFP
IFE->if_outucastpkts += IFStatsInfo.OutPacketsForwarded;
}
}
#endif // if FFP_SUPPORT
fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *) IFE, IFE_FIXED_SIZE, &Offset);
if (fStatus == FALSE) {
return TDI_NO_RESOURCES;
}
// See if he has room for the descriptor string.
if (BufferSize >= (IFE_FIXED_SIZE + AI->ai_desclen)) {
// He has room. Copy it.
if (AI->ai_desclen != 0) {
fStatus = CopyToNdisSafe(Buffer, NULL, AI->ai_desc, AI->ai_desclen, &Offset);
}
if (fStatus == FALSE) {
return TDI_NO_RESOURCES;
}
*Size = IFE_FIXED_SIZE + AI->ai_desclen;
return TDI_SUCCESS;
} else {
// Not enough room to copy the desc. string.
*Size = IFE_FIXED_SIZE;
return TDI_BUFFER_OVERFLOW;
}
} else if (ID->toi_id == IF_FRIENDLY_NAME_ID) {
uint Status;
PNDIS_BUFFER NextBuffer;
NDIS_STRING NdisString;
// This is a query for the adapter's friendly name.
// We'll convert this to an OID_GEN_FRIENDLY_NAME query for NDIS,
// and transfer the resulting UNICODE_STRING to the caller's buffer
// as a nul-terminated Unicode string.
if (NdisQueryAdapterInstanceName(&NdisString, AI->ai_handle) ==
NDIS_STATUS_SUCCESS) {
// Verify that the buffer is large enough for the string we just
// retrieved and, if so, attempt to copy the string to the
// caller's buffer. If that succeeds, nul-terminate the resulting
// string.
if (BufferSize >= (NdisString.Length + 1) * sizeof(WCHAR)) {
fStatus = CopyToNdisSafe(Buffer, &NextBuffer,
(uchar *)NdisString.Buffer,
NdisString.Length, &Offset);
if (fStatus) {
WCHAR Nul = L'\0';
fStatus = CopyToNdisSafe(Buffer, &NextBuffer, (uchar *)&Nul,
sizeof(Nul), &Offset);
if (fStatus) {
*Size = NdisString.Length + sizeof(Nul);
Status = TDI_SUCCESS;
} else
Status = TDI_NO_RESOURCES;
} else
Status = TDI_NO_RESOURCES;
} else
Status = TDI_BUFFER_OVERFLOW;
NdisFreeString(NdisString);
return Status;
} else
return TDI_NO_RESOURCES;
}
return TDI_INVALID_PARAMETER;
}
//* ARPSetInfo - ARP set information handler.
//
// The ARP set information handler. We support setting of an I/F admin
// status, and setting/deleting of ARP table entries.
//
// Input: Context - Pointer to I/F to set on.
// ID - The object ID
// Buffer - Pointer to buffer containing value to set.
// Size - Size in bytes of Buffer.
//
// Returns: Status of attempt to set information.
//
int
__stdcall
ARPSetInfo(void *Context, TDIObjectID * ID, void *Buffer, uint Size)
{
ARPInterface *Interface = (ARPInterface *) Context;
CTELockHandle Handle, EntryHandle;
int Status;
IFEntry *IFE = (IFEntry *) Buffer;
IPNetToMediaEntry *IPNME;
ARPTableEntry *PrevATE, *CurrentATE;
ARPTable *Table;
ENetHeader *Header;
uint Entity, Instance;
PNDIS_PACKET Packet;
Entity = ID->toi_entity.tei_entity;
Instance = ID->toi_entity.tei_instance;
// First, make sure it's possibly an ID we can handle.
if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) &&
(Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) {
return TDI_INVALID_REQUEST;
}
if (ID->toi_type != INFO_TYPE_PROVIDER) {
return TDI_INVALID_PARAMETER;
}
// Might be able to handle this.
if (Entity == IF_ENTITY) {
// It's for the I/F level, see if it's for the statistics.
if (ID->toi_class != INFO_CLASS_PROTOCOL)
return TDI_INVALID_PARAMETER;
if (ID->toi_id == IF_MIB_STATS_ID) {
// It's for the stats. Make sure it's a valid size.
if (Size >= IFE_FIXED_SIZE) {
// It's a valid size. See what he wants to do.
CTEGetLock(&Interface->ai_lock, &Handle);
switch (IFE->if_adminstatus) {
case IF_STATUS_UP:
// He's marking it up. If the operational state is
// alse up, mark the whole interface as up.
Interface->ai_adminstate = IF_STATUS_UP;
if (Interface->ai_operstate == IF_OPER_STATUS_OPERATIONAL)
Interface->ai_state = INTERFACE_UP;
Status = TDI_SUCCESS;
break;
case IF_STATUS_DOWN:
// He's taking it down. Mark both the admin state and
// the interface state down.
Interface->ai_adminstate = IF_STATUS_DOWN;
Interface->ai_state = INTERFACE_DOWN;
Status = TDI_SUCCESS;
break;
case IF_STATUS_TESTING:
// He's trying to cause up to do testing, which we
// don't support. Just return success.
Status = TDI_SUCCESS;
break;
default:
Status = TDI_INVALID_PARAMETER;
break;
}
CTEFreeLock(&Interface->ai_lock, Handle);
return Status;
} else
return TDI_INVALID_PARAMETER;
} else {
return TDI_INVALID_PARAMETER;
}
}
// Not for the interface level. See if it's an implementation or protocol
// class.
if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
ProxyArpEntry *PArpEntry;
ARPIPAddr *Addr;
IPAddr AddAddr;
IPMask Mask;
// It's for the implementation. It should be the proxy-ARP ID.
if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry))
return TDI_INVALID_PARAMETER;
PArpEntry = (ProxyArpEntry *) Buffer;
AddAddr = PArpEntry->pae_addr;
Mask = PArpEntry->pae_mask;
// See if he's trying to add or delete a proxy arp entry.
if (PArpEntry->pae_status == PAE_STATUS_VALID) {
// We're trying to add an entry. We won't allow an entry
// to be added that we believe to be invalid or conflicting
// with our local addresses.
if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) ||
IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) ||
IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) ||
CLASSD_ADDR(AddAddr))
return TDI_INVALID_PARAMETER;
// Walk through the list of addresses on the interface, and see
// if they would match the AddAddr. If so, fail the request.
CTEGetLock(&Interface->ai_lock, &Handle);
if (IsBCastOnIF(Interface, AddAddr & Mask)) {
CTEFreeLock(&Interface->ai_lock, Handle);
return TDI_INVALID_PARAMETER;
}
Addr = &Interface->ai_ipaddr;
do {
if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr))
break;
}
Addr = Addr->aia_next;
} while (Addr != NULL);
CTEFreeLock(&Interface->ai_lock, Handle);
if (Addr != NULL)
return TDI_INVALID_PARAMETER;
// At this point, we believe we're ok. Try to add the address.
if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL))
return TDI_SUCCESS;
else
return TDI_NO_RESOURCES;
} else {
if (PArpEntry->pae_status == PAE_STATUS_INVALID) {
// He's trying to delete a proxy ARP address.
if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask))
return TDI_SUCCESS;
}
return TDI_INVALID_PARAMETER;
}
}
if (ID->toi_class != INFO_CLASS_PROTOCOL) {
return TDI_INVALID_PARAMETER;
}
if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID &&
Size >= sizeof(IPNetToMediaEntry)) {
// He does want to set an ARP table entry. See if he's trying to
// create or delete one.
IPNME = (IPNetToMediaEntry *) Buffer;
if (IPNME->inme_type == INME_TYPE_INVALID) {
uint Index = ARP_HASH(IPNME->inme_addr);
// We're trying to delete an entry. See if we can find it,
// and then delete it.
CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
Table = Interface->ai_ARPTbl;
PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next);
CurrentATE = (*Table)[Index];
while (CurrentATE != (ARPTableEntry *) NULL) {
if (CurrentATE->ate_dest == IPNME->inme_addr) {
// Found him. Break out of the loop.
break;
} else {
PrevATE = CurrentATE;
CurrentATE = CurrentATE->ate_next;
}
}
if (CurrentATE != NULL) {
CTEGetLock(&CurrentATE->ate_lock, &EntryHandle);
if (CurrentATE->ate_resolveonly) {
ARPControlBlock *ArpContB, *TmpArpContB;
ArpContB = CurrentATE->ate_resolveonly;
while (ArpContB) {
ArpRtn rtn;
rtn = (ArpRtn) ArpContB->CompletionRtn;
ArpContB->status = STATUS_UNSUCCESSFUL;
TmpArpContB = ArpContB->next;
(*rtn) (ArpContB, STATUS_UNSUCCESSFUL);
ArpContB = TmpArpContB;
}
CurrentATE->ate_resolveonly = NULL;
}
RemoveARPTableEntry(PrevATE, CurrentATE);
Interface->ai_count--;
CTEFreeLockFromDPC(&CurrentATE->ate_lock, EntryHandle);
CTEFreeLock(&Interface->ai_ARPTblLock, Handle);
if (CurrentATE->ate_packet != NULL) {
IPSendComplete(Interface->ai_context,
CurrentATE->ate_packet, NDIS_STATUS_SUCCESS);
}
CTEFreeMem(CurrentATE);
return TDI_SUCCESS;
} else
Status = TDI_INVALID_PARAMETER;
CTEFreeLock(&Interface->ai_ARPTblLock, Handle);
return Status;
}
// We're not trying to delete. See if we're trying to create.
if (IPNME->inme_type != INME_TYPE_DYNAMIC &&
IPNME->inme_type != INME_TYPE_STATIC) {
// Not creating, return an error.
return TDI_INVALID_PARAMETER;
}
// Make sure he's trying to create a valid address.
if (IPNME->inme_physaddrlen != Interface->ai_addrlen)
return TDI_INVALID_PARAMETER;
// We're trying to create an entry. Call CreateARPTableEntry to create
// one, and fill it in.
CurrentATE = CreateARPTableEntry(Interface, IPNME->inme_addr, &Handle, 0);
if (CurrentATE == NULL) {
return TDI_NO_RESOURCES;
}
// We've created or found an entry. Fill it in.
Header = (ENetHeader *) CurrentATE->ate_addr;
switch (Interface->ai_media) {
case NdisMedium802_5:
{
TRHeader *Temp = (TRHeader *) Header;
// Fill in the TR specific parts, and set the length to the
// size of a TR header.
Temp->tr_ac = ARP_AC;
Temp->tr_fc = ARP_FC;
RtlCopyMemory(&Temp->tr_saddr[ARP_802_ADDR_LENGTH], ARPSNAP,
sizeof(SNAPHeader));
Header = (ENetHeader *) & Temp->tr_daddr;
CurrentATE->ate_addrlength = sizeof(TRHeader) +
sizeof(SNAPHeader);
}
break;
case NdisMedium802_3:
CurrentATE->ate_addrlength = sizeof(ENetHeader);
break;
case NdisMediumFddi:
{
FDDIHeader *Temp = (FDDIHeader *) Header;
Temp->fh_pri = ARP_FDDI_PRI;
RtlCopyMemory(&Temp->fh_saddr[ARP_802_ADDR_LENGTH], ARPSNAP,
sizeof(SNAPHeader));
Header = (ENetHeader *) & Temp->fh_daddr;
CurrentATE->ate_addrlength = sizeof(FDDIHeader) +
sizeof(SNAPHeader);
}
break;
case NdisMediumArcnet878_2:
{
ARCNetHeader *Temp = (ARCNetHeader *) Header;
Temp->ah_saddr = Interface->ai_addr[0];
Temp->ah_daddr = IPNME->inme_physaddr[0];
Temp->ah_prot = ARP_ARCPROT_IP;
CurrentATE->ate_addrlength = sizeof(ARCNetHeader);
}
break;
default:
ASSERT(0);
break;
}
// Copy in the source and destination addresses.
if (Interface->ai_media != NdisMediumArcnet878_2) {
RtlCopyMemory(Header->eh_daddr, IPNME->inme_physaddr,
ARP_802_ADDR_LENGTH);
RtlCopyMemory(Header->eh_saddr, Interface->ai_addr,
ARP_802_ADDR_LENGTH);
// Now fill in the Ethertype.
*(ushort *) & CurrentATE->ate_addr[CurrentATE->ate_addrlength - 2] =
net_short(ARP_ETYPE_IP);
}
// If he's creating a static entry, mark it as always valid. Otherwise
// mark him as valid now.
if (IPNME->inme_type == INME_TYPE_STATIC)
CurrentATE->ate_valid = ALWAYS_VALID;
else
CurrentATE->ate_valid = CTESystemUpTime();
CurrentATE->ate_state = ARP_GOOD;
Packet = CurrentATE->ate_packet;
CurrentATE->ate_packet = NULL;
CTEFreeLock(&CurrentATE->ate_lock, Handle);
if (Packet) {
IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
}
return TDI_SUCCESS;
}
return TDI_INVALID_PARAMETER;
}
#pragma BEGIN_INIT
//** ARPInit - Initialize the ARP module.
//
// This functions intializes all of the ARP module, including allocating
// the ARP table and any other necessary data structures.
//
// Entry: nothing.
//
// Exit: Returns 0 if we fail to init., !0 if we succeed.
//
int
ARPInit()
{
NDIS_STATUS Status; // Status for NDIS calls.
NDIS_PROTOCOL_CHARACTERISTICS Characteristics;
DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+ARPInit()\n")));
RtlZeroMemory(&Characteristics, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
Characteristics.MajorNdisVersion = NDIS_MAJOR_VERSION;
Characteristics.MinorNdisVersion = NDIS_MINOR_VERSION;
Characteristics.OpenAdapterCompleteHandler = ARPOAComplete;
Characteristics.CloseAdapterCompleteHandler = ARPCAComplete;
Characteristics.SendCompleteHandler = ARPSendComplete;
Characteristics.TransferDataCompleteHandler = ARPTDComplete;
Characteristics.ResetCompleteHandler = ARPResetComplete;
Characteristics.RequestCompleteHandler = ARPRequestComplete;
Characteristics.ReceiveHandler = ARPRcv,
Characteristics.ReceiveCompleteHandler = ARPRcvComplete;
Characteristics.StatusHandler = ARPStatus;
Characteristics.StatusCompleteHandler = ARPStatusComplete;
//
// Re-direct to IP since IP now binds to NDIS.
//
Characteristics.BindAdapterHandler = IPBindAdapter; // ARPBindAdapter;
Characteristics.UnbindAdapterHandler = ARPUnbindAdapter;
Characteristics.PnPEventHandler = ARPPnPEvent;
#if MILLEN
Characteristics.UnloadHandler = ARPUnloadProtocol;
#endif // MILLEN
RtlInitUnicodeString(&(Characteristics.Name), ARPName);
Characteristics.ReceivePacketHandler = ARPRcvPacket;
DEBUGMSG(DBG_INFO && DBG_INIT,
(DTEXT("ARPInit: Calling NdisRegisterProtocol %d:%d %ws\n"),
NDIS_MAJOR_VERSION, NDIS_MINOR_VERSION, ARPName));
NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *)
& Characteristics, sizeof(Characteristics));
DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-ARPInit [%x]\n"), Status));
if (Status == NDIS_STATUS_SUCCESS) {
return(1);
} else {
return(0);
}
}
//* FreeARPInterface - Free an ARP interface
//
// Called in the event of some sort of initialization failure. We free all
// the memory associated with an ARP interface.
//
// Entry: Interface - Pointer to interface structure to be freed.
//
// Returns: Nothing.
//
void
FreeARPInterface(ARPInterface *Interface)
{
NDIS_STATUS Status;
ARPTable *Table; // ARP table.
uint i; // Index variable.
ARPTableEntry *ATE;
CTELockHandle LockHandle;
NDIS_HANDLE Handle;
PNDIS_BUFFER tmpBuffer;
PSINGLE_LIST_ENTRY pBufLink;
if (Interface->ai_timerstarted &&
!CTEStopTimer(&Interface->ai_timer)) {
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not stop ai_timer - waiting for event\n"));
(VOID) CTEBlock(&Interface->ai_timerblock);
KeClearEvent(&Interface->ai_timerblock.cbs_event);
}
// If we're bound to the adapter, close it now.
CTEInitBlockStruc(&Interface->ai_block);
CTEGetLock(&Interface->ai_lock, &LockHandle);
if (Interface->ai_handle != (NDIS_HANDLE) NULL) {
Handle = Interface->ai_handle;
Interface->ai_handle = NULL;
CTEFreeLock(&Interface->ai_lock, LockHandle);
NdisCloseAdapter(&Status, Handle);
if (Status == NDIS_STATUS_PENDING)
Status = CTEBlock(&Interface->ai_block);
} else {
CTEFreeLock(&Interface->ai_lock, LockHandle);
}
// First free any outstanding ARP table entries.
Table = Interface->ai_ARPTbl;
if (Table != NULL) {
for (i = 0; i < ARP_TABLE_SIZE; i++) {
while ((*Table)[i] != NULL) {
ATE = (*Table)[i];
if (ATE->ate_resolveonly) {
ARPControlBlock *ArpContB, *TmpArpContB;
ArpContB = ATE->ate_resolveonly;
while (ArpContB) {
ArpRtn rtn;
rtn = (ArpRtn) ArpContB->CompletionRtn;
ArpContB->status = STATUS_UNSUCCESSFUL;
TmpArpContB = ArpContB->next;
(*rtn) (ArpContB, STATUS_UNSUCCESSFUL);
ArpContB = TmpArpContB;
}
ATE->ate_resolveonly = NULL;
}
RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]),
ate_next), ATE);
if (ATE->ate_packet) {
IPSendComplete(Interface->ai_context, ATE->ate_packet,
NDIS_STATUS_SUCCESS);
}
CTEFreeMem(ATE);
}
}
CTEFreeMem(Table);
}
Interface->ai_ARPTbl = NULL;
if (Interface->ai_ppool != (NDIS_HANDLE) NULL)
NdisFreePacketPool(Interface->ai_ppool);
if (Interface->ai_devicename.Buffer != NULL) {
CTEFreeMem(Interface->ai_devicename.Buffer);
}
if (Interface->ai_desc) {
CTEFreeMem(Interface->ai_desc);
}
// Free the interface itself.
CTEFreeMem(Interface);
}
//** ARPOpen - Open an adapter for reception.
//
// This routine is called when the upper layer is done initializing and wishes to
// begin receiveing packets. The adapter is actually 'open', we just call InitAdapter
// to set the packet filter and lookahead size.
//
// Input: Context - Interface pointer we gave to IP earlier.
//
// Returns: Nothing
//
void
__stdcall
ARPOpen(void *Context)
{
ARPInterface *Interface = (ARPInterface *) Context;
InitAdapter(Interface); // Set the packet filter - we'll begin receiving.
}
//* ARPGetEList - Get the entity list.
//
// Called at init time to get an entity list. We fill our stuff in, and
// then call the interfaces below us to allow them to do the same.
//
// Input: EntityList - Pointer to entity list to be filled in.
// Count - Pointer to number of entries in the list.
//
// Returns Status of attempt to get the info.
//
int
__stdcall
ARPGetEList(void *Context, TDIEntityID * EList, uint * Count)
{
ARPInterface *Interface = (ARPInterface *) Context;
uint MyATBase;
uint MyIFBase;
uint i;
TDIEntityID *ATEntity, *IFEntity;
TDIEntityID *EntityList;
// Walk down the list, looking for existing AT or IF entities, and
// adjust our base instance accordingly.
// if we are already on the list then do nothing.
// if we are going away, mark our entry invalid.
EntityList = EList;
MyATBase = 0;
MyIFBase = 0;
ATEntity = NULL;
IFEntity = NULL;
for (i = 0; i < *Count; i++, EntityList++) {
if (EntityList->tei_entity == AT_ENTITY) {
// if we are already on the list remember our entity item
// o/w find an instance # for us.
if (EntityList->tei_instance == Interface->ai_atinst &&
EntityList->tei_instance != INVALID_ENTITY_INSTANCE) {
ATEntity = EntityList;
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - Found our interface %lx at_atinst %lx\n",Interface, Interface->ai_atinst));
} else {
MyATBase = MAX(MyATBase, EntityList->tei_instance + 1);
}
} else {
if (EntityList->tei_entity == IF_ENTITY)
// if we are already on the list remember our entity item
// o/w find an instance # for us.
if (EntityList->tei_instance == Interface->ai_ifinst &&
EntityList->tei_instance != INVALID_ENTITY_INSTANCE) {
IFEntity = EntityList;
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - Found our interface %lx ai_ifinst %lx\n",Interface, Interface->ai_ifinst));
} else {
MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1);
}
}
if (ATEntity && IFEntity) {
break;
}
}
if (ATEntity) {
// we are already on the list.
// are we going away?
if (Interface->ai_state & INTERFACE_DOWN) {
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - our interface %lx atinst %lx going away \n",Interface, Interface->ai_atinst));
ATEntity->tei_instance = INVALID_ENTITY_INSTANCE;
}
} else {
// we are not on the list.
// insert ourself iff we are not going away.
if (!(Interface->ai_state & INTERFACE_DOWN)) {
// make sure we have the room for it.
if (*Count >= MAX_TDI_ENTITIES) {
return FALSE;
}
Interface->ai_atinst = MyATBase;
ATEntity = &EList[*Count];
ATEntity->tei_entity = AT_ENTITY;
ATEntity->tei_instance = MyATBase;
(*Count)++;
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - adding interface %lx atinst %lx \n",Interface, Interface->ai_atinst));
}
}
if (IFEntity) {
// we are already on the list.
// are we going away?
if (Interface->ai_state & INTERFACE_DOWN) {
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - our interface %lx ifinst %lx going away \n",Interface, Interface->ai_ifinst));
IFEntity->tei_instance = INVALID_ENTITY_INSTANCE;
}
} else {
// we are not on the list.
// insert ourself iff we are not going away.
if (!(Interface->ai_state & INTERFACE_DOWN)) {
// make sure we have the room for it.
if (*Count >= MAX_TDI_ENTITIES) {
return FALSE;
}
Interface->ai_ifinst = MyIFBase;
IFEntity = &EList[*Count];
IFEntity->tei_entity = IF_ENTITY;
IFEntity->tei_instance = MyIFBase;
(*Count)++;
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - adding interface %lx ifinst %lx \n",Interface, Interface->ai_ifinst));
}
}
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetEList: arp interface %lx, ai_atinst %lx, ai_ifinst %lx, total %lx\n",
// Interface, Interface->ai_atinst, Interface->ai_ifinst, *Count));
return TRUE;
}
extern uint UseEtherSNAP(PNDIS_STRING Name);
extern void GetAlwaysSourceRoute(uint * pArpAlwaysSourceRoute, uint * pIPAlwaysSourceRoute);
extern uint GetArpCacheLife(void);
extern uint GetArpRetryCount(void);
//** InitTaskOffloadHeader - Initializes the task offload header wrt version
// and encapsulation, etc.
//
// All task offload header structure members are initialized.
//
// Input:
// ai - ARPInterface for which we are initializing
// the task offload header.
// TaskOffloadHeader - Pointer to task offload header to initialize.
// Returns:
// None.
//
VOID
InitTaskOffloadHeader(ARPInterface *ai,
PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader)
{
TaskOffloadHeader->Version = NDIS_TASK_OFFLOAD_VERSION;
TaskOffloadHeader->Size = sizeof(NDIS_TASK_OFFLOAD_HEADER);
TaskOffloadHeader->EncapsulationFormat.Flags.FixedHeaderSize = 1;
TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize = ai->ai_hdrsize;
TaskOffloadHeader->OffsetFirstTask = 0;
if (ai->ai_media == NdisMedium802_3) {
if (ai->ai_snapsize) {
TaskOffloadHeader->EncapsulationFormat.Encapsulation = LLC_SNAP_ROUTED_Encapsulation;
TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize += ai->ai_snapsize;
} else {
TaskOffloadHeader->EncapsulationFormat.Encapsulation = IEEE_802_3_Encapsulation;
}
} else if (ai->ai_media == NdisMedium802_5) {
TaskOffloadHeader->EncapsulationFormat.Encapsulation = IEEE_802_5_Encapsulation;
} else {
TaskOffloadHeader->EncapsulationFormat.Encapsulation = UNSPECIFIED_Encapsulation;
}
return;
}
//**SetOffload - Set offload capabilities
//
//
// All task offload header structure members are initialized.
//
// Input:
// ai - ARPInterface for which we are initializing
// the task offload header.
// TaskOffloadHeader - Pointer to task offload header to initialize.
// Bufsize - length of task offload buffer allocated by teh caller
//
// Returns:
// TRUE - successfully set the offload capability
// FALSE - failure case
//
BOOLEAN
SetOffload(ARPInterface *ai,PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader,uint BufSize)
{
PNDIS_TASK_OFFLOAD tmpoffload;
PNDIS_TASK_OFFLOAD TaskOffload, NextTaskOffLoad, LastTaskOffload;
NDIS_TASK_IPSEC ipsecCaps;
uint TotalLength;
NDIS_STATUS Status;
uint PrevOffLoad=ai->ai_OffloadFlags;
//Parse the buffer for Checksum and tcplargesend offload capabilities
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Something to Offload. offload buffer size %x\n", BufSize));
ASSERT(TaskOffloadHeader->OffsetFirstTask == sizeof(NDIS_TASK_OFFLOAD_HEADER));
TaskOffload = tmpoffload = (NDIS_TASK_OFFLOAD *) ((uchar *) TaskOffloadHeader + TaskOffloadHeader->OffsetFirstTask);
if (BufSize >= (TaskOffloadHeader->OffsetFirstTask + sizeof(NDIS_TASK_OFFLOAD))) {
while (tmpoffload) {
if (tmpoffload->Task == TcpIpChecksumNdisTask) {
//Okay we this adapter supports checksum offload
//check if tcp and/or ip chksums bits are present
PNDIS_TASK_TCP_IP_CHECKSUM ChecksumInfo;
ChecksumInfo = (PNDIS_TASK_TCP_IP_CHECKSUM) & tmpoffload->TaskBuffer[0];
//if (ChecksumInfo->V4Transmit.V4Checksum) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"V4 Checksum offload\n"));
if (ChecksumInfo->V4Transmit.TcpChecksum) {
ai->ai_OffloadFlags |= TCP_XMT_CHECKSUM_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Tcp Checksum offload\n"));
}
if (ChecksumInfo->V4Transmit.IpChecksum) {
ai->ai_OffloadFlags |= IP_XMT_CHECKSUM_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP xmt Checksum offload\n"));
}
if (ChecksumInfo->V4Receive.TcpChecksum) {
ai->ai_OffloadFlags |= TCP_RCV_CHECKSUM_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Tcp Rcv Checksum offload\n"));
}
if (ChecksumInfo->V4Receive.IpChecksum) {
ai->ai_OffloadFlags |= IP_RCV_CHECKSUM_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP rcv Checksum offload\n"));
}
if (ChecksumInfo->V4Transmit.IpOptionsSupported) {
ai->ai_OffloadFlags |= IP_CHECKSUM_OPT_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP Checksum xmt options offload\n"));
}
if (ChecksumInfo->V4Transmit.TcpOptionsSupported) {
ai->ai_OffloadFlags |= TCP_CHECKSUM_OPT_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," TCP Checksum xmt options offload\n"));
}
} else if ((tmpoffload->Task == TcpLargeSendNdisTask) && (ai->ai_snapsize == 0)) {
PNDIS_TASK_TCP_LARGE_SEND TcpLargeSend, in_LargeSend = (PNDIS_TASK_TCP_LARGE_SEND) & tmpoffload->TaskBuffer[0];
ai->ai_OffloadFlags |= TCP_LARGE_SEND_OFFLOAD;
TcpLargeSend = &ai->ai_TcpLargeSend;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Tcp large send!! \n"));
TcpLargeSend->MaxOffLoadSize = in_LargeSend->MaxOffLoadSize;
TcpLargeSend->MinSegmentCount = in_LargeSend->MinSegmentCount;
// no tcp or ip options when doing large send
// Need to reevaluate this as we turn on Time stamp option.
if (in_LargeSend->TcpOptions) {
ai->ai_OffloadFlags |= TCP_LARGE_SEND_TCPOPT_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," TCP largesend options offload\n"));
}
if (in_LargeSend->IpOptions) {
ai->ai_OffloadFlags |= TCP_LARGE_SEND_IPOPT_OFFLOAD;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP largesend options offload\n"));
}
} else if (tmpoffload->Task == IpSecNdisTask) {
PNDIS_TASK_IPSEC pIPSecCaps = (PNDIS_TASK_IPSEC) & tmpoffload->TaskBuffer[0];
//
// Save off the capabilities for setting them later.
//
ipsecCaps = *pIPSecCaps;
//
// CryptoOnly is assumed if we have IpSecNdisTask
//
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_CRYPTO_ONLY;
//
// Do Support first
//
if (pIPSecCaps->Supported.AH_ESP_COMBINED) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_ESP;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_ESP\n"));
}
if (pIPSecCaps->Supported.TRANSPORT_TUNNEL_COMBINED) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_TPT_TUNNEL;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TPT_TUNNEL\n"));
}
if (pIPSecCaps->Supported.V4_OPTIONS) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_V4_OPTIONS;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"V4_OPTIONS\n"));
}
if (pIPSecCaps->Supported.RESERVED) {
pIPSecCaps->Supported.RESERVED = 0;
//ai->ai_OffloadFlags |= IPSEC_OFFLOAD_QUERY_SPI;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"QUERY_SPI\n"));
}
//
// Do V4AH next
//
if (pIPSecCaps->V4AH.MD5) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_MD5;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"MD5\n"));
}
if (pIPSecCaps->V4AH.SHA_1) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_SHA_1;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"SHA\n"));
}
if (pIPSecCaps->V4AH.Transport) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_TPT;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_TRANSPORT\n"));
}
if (pIPSecCaps->V4AH.Tunnel) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_TUNNEL;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_TUNNEL\n"));
}
if (pIPSecCaps->V4AH.Send) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_XMT;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_XMT\n"));
}
if (pIPSecCaps->V4AH.Receive) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_AH_RCV;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_RCV\n"));
}
//
// Do V4ESP next
//
if (pIPSecCaps->V4ESP.DES) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_DES;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_DES\n"));
}
if (pIPSecCaps->V4ESP.RESERVED) {
pIPSecCaps->V4ESP.RESERVED = 0;
//ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_DES_40;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_DES_40\n"));
}
if (pIPSecCaps->V4ESP.TRIPLE_DES) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_3_DES;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_3_DES\n"));
}
if (pIPSecCaps->V4ESP.NULL_ESP) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_NONE;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_NONE\n"));
}
if (pIPSecCaps->V4ESP.Transport) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_TPT;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_TRANSPORT\n"));
}
if (pIPSecCaps->V4ESP.Tunnel) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_TUNNEL;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_TUNNEL\n"));
}
if (pIPSecCaps->V4ESP.Send) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_XMT;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_XMT\n"));
}
if (pIPSecCaps->V4ESP.Receive) {
ai->ai_OffloadFlags |= IPSEC_OFFLOAD_ESP_RCV;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_RCV\n"));
}
}
// Point to the next offload structure
if (tmpoffload->OffsetNextTask) {
tmpoffload = (PNDIS_TASK_OFFLOAD)
((PUCHAR) tmpoffload + tmpoffload->OffsetNextTask);
} else {
tmpoffload = NULL;
}
} //while
} else { //if BufSize is not okay
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"response of task offload does not have sufficient space even for 1 offload task!!\n"));
return FALSE;
}
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP: Previous H/W capabilities: %lx\n", PrevOffLoad));
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP: Supported H/W capabilities: %lx\n", ai->ai_OffloadFlags));
//Enable the capabilities by setting them.
if (PrevOffLoad) {
\
ai->ai_OffloadFlags &= PrevOffLoad;
}
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP: Enabling H/W capabilities: %lx\n", ai->ai_OffloadFlags));
TaskOffload->Task = 0;
TaskOffload->OffsetNextTask = 0;
NextTaskOffLoad = LastTaskOffload = TaskOffload;
TotalLength = sizeof(NDIS_TASK_OFFLOAD_HEADER);
if ((ai->ai_OffloadFlags & TCP_XMT_CHECKSUM_OFFLOAD) ||
(ai->ai_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) ||
(ai->ai_OffloadFlags & TCP_RCV_CHECKSUM_OFFLOAD) ||
(ai->ai_OffloadFlags & IP_RCV_CHECKSUM_OFFLOAD)) {
PNDIS_TASK_TCP_IP_CHECKSUM ChksumBuf = (PNDIS_TASK_TCP_IP_CHECKSUM) & NextTaskOffLoad->TaskBuffer[0];
NextTaskOffLoad->Task = TcpIpChecksumNdisTask;
NextTaskOffLoad->TaskBufferLength = sizeof(NDIS_TASK_TCP_IP_CHECKSUM);
NextTaskOffLoad->OffsetNextTask = FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) +
NextTaskOffLoad->TaskBufferLength;
TotalLength += NextTaskOffLoad->OffsetNextTask;
RtlZeroMemory(ChksumBuf, sizeof(NDIS_TASK_TCP_IP_CHECKSUM));
if (ai->ai_OffloadFlags & TCP_XMT_CHECKSUM_OFFLOAD) {
ChksumBuf->V4Transmit.TcpChecksum = 1;
//ChksumBuf->V4Transmit.V4Checksum = 1;
}
if (ai->ai_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) {
ChksumBuf->V4Transmit.IpChecksum = 1;
//ChksumBuf->V4Transmit.V4Checksum = 1;
}
if (ai->ai_OffloadFlags & TCP_RCV_CHECKSUM_OFFLOAD) {
ChksumBuf->V4Receive.TcpChecksum = 1;
//ChksumBuf->V4Receive.V4Checksum = 1;
}
if (ai->ai_OffloadFlags & IP_RCV_CHECKSUM_OFFLOAD) {
ChksumBuf->V4Receive.IpChecksum = 1;
//ChksumBuf->V4Receive.V4Checksum = 1;
}
LastTaskOffload = NextTaskOffLoad;
NextTaskOffLoad = (PNDIS_TASK_OFFLOAD)
((PUCHAR) NextTaskOffLoad + NextTaskOffLoad->OffsetNextTask);
}
if (ai->ai_OffloadFlags & TCP_LARGE_SEND_OFFLOAD) {
PNDIS_TASK_TCP_LARGE_SEND TcpLargeSend, out_LargeSend = (PNDIS_TASK_TCP_LARGE_SEND) & NextTaskOffLoad->TaskBuffer[0];
NextTaskOffLoad->Task = TcpLargeSendNdisTask;
NextTaskOffLoad->TaskBufferLength = sizeof(NDIS_TASK_TCP_LARGE_SEND);
NextTaskOffLoad->OffsetNextTask = FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + NextTaskOffLoad->TaskBufferLength;
TotalLength += NextTaskOffLoad->OffsetNextTask;
//(uchar)TaskOffload + sizeof(NDIS_TASK_OFFLOAD) + NextTaskOffload->TaskBufferLength;
TcpLargeSend = &ai->ai_TcpLargeSend;
RtlZeroMemory(out_LargeSend, sizeof(NDIS_TASK_TCP_LARGE_SEND));
out_LargeSend->MaxOffLoadSize = TcpLargeSend->MaxOffLoadSize;
out_LargeSend->MinSegmentCount = TcpLargeSend->MinSegmentCount;
if (ai->ai_OffloadFlags & TCP_LARGE_SEND_TCPOPT_OFFLOAD) {
out_LargeSend->TcpOptions = 1;
}
if (ai->ai_OffloadFlags & TCP_LARGE_SEND_IPOPT_OFFLOAD) {
out_LargeSend->IpOptions = 1;
}
LastTaskOffload = NextTaskOffLoad;
NextTaskOffLoad = (PNDIS_TASK_OFFLOAD)
((PUCHAR) NextTaskOffLoad + NextTaskOffLoad->OffsetNextTask);
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_CRYPTO_ONLY) {
PNDIS_TASK_IPSEC pIPSecCaps = (PNDIS_TASK_IPSEC) & NextTaskOffLoad->TaskBuffer[0];
//
// plunk down the advertised capabilities
//
RtlZeroMemory(pIPSecCaps, sizeof(NDIS_TASK_IPSEC));
NextTaskOffLoad->Task = IpSecNdisTask;
NextTaskOffLoad->TaskBufferLength = sizeof(NDIS_TASK_IPSEC);
NextTaskOffLoad->OffsetNextTask = (FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + NextTaskOffLoad->TaskBufferLength);
TotalLength += NextTaskOffLoad->OffsetNextTask;
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_ESP) {
pIPSecCaps->Supported.AH_ESP_COMBINED = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_TPT_TUNNEL) {
pIPSecCaps->Supported.TRANSPORT_TUNNEL_COMBINED = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_V4_OPTIONS) {
pIPSecCaps->Supported.V4_OPTIONS = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_MD5) {
pIPSecCaps->V4AH.MD5 = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_SHA_1) {
pIPSecCaps->V4AH.SHA_1 = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_TPT) {
pIPSecCaps->V4AH.Transport = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_TUNNEL) {
pIPSecCaps->V4AH.Tunnel = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_XMT) {
pIPSecCaps->V4AH.Send = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_AH_RCV) {
pIPSecCaps->V4AH.Receive = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_DES) {
pIPSecCaps->V4ESP.DES = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_3_DES) {
pIPSecCaps->V4ESP.TRIPLE_DES = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_NONE) {
pIPSecCaps->V4ESP.NULL_ESP = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_TPT) {
pIPSecCaps->V4ESP.Transport = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_TUNNEL) {
pIPSecCaps->V4ESP.Tunnel = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_XMT) {
pIPSecCaps->V4ESP.Send = 1;
}
if (ai->ai_OffloadFlags & IPSEC_OFFLOAD_ESP_RCV) {
pIPSecCaps->V4ESP.Receive = 1;
}
LastTaskOffload = NextTaskOffLoad;
NextTaskOffLoad = (PNDIS_TASK_OFFLOAD)
((PUCHAR) NextTaskOffLoad + NextTaskOffLoad->OffsetNextTask);
}
LastTaskOffload->OffsetNextTask = 0;
// Okay, lets set this now.
Status = DoNDISRequest(ai, NdisRequestSetInformation,
OID_TCP_TASK_OFFLOAD, TaskOffloadHeader, TotalLength,
NULL, TRUE);
if (Status != NDIS_STATUS_SUCCESS) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Failed to enable indicated offload capabilities!!\n"));
ai->ai_OffloadFlags = 0;
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP: Failed set: %lx, status: %lx\n", ai->ai_OffloadFlags, Status));
}
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP: Enabling H/W capabilities: %lx\n", ai->ai_OffloadFlags));
return TRUE;
}
//**QueryOffload - Query offload capabilities
//
// Input:
// ai - ARPInterface for which we are initializing
// the task offload header.
// Returns:
// TRUE/FALSE - Success/Failure to query/set
//
BOOLEAN
QueryAndSetOffload(ARPInterface *ai)
{
PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader;
uint TotalLength;
NDIS_STATUS Status;
BOOLEAN stat;
uint Needed;
uchar *buffer;
// Query and set checksum capability
TaskOffloadHeader = CTEAllocMemNBoot(sizeof(NDIS_TASK_OFFLOAD_HEADER), '8ICT');
Status = STATUS_BUFFER_OVERFLOW;
if (TaskOffloadHeader) {
InitTaskOffloadHeader(ai, TaskOffloadHeader);
Status = DoNDISRequest(ai, NdisRequestQueryInformation,
OID_TCP_TASK_OFFLOAD, TaskOffloadHeader,
sizeof(NDIS_TASK_OFFLOAD_HEADER),
&Needed, TRUE);
// Need to initialize Needed to the real size of the buffer. The NDIS
// call may not init on success.
if (Status == NDIS_STATUS_SUCCESS) {
Needed = sizeof(NDIS_TASK_OFFLOAD_HEADER);
} else if ((Status == NDIS_STATUS_INVALID_LENGTH) ||
(Status == NDIS_STATUS_BUFFER_TOO_SHORT)) {
// We know the size we need. Allocate a buffer.
ASSERT(Needed >= sizeof(NDIS_TASK_OFFLOAD_HEADER));
buffer = CTEAllocMemNBoot(Needed, '9ICT');
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,
"Calling OID_TCP_TASK_OFFLOAD with %d bytes\n", Needed));
if (buffer != NULL) {
CTEFreeMem(TaskOffloadHeader);
TaskOffloadHeader = (PNDIS_TASK_OFFLOAD_HEADER) buffer;
InitTaskOffloadHeader(ai, TaskOffloadHeader);
Status = DoNDISRequest(ai, NdisRequestQueryInformation,
OID_TCP_TASK_OFFLOAD, buffer, Needed, NULL, TRUE);
}
}
}
if ((Status != NDIS_STATUS_SUCCESS)
|| (TaskOffloadHeader && TaskOffloadHeader->OffsetFirstTask == 0)) {
//Make sure that the flag is null.
ai->ai_OffloadFlags = 0;
if (TaskOffloadHeader) {
CTEFreeMem(TaskOffloadHeader);
}
return FALSE;
}
if (TaskOffloadHeader) {
stat = SetOffload(ai, TaskOffloadHeader, Needed);
CTEFreeMem(TaskOffloadHeader);
return stat;
}
return FALSE;
}
//** ARPRegister - Register a protocol with the ARP module.
//
// We register a protocol for ARP processing. We also open the
// NDIS adapter here.
//
// Note that much of the information passed in here is unused, as
// ARP currently only works with IP.
//
// Entry:
// Adapter - Name of the adapter to bind to.
// IPContext - Value to be passed to IP on upcalls.
//
int
ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface)
{
ARPInterface *ai; // Pointer to interface struct. for this interface.
NDIS_STATUS Status, OpenStatus; // Status values.
uint i = 0; // Medium index.
NDIS_MEDIUM MediaArray[MAX_MEDIA];
uchar *buffer; // Pointer to our buffers.
uint mss;
uint speed;
uint Needed;
uint MacOpts;
uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize;
uint OID;
uint PF;
PNDIS_BUFFER Buffer;
TRANSPORT_HEADER_OFFSET IPHdrOffset;
CTELockHandle LockHandle;
UINT MediaType;
NDIS_STRING NdisString;
DEBUGMSG(DBG_TRACE && DBG_PNP,
(DTEXT("+ARPRegister(%x, %x, %x)\n"),
Adapter, Flags, Interface));
if ((ai = CTEAllocMemNBoot(sizeof(ARPInterface), '4ICT')) == (ARPInterface *) NULL)
return FALSE; // Couldn't allocate memory for this one.
*Interface = ai;
RtlZeroMemory(ai, sizeof(ARPInterface));
CTEInitTimer(&ai->ai_timer);
ai->ai_timerstarted = FALSE;
ai->ai_stoptimer = FALSE;
MediaArray[MEDIA_DIX] = NdisMedium802_3;
MediaArray[MEDIA_TR] = NdisMedium802_5;
MediaArray[MEDIA_FDDI] = NdisMediumFddi;
MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2;
// Initialize this adapter interface structure.
ai->ai_state = INTERFACE_INIT;
ai->ai_adminstate = IF_STATUS_DOWN;
ai->ai_operstate = IF_OPER_STATUS_NON_OPERATIONAL;
ai->ai_lastchange = GetTimeTicks();
ai->ai_bcast = IP_LOCAL_BCST;
ai->ai_atinst = ai->ai_ifinst = INVALID_ENTITY_INSTANCE;
ai->ai_telladdrchng = 1; //Initially let us do try to do network layer address stuff
// Initialize the locks.
CTEInitLock(&ai->ai_lock);
CTEInitLock(&ai->ai_ARPTblLock);
GetAlwaysSourceRoute(&sArpAlwaysSourceRoute, &sIPAlwaysSourceRoute);
ArpCacheLife = GetArpCacheLife();
if (!ArpCacheLife) {
ArpCacheLife = 1;
}
ArpCacheLife = (ArpCacheLife * 1000L) / ARP_TIMER_TIME;
ArpRetryCount = GetArpRetryCount();
if (!ArpMinValidCacheLife) {
ArpMinValidCacheLife = 1;
}
// Allocate the buffer and packet pools.
NdisAllocatePacketPoolEx(&Status, &ai->ai_ppool,
ARP_DEFAULT_PACKETS, ARP_DEFAULT_PACKETS * 1000,
sizeof(struct PCCommon));
if (Status != NDIS_STATUS_SUCCESS) {
FreeARPInterface(ai);
return FALSE;
}
// Allocate the ARP table
ai->ai_ARPTbl = (ARPTable *) CTEAllocMemNBoot(ARP_TABLE_SIZE * sizeof(ARPTableEntry*), '5ICT');
if (ai->ai_ARPTbl == (ARPTable *) NULL) {
FreeARPInterface(ai);
return FALSE;
}
//
// NULL out the pointers
//
RtlZeroMemory(ai->ai_ARPTbl, ARP_TABLE_SIZE * sizeof(ARPTableEntry *));
CTEInitBlockStruc(&ai->ai_block);
DEBUGMSG(DBG_INFO && DBG_PNP,
(DTEXT("ARPRegister calling NdisOpenAdapter\n")));
// Open the NDIS adapter.
NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray,
MAX_MEDIA, ARPHandle, ai, Adapter, 0, NULL);
// Block for open to complete.
if (Status == NDIS_STATUS_PENDING)
Status = (NDIS_STATUS) CTEBlock(&ai->ai_block);
ai->ai_media = MediaArray[i]; // Fill in media type.
// Open adapter completed. If it succeeded, we'll finish our intialization.
// If it failed, bail out now.
if (Status != NDIS_STATUS_SUCCESS) {
ai->ai_handle = NULL;
FreeARPInterface(ai);
return FALSE;
}
#if FFP_SUPPORT
// Store NIC driver handle
NdisGetDriverHandle(ai->ai_handle, &ai->ai_driver);
#endif
// Read the local address.
switch (ai->ai_media) {
case NdisMedium802_3:
addrlen = ARP_802_ADDR_LENGTH;
bcastmask = ENET_BCAST_MASK;
bcastval = ENET_BCAST_VAL;
bcastoff = ENET_BCAST_OFF;
OID = OID_802_3_CURRENT_ADDRESS;
hdrsize = sizeof(ENetHeader);
if (!UseEtherSNAP(Adapter)) {
snapsize = 0;
} else {
snapsize = sizeof(SNAPHeader);
}
PF = NDIS_PACKET_TYPE_BROADCAST | \
NDIS_PACKET_TYPE_DIRECTED | \
NDIS_PACKET_TYPE_MULTICAST;
ai->ai_mediatype = IF_TYPE_IS088023_CSMACD;
break;
case NdisMedium802_5:
addrlen = ARP_802_ADDR_LENGTH;
bcastmask = TR_BCAST_MASK;
bcastval = TR_BCAST_VAL;
bcastoff = TR_BCAST_OFF;
OID = OID_802_5_CURRENT_ADDRESS;
hdrsize = sizeof(TRHeader);
snapsize = sizeof(SNAPHeader);
PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
ai->ai_mediatype = IF_TYPE_ISO88025_TOKENRING;
break;
case NdisMediumFddi:
addrlen = ARP_802_ADDR_LENGTH;
bcastmask = FDDI_BCAST_MASK;
bcastval = FDDI_BCAST_VAL;
bcastoff = FDDI_BCAST_OFF;
OID = OID_FDDI_LONG_CURRENT_ADDR;
hdrsize = sizeof(FDDIHeader);
snapsize = sizeof(SNAPHeader);
PF = NDIS_PACKET_TYPE_BROADCAST | \
NDIS_PACKET_TYPE_DIRECTED | \
NDIS_PACKET_TYPE_MULTICAST;
ai->ai_mediatype = IF_TYPE_FDDI;
break;
case NdisMediumArcnet878_2:
addrlen = 1;
bcastmask = ARC_BCAST_MASK;
bcastval = ARC_BCAST_VAL;
bcastoff = ARC_BCAST_OFF;
OID = OID_ARCNET_CURRENT_ADDRESS;
hdrsize = sizeof(ARCNetHeader);
snapsize = 0;
PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
ai->ai_mediatype = IF_TYPE_ARCNET;
break;
default:
ASSERT(0);
FreeARPInterface(ai);
return FALSE;
}
ai->ai_bcastmask = bcastmask;
ai->ai_bcastval = bcastval;
ai->ai_bcastoff = bcastoff;
ai->ai_addrlen = addrlen;
ai->ai_hdrsize = hdrsize;
ai->ai_snapsize = snapsize;
ai->ai_pfilter = PF;
Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID,
ai->ai_addr, addrlen, NULL, TRUE);
if (Status != NDIS_STATUS_SUCCESS) {
FreeARPInterface(ai);
return FALSE;
}
// Read the maximum frame size.
if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL, TRUE)) != NDIS_STATUS_SUCCESS) {
FreeARPInterface(ai);
return FALSE;
}
// If this is token ring, figure out the RC len stuff now.
mss -= (uint) ai->ai_snapsize;
if (ai->ai_media == NdisMedium802_5) {
mss -= (sizeof(RC) + (ARP_MAX_RD * sizeof(ushort)));
} else {
if (ai->ai_media == NdisMediumFddi) {
mss = MIN(mss, ARP_FDDI_MSS);
}
}
ai->ai_mtu = (ushort) mss;
// Read the speed for local purposes.
if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL, TRUE)) == NDIS_STATUS_SUCCESS) {
ai->ai_speed = speed * 100L;
}
// Read and save the options.
Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS,
&MacOpts, sizeof(MacOpts), NULL, TRUE);
if (Status != NDIS_STATUS_SUCCESS) {
*Flags = 0;
} else {
*Flags = (MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0;
}
if (CTEMemCmp(ai->ai_addr, PPP_HW_ADDR, PPP_HW_ADDR_LEN) == 0) {
*Flags = *Flags | LIP_P2P_FLAG;
}
//
// Query the media capability to determine if it is a uni-directional adapter.
//
Status = DoNDISRequest(
ai,
NdisRequestQueryInformation,
OID_GEN_MEDIA_CAPABILITIES,
&MediaType,
sizeof(MediaType),
NULL,
TRUE); // Blocking.
if (Status == NDIS_STATUS_SUCCESS) {
// Bit field of Rx and Tx. If only Rx, set uni flag.
if (MediaType == NDIS_MEDIA_CAP_RECEIVE) {
DEBUGMSG(DBG_WARN,
(DTEXT("ARPRegister: ai %x: MEDIA_CAP_RX -> UniAdapter!!\n"), ai));
*Flags |= LIP_UNI_FLAG;
InterlockedIncrement(&cUniAdapters);
}
}
// Read and store the vendor description string.
Status = NdisQueryAdapterInstanceName(&NdisString, ai->ai_handle);
if (Status == NDIS_STATUS_SUCCESS) {
ANSI_STRING AnsiString;
// Convert the string to ANSI, and use the new ANSI string's buffer
// to store the description in the ARP interface.
// N.B. The conversion results in a nul-terminated string.
Status = RtlUnicodeStringToAnsiString(&AnsiString, &NdisString, TRUE);
if (Status == STATUS_SUCCESS) {
ai->ai_desc = AnsiString.Buffer;
ai->ai_desclen = strlen(AnsiString.Buffer) + 1;
}
NdisFreeString(NdisString);
}
if (!ArpEnetHeaderPool || !ArpAuxHeaderPool) {
PVOID SectionHandle;
// Allocate our small and big buffer pools. Take the interface list
// lock simply to protect creating of the buffer pools if we haven't
// already done so. We could have used our own lock, but the interface
// list lock is global, and not already used in this path.
//
// This routine is in pageable memory. Since getting the lock
// requires writable access to LockHandle at DISPATCH, we need to
// lock this code in.
//
SectionHandle = MmLockPagableCodeSection(ARPRegister);
CTEGetLock(&ArpInterfaceListLock.Lock, &LockHandle);
if (!ArpEnetHeaderPool) {
ArpEnetHeaderPool = MdpCreatePool(BUFSIZE_ENET_HEADER_POOL, 'ehCT');
}
if (!ArpAuxHeaderPool) {
ArpAuxHeaderPool = MdpCreatePool(BUFSIZE_AUX_HEADER_POOL, 'ahCT');
}
CTEFreeLock(&ArpInterfaceListLock.Lock, LockHandle);
MmUnlockPagableImageSection(SectionHandle);
if (!ArpAuxHeaderPool || !ArpEnetHeaderPool) {
FreeARPInterface(ai);
return FALSE;
}
}
ai->ai_promiscuous = 0;
#if FFP_SUPPORT
{
FFPVersionParams Version =
{
NDIS_PROTOCOL_ID_TCP_IP, 0
};
// Initialize all FFP Handling Variables
ai->ai_ffpversion = 0;
ai->ai_ffplastflush = 0;
// Query FFP Handling capabilities
Status = DoNDISRequest(ai, NdisRequestQueryInformation,
OID_FFP_SUPPORT, &Version, sizeof(FFPVersionParams), NULL, TRUE);
TCPTRACE(("Querying FFP capabilities: Status = %08x, Version = %lu\n",
Status,
Version.FFPVersion));
// Non-Zero Value indicates FFP support
if (Version.FFPVersion) {
// Set the FFP startup parameters
FFPSupportParams Info =
{
NDIS_PROTOCOL_ID_TCP_IP,
FFPRegFastForwardingCacheSize,
FFPRegControlFlags
};
// But store away the version first
ai->ai_ffpversion = Version.FFPVersion;
DoNDISRequest(ai, NdisRequestSetInformation,
OID_FFP_SUPPORT, &Info, sizeof(FFPSupportParams), NULL, TRUE);
TCPTRACE(("Setting FFP capabilities: Cache Size = %lu, Flags = %08x\n",
Info.FastForwardingCacheSize,
Info.FFPControlFlags));
}
}
#endif // if FFP_SUPPORT
ai->ai_OffloadFlags = 0;
if (DisableTaskOffload) {
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Taskoffload disabled\n"));
} else {
if(!QueryAndSetOffload(ai)){
DEBUGMSG(DBG_ERROR, (DTEXT("ARP: Query and set offload failed.\n")));
}
}
// query the wakeup capabilities.
Status = DoNDISRequest(
ai,
NdisRequestQueryInformation,
OID_PNP_CAPABILITIES,
&ai->ai_wakeupcap,
sizeof(NDIS_PNP_CAPABILITIES),
NULL, TRUE);
if (Status == NDIS_STATUS_SUCCESS) {
uint wakeup = NDIS_PNP_WAKE_UP_PATTERN_MATCH;
// enable wakeup capabilities.
Status = DoNDISRequest(
ai,
NdisRequestSetInformation,
OID_PNP_ENABLE_WAKE_UP,
&wakeup,
sizeof(wakeup),
NULL, TRUE);
if (Status != NDIS_STATUS_SUCCESS) {
ai->ai_wakeupcap.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified;
}
}
// Store the device name, we need to pass this to our TDI clients when
// we do the PNP notification.
if ((ai->ai_devicename.Buffer = CTEAllocMemNBoot(Adapter->MaximumLength, 'aICT')) == NULL) {
FreeARPInterface(ai);
return FALSE;
}
RtlCopyMemory(ai->ai_devicename.Buffer, Adapter->Buffer, Adapter->MaximumLength);
ai->ai_devicename.Length = Adapter->Length;
ai->ai_devicename.MaximumLength = Adapter->MaximumLength;
ai->ai_timerstarted = TRUE;
IPHdrOffset.HeaderOffset = ai->ai_snapsize + ai->ai_hdrsize;
IPHdrOffset.ProtocolType = NDIS_PROTOCOL_ID_TCP_IP;
Status = DoNDISRequest(ai, NdisRequestSetInformation, OID_GEN_TRANSPORT_HEADER_OFFSET,
&IPHdrOffset, sizeof(TRANSPORT_HEADER_OFFSET), NULL, TRUE);
// Everything's set up, so get the ARP timer running.
CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai);
return TRUE;
}
#pragma END_INIT
//* ARPDynRegister - Dynamically register IP.
//
// Called by IP when he's about done binding to register with us. Since we
// call him directly, we don't save his info here. We do keep his context
// and index number.
//
// Input: See ARPRegister
//
// Returns: Nothing.
//
int
__stdcall
ARPDynRegister(
IN PNDIS_STRING Adapter,
IN void *IPContext,
IN struct _IP_HANDLERS *IpHandlers,
OUT struct LLIPBindInfo *Info,
IN uint NumIFBound)
{
ARPInterface *Interface = (ARPInterface *) Info->lip_context;
Interface->ai_context = IPContext;
Interface->ai_index = NumIFBound;
// TCPTRACE(("Arp Interface %lx ai_context %lx ai_index %lx\n",Interface, Interface->ai_context, Interface->ai_index));
return TRUE;
}
//* ARPBindAdapter - Bind and initialize an adapter.
//
// Called in a PNP environment to initialize and bind an adapter. We open
// the adapter and get it running, and then we call up to IP to tell him
// about it. IP will initialize, and if all goes well call us back to start
// receiving.
//
// Input: RetStatus - Where to return the status of this call.
// BindContext - Handle to use for calling BindAdapterComplete.
// AdapterName - Pointer to name of adapter.
// SS1 - System specific 1 parameter.
// SS2 - System specific 2 parameter.
//
// Returns: Nothing.
//
void NDIS_API
ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext,
PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2)
{
uint Flags; // MAC binding flags.
ARPInterface *Interface; // Newly created interface.
PNDIS_STRING ConfigName; // Name used by IP for config. info.
IP_STATUS Status; // State of IPAddInterface call.
LLIPBindInfo BindInfo; // Binding information for IP.
NDIS_HANDLE Handle;
NDIS_STRING IPConfigName;
DEBUGMSG(DBG_TRACE && DBG_PNP,
(DTEXT("+ARPBindAdapter(%x, %x, %x, %x, %x)\n"),
RetStatus, BindContext, AdapterName, SS1, SS2));
if (!OpenIFConfig(SS1, &Handle)) {
*RetStatus = NDIS_STATUS_FAILURE;
DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: Open failure\n")));
DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus));
return;
}
#if !MILLEN
if ((*RetStatus = GetIPConfigValue(Handle, &IPConfigName)) != NDIS_STATUS_SUCCESS) {
CloseIFConfig(Handle);
DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: GetIPConfigValue failure\n")));
DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus));
return;
}
#endif // !MILLEN
CloseIFConfig(Handle);
// First, open the adapter and get the info.
if (!ARPRegister(AdapterName, &Flags, &Interface)) {
#if !MILLEN
CTEFreeMem(IPConfigName.Buffer);
#endif // !MILLEN
*RetStatus = NDIS_STATUS_FAILURE;
DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: ARPRegister failure\n")));
DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus));
return;
}
// OK, we're opened the adapter. Call IP to tell him about it.
BindInfo.lip_context = Interface;
BindInfo.lip_transmit = ARPTransmit;
BindInfo.lip_transfer = ARPXferData;
BindInfo.lip_close = ARPClose;
BindInfo.lip_addaddr = ARPAddAddr;
BindInfo.lip_deladdr = ARPDeleteAddr;
BindInfo.lip_invalidate = ARPInvalidate;
BindInfo.lip_open = ARPOpen;
BindInfo.lip_qinfo = ARPQueryInfo;
BindInfo.lip_setinfo = ARPSetInfo;
BindInfo.lip_getelist = ARPGetEList;
BindInfo.lip_dondisreq = DoNDISRequest;
BindInfo.lip_mss = Interface->ai_mtu;
BindInfo.lip_speed = Interface->ai_speed;
BindInfo.lip_flags = Flags;
BindInfo.lip_addrlen = Interface->ai_addrlen;
BindInfo.lip_addr = Interface->ai_addr;
BindInfo.lip_dowakeupptrn = DoWakeupPattern;
BindInfo.lip_pnpcomplete = ARPPnPComplete;
BindInfo.lip_setndisrequest = ARPSetNdisRequest;
BindInfo.lip_arpresolveip = ARPResolveIP;
BindInfo.lip_arpflushate = ARPFlushATE;
BindInfo.lip_arpflushallate = ARPFlushAllATE;
#if !MILLEN
BindInfo.lip_cancelpackets = ARPCancelPackets;
#endif
#if FFP_SUPPORT
// NDIS Driver Handle, FFP Version are passed up
// [ Non zero version implies FFP Support exists ]
BindInfo.lip_ffpversion = Interface->ai_ffpversion;
BindInfo.lip_ffpdriver = (ULONG_PTR) Interface->ai_driver;
#endif
//Interface capability is passed on to IP via BindInfo
BindInfo.lip_OffloadFlags = Interface->ai_OffloadFlags;
BindInfo.lip_MaxOffLoadSize = (uint) Interface->ai_TcpLargeSend.MaxOffLoadSize;
BindInfo.lip_MaxSegments = (uint) Interface->ai_TcpLargeSend.MinSegmentCount;
BindInfo.lip_closelink = NULL;
BindInfo.lip_pnpcap = Interface->ai_wakeupcap.Flags;
DEBUGMSG(DBG_INFO && DBG_PNP,
(DTEXT("ARPBindAdapter calling IPAddInterface.\n")));
Status = IPAddInterface(AdapterName,
NULL,
#if MILLEN
(PNDIS_STRING) SS1,
#else // MILLEN
(PNDIS_STRING) & IPConfigName,
#endif // !MILLEN
SS2,
Interface,
ARPDynRegister,
&BindInfo,
0,
Interface->ai_mediatype,
IF_ACCESS_BROADCAST,
IF_CONNECTION_DEDICATED);
#if !MILLEN
CTEFreeMem(IPConfigName.Buffer);
#endif // !MILLEN
if (Status != IP_SUCCESS) {
// Need to close the binding. FreeARPInterface will do that, as well
// as freeing resources.
DEBUGMSG(DBG_ERROR && DBG_PNP,
(DTEXT("ARPBindAdapter: IPAddInterface failure %x\n"), Status));
FreeARPInterface(Interface);
*RetStatus = NDIS_STATUS_FAILURE;
} else {
//
// Insert into ARP IF list
//
ExInterlockedInsertTailList(&ArpInterfaceList,
&Interface->ai_linkage,
&ArpInterfaceListLock.Lock);
*RetStatus = NDIS_STATUS_SUCCESS;
}
DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus));
}
//* ARPUnbindAdapter - Unbind from an adapter.
//
// Called when we need to unbind from an adapter. We'll call up to IP to tell
// him. When he's done, we'll free our memory and return.
//
// Input: RetStatus - Where to return status from call.
// ProtBindContext - The context we gave NDIS earlier - really a
// pointer to an ARPInterface structure.
// UnbindContext - Context for completeing this request.
//
// Returns: Nothing.
//
void NDIS_API
ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext,
NDIS_HANDLE UnbindContext)
{
ARPInterface *Interface = (ARPInterface *) ProtBindContext;
NDIS_STATUS Status; // Status of close call.
CTELockHandle LockHandle;
// Shut him up, so we don't get any more frames.
Interface->ai_pfilter = 0;
if (Interface->ai_handle != NULL) {
DoNDISRequest(Interface, NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint),
NULL, TRUE);
}
CTEInitBlockStrucEx(&Interface->ai_timerblock);
Interface->ai_stoptimer = TRUE;
// Mark him as down.
Interface->ai_state = INTERFACE_DOWN;
Interface->ai_adminstate = IF_STATUS_DOWN;
#if FFP_SUPPORT
// Stop FFP on this interface
Interface->ai_ffpversion = 0;
#endif
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Flushing all ates %x\n", Interface));
ARPFlushAllATE(Interface);
// Now tell IP he's gone. We need to make sure that we don't tell him twice.
// To do this we set the context to NULL after we tell him the first time,
// and we check to make sure it's non-NULL before notifying him.
if (Interface->ai_context != NULL) {
IPDelInterface(Interface->ai_context, TRUE);
Interface->ai_context = NULL;
}
// Finally, close him. We do this here so we can return a valid status.
CTEGetLock(&Interface->ai_lock, &LockHandle);
if (Interface->ai_handle != NULL) {
NDIS_HANDLE Handle = Interface->ai_handle;
CTEFreeLock(&Interface->ai_lock, LockHandle);
CTEInitBlockStruc(&Interface->ai_block);
NdisCloseAdapter(&Status, Handle);
// Block for close to complete.
if (Status == NDIS_STATUS_PENDING) {
Status = (NDIS_STATUS) CTEBlock(&Interface->ai_block);
}
Interface->ai_handle = NULL;
} else {
CTEFreeLock(&Interface->ai_lock, LockHandle);
Status = NDIS_STATUS_SUCCESS;
}
//Check if are called from ARPUnload
if ((ARPInterface *) UnbindContext != Interface) {
CTELockHandle Handle;
//No. Acquire lock and remove entry.
CTEGetLock(&ArpInterfaceListLock.Lock, &Handle);
RemoveEntryList(&Interface->ai_linkage);
CTEFreeLock(&ArpInterfaceListLock.Lock, Handle);
}
*RetStatus = Status;
if (Status == NDIS_STATUS_SUCCESS) {
FreeARPInterface(Interface);
}
}
extern ulong VIPTerminate;
//* ARPUnloadProtocol - Unload.
//
// Called when we need to unload. All we do is call up to IP, and return.
//
// Input: Nothing.
//
// Returns: Nothing.
//
void NDIS_API
ARPUnloadProtocol(void)
{
NDIS_STATUS Status;
#if MILLEN
DEBUGMSG(1, (DTEXT("ARPUnloadProtocol called! What to do???\n")));
#endif // MILLEN
}
VOID
ArpUnload(IN PDRIVER_OBJECT DriverObject)
/*++
Routine Description:
This routine unloads the TCPIP stack.
It unbinds from any NDIS drivers that are open and frees all resources
associated with the transport. The I/O system will not call us until
nobody above has IPX open.
NOTE: Also, since other ARP modules depend on IP, they are unloaded before
out unload handler is called. We concern ourselves with the LAN arp
only at this point
Arguments:
DriverObject - Pointer to driver object created by the system.
Return Value:
None. When the function returns, the driver is unloaded.
--*/
{
PLIST_ENTRY pEntry;
CTELockHandle LockHandle;
NTSTATUS status;
ARPInterface *Interface;
//
// Walk the list of opened ARP interfaces, issuing
// PnP deletes on each in turn.
//
CTEGetLock(&ArpInterfaceListLock.Lock, &LockHandle);
while(!IsListEmpty(&ArpInterfaceList)) {
pEntry = ArpInterfaceList.Flink;
Interface = STRUCT_OF(ARPInterface, pEntry, ai_linkage);
RemoveEntryList(&Interface->ai_linkage);
CTEFreeLock(&ArpInterfaceListLock.Lock, LockHandle);
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Issuing unbind on %lx\n", Interface));
ARPUnbindAdapter(&status, Interface, Interface);
CTEGetLock(&ArpInterfaceListLock.Lock, &LockHandle);
}
CTEFreeLock(&ArpInterfaceListLock.Lock, LockHandle);
MdpDestroyPool(ArpEnetHeaderPool);
MdpDestroyPool(ArpAuxHeaderPool);
//
// Deal with any residual events/timers
// Only one timer sits at this layer: ai_timer, which is stopped
// on the unbind above.
//
//
// call into IP so it can cleanup.
//
IPUnload(DriverObject);
}