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.
7768 lines
281 KiB
7768 lines
281 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 = 0;
|
|
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_adminstate == INTERFACE_DOWN) ||
|
|
(Adapter->ai_handle == NULL)) {
|
|
return NDIS_STATUS_ADAPTER_NOT_READY;
|
|
}
|
|
|
|
// 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) {
|
|
CTEBlockTracker Tracker;
|
|
|
|
Status = (NDIS_STATUS) CTEBlockWithTracker(&pReqBlock->Block,
|
|
&Tracker, Adapter);
|
|
|
|
#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( (PLONG) &pReqBlock->RefCount);
|
|
}
|
|
|
|
if (Needed != NULL)
|
|
*Needed = pReqBlock->Request.DATA.QUERY_INFORMATION.BytesNeeded;
|
|
|
|
if (InterlockedDecrement( (PLONG) &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)
|
|
{
|
|
UNREFERENCED_PARAMETER(Interface);
|
|
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;
|
|
UNREFERENCED_PARAMETER(Interface);
|
|
|
|
|
|
#if DBG
|
|
*BufPtr = NULL;
|
|
#endif
|
|
|
|
UNREFERENCED_PARAMETER(Interface);
|
|
|
|
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;
|
|
ulong Proc;
|
|
|
|
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_operstatus == INTERFACE_UP)) {
|
|
|
|
Proc = KeGetCurrentProcessorNumber();
|
|
Interface->ai_qlen[Proc].ai_qlen++;
|
|
|
|
NdisSend(&Status, Interface->ai_handle, Packet);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
PacketDone = TRUE;
|
|
Interface->ai_qlen[Proc].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 = Size + (uchar) Interface->ai_snapsize;
|
|
|
|
if (Interface->ai_media == NdisMedium802_5)
|
|
Size = 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;
|
|
DummyAC->ac_next = 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.
|
|
//
|
|
// Returns: Pointer to ARPTableEntry if found, or NULL if not.
|
|
//
|
|
ARPTableEntry *
|
|
ARPLookup(ARPInterface * Interface, IPAddr Address)
|
|
{
|
|
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);
|
|
if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match.
|
|
return Current;
|
|
}
|
|
CTEFreeLockFromDPC(&Current->ate_lock);
|
|
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;
|
|
ulong Proc;
|
|
|
|
// 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_operstatus == 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])++;
|
|
Proc = KeGetCurrentProcessorNumber();
|
|
Interface->ai_qlen[Proc].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++;
|
|
}
|
|
|
|
Proc = KeGetCurrentProcessorNumber();
|
|
Interface->ai_qlen[Proc].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 Handle; // Lock handle
|
|
NDIS_STATUS Status;
|
|
uchar ate_state;
|
|
|
|
CTEGetLock(&ai->ai_ARPTblLock, &Handle);
|
|
|
|
// Check if we already got the mapping.
|
|
|
|
if ((entry = ARPLookup(ai, Destination)) != 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);
|
|
CTEFreeLock(&ai->ai_ARPTblLock, Handle);
|
|
return Status;
|
|
}
|
|
// We need to send arp request.
|
|
|
|
CTEFreeLock(&ai->ai_ARPTblLock, Handle);
|
|
|
|
entry = CreateARPTableEntry(ai, Destination, &Handle, 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, Handle);
|
|
|
|
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, Handle);
|
|
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, Handle);
|
|
return Status;
|
|
|
|
} else { // An invalid entry!
|
|
CTEFreeLock(&entry->ate_lock, Handle);
|
|
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 = NULL; // Address of NDIS buffer
|
|
NDIS_STATUS Status; // Status of send.
|
|
ulong Proc;
|
|
|
|
#if BACK_FILL
|
|
PMDL TmpMdl = NULL;
|
|
#endif
|
|
|
|
if (Interface->ai_operstatus == 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) {
|
|
|
|
TmpMdl->MappedSystemVa = (PVOID) (((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])++;
|
|
Proc = KeGetCurrentProcessorNumber();
|
|
Interface->ai_qlen[Proc].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++;
|
|
}
|
|
|
|
Proc = KeGetCurrentProcessorNumber();
|
|
Interface->ai_qlen[Proc].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;
|
|
|
|
TmpMdl->MappedSystemVa = (PVOID) (((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)) != NULL) {
|
|
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock);
|
|
*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);
|
|
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock);
|
|
*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 = NULL; // ARP context pointer.
|
|
ARPTableEntry *entry; // Pointer to ARP tbl. entry
|
|
CTELockHandle Handle; // Lock handle
|
|
NDIS_STATUS Status;
|
|
PNDIS_PACKET Packet = *PacketArray;
|
|
|
|
//
|
|
// For now, we get only one packet...
|
|
//
|
|
DBG_UNREFERENCED_PARAMETER(NumberOfPackets);
|
|
UNREFERENCED_PARAMETER(LinkCtxt);
|
|
|
|
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_operstatus != INTERFACE_UP) {
|
|
return NDIS_STATUS_ADAPTER_NOT_READY;
|
|
}
|
|
|
|
CTEGetLock(&ai->ai_ARPTblLock, &Handle);
|
|
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); // 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);
|
|
status = ARPSendData(ai, Packet, entry, Handle); // Send the data
|
|
if (refresh) {
|
|
if (sArpAlwaysSourceRoute) {
|
|
//
|
|
// If Always source route is on,
|
|
// the state should ve resolving_global
|
|
// so that SendArpRequest will send SR
|
|
// header in case of 802.5.
|
|
//
|
|
state = ARP_RESOLVING_GLOBAL;
|
|
}
|
|
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);
|
|
// 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)) != (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);
|
|
return ARPSendData(ai, Packet, entry, Handle);
|
|
}
|
|
|
|
// 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, Handle);
|
|
|
|
if (ARPSendBCast(ai, Destination, Packet, &Status)) {
|
|
return Status;
|
|
}
|
|
|
|
entry = CreateARPTableEntry(ai, Destination, &Handle, 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, Handle);
|
|
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, Handle);
|
|
}
|
|
} else if (entry->ate_state == ARP_GOOD) { // Yow! A valid entry.
|
|
return ARPSendData(ai, Packet, entry, Handle);
|
|
} else { // An invalid entry!
|
|
CTEFreeLock(&entry->ate_lock, Handle);
|
|
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;
|
|
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 = (ULONG) STATUS_UNSUCCESSFUL;
|
|
TmpArpContB = ArpContB->next;
|
|
(*rtn) (ArpContB, (IP_STATUS) 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 = (ULONG) STATUS_UNSUCCESSFUL;
|
|
TmpArpContB = ArpContB->next;
|
|
(*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL);
|
|
ArpContB = TmpArpContB;
|
|
}
|
|
|
|
ATE->ate_resolveonly = NULL;
|
|
|
|
}
|
|
|
|
// Acquire ate_lock to ensure exclusive access to the ATE.
|
|
CTEGetLockAtDPC(&ATE->ate_lock);
|
|
RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]), ate_next),
|
|
ATE);
|
|
CTEFreeLockFromDPC(&ATE->ate_lock);
|
|
|
|
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;
|
|
}
|
|
|
|
//* ARPUpdateOperStatus - Update the OperStatus and LastChange values.
|
|
//
|
|
// Called whenever ai_adminstate or ai_mediastatus changes, to preserve
|
|
// the invariant that ai_operstatus should only be up if the admin
|
|
// status is up and media sense is present.
|
|
//
|
|
// Entry: Interface - Interface to update.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
__inline
|
|
void
|
|
ARPUpdateOperStatus(ARPInterface *Interface)
|
|
{
|
|
uchar NewOperStatus;
|
|
|
|
if (Interface->ai_operstatus == INTERFACE_UNINIT) {
|
|
return;
|
|
}
|
|
|
|
if ((Interface->ai_adminstate == IF_STATUS_DOWN) ||
|
|
(Interface->ai_mediastatus == FALSE)) {
|
|
NewOperStatus = INTERFACE_DOWN;
|
|
} else {
|
|
NewOperStatus = INTERFACE_UP;
|
|
}
|
|
|
|
if (Interface->ai_operstatus != NewOperStatus) {
|
|
Interface->ai_operstatus = NewOperStatus;
|
|
Interface->ai_lastchange = GetTimeTicks();
|
|
}
|
|
}
|
|
|
|
//* 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_mediastatus = FALSE;
|
|
ARPUpdateOperStatus(Interface);
|
|
|
|
//
|
|
// Mark the interface as going away so it will disappear from the
|
|
// entity list.
|
|
//
|
|
Interface->ai_operstatus = INTERFACE_UNINIT;
|
|
|
|
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;
|
|
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);
|
|
|
|
if (ATE != NULL) {
|
|
tmpATE = ATE;
|
|
while (ATE) {
|
|
if (ATE->ate_rce == RCE) {
|
|
DbgBreakPoint();
|
|
}
|
|
ATE = ATE->ate_next;
|
|
}
|
|
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock);
|
|
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);
|
|
ARPRemoveRCE(ATE, RCE);
|
|
RtlZeroMemory(RCE->rce_context, RCE_CONTEXT_SIZE);
|
|
CTEFreeLockFromDPC(&Interface->ai_ARPTblLock);
|
|
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 = NDIS_STATUS_SUCCESS;
|
|
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_operstatus != 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_operstatus == 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_operstatus == 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;
|
|
SetAddrControl *SAC;
|
|
AddAddrNotifyEvent *DelayedEvent;
|
|
|
|
if (Type == LLIP_ADDR_LOCAL) {
|
|
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
|
|
if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) {
|
|
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;
|
|
if (DelAddr->aia_next &&
|
|
DelAddr->aia_next->aia_age == ARPADDR_MARKER) {
|
|
DelAddr->aia_next->aia_context = (PVOID) PrevAddr;
|
|
}
|
|
Interface->ai_ipaddrcnt--;
|
|
|
|
if (Interface->ai_telladdrchng) {
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
AddrNotifyLink(Interface);
|
|
} else {
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
}
|
|
if (DelAddr->aia_context != NULL) {
|
|
SAC = (SetAddrControl *) DelAddr->aia_context;
|
|
DelAddr->aia_context = NULL;
|
|
DelayedEvent = CTEAllocMemNBoot(sizeof(AddAddrNotifyEvent), 'ViCT');
|
|
if (DelayedEvent) {
|
|
DelayedEvent->SAC = SAC;
|
|
DelayedEvent->Address = DelAddr->aia_addr;
|
|
DelayedEvent->Status = IP_SUCCESS;
|
|
CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed);
|
|
CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent);
|
|
} else {
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
CTEFreeMem(DelAddr);
|
|
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;
|
|
|
|
//
|
|
// Skip if this is a Marker.
|
|
//
|
|
if (addrlist->aia_age != ARPADDR_MARKER) {
|
|
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--;
|
|
i++;
|
|
}
|
|
addrlist = addrlist->aia_next;
|
|
}
|
|
|
|
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;
|
|
|
|
Buffer = CTEAllocMem(PtrnBufferLen);
|
|
|
|
if (Buffer) {
|
|
|
|
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;
|
|
IPAddr Address;
|
|
IP_STATUS Status;
|
|
|
|
UNREFERENCED_PARAMETER(WorkerThreadEvent);
|
|
|
|
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( (PLONG)
|
|
&((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;
|
|
uchar Deleted;
|
|
PNDIS_PACKET PList = (PNDIS_PACKET) NULL;
|
|
ARPIPAddr *Addr;
|
|
ARPIPAddr Marker;
|
|
|
|
|
|
UNREFERENCED_PARAMETER(Timer);
|
|
|
|
// 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;
|
|
|
|
//
|
|
// Marker is used to track the next addr to be processed
|
|
// in the ipaddr list. Initialize it so that its aia_age is
|
|
// OLD_LOCAL, and aia_addr is NULL_IP_ADDR. The places where
|
|
// address list is scanned will skip ARPIPAddr with NULL_IP_ADDR
|
|
// and in ARPDeleteAddr, if an addr element before the marker is
|
|
// removed, Marker's aia_context will be changed to its prev element.
|
|
//
|
|
Marker.aia_addr = NULL_IP_ADDR;
|
|
do {
|
|
|
|
|
|
if (Addr->aia_age != ARPADDR_OLD_LOCAL) {
|
|
IPAddr IpAddress;
|
|
(Addr->aia_age)--;
|
|
|
|
//
|
|
// Insert Marker after this Addr
|
|
// Marker's aia_context is used as blink
|
|
//
|
|
Marker.aia_age = ARPADDR_MARKER;
|
|
Marker.aia_next = Addr->aia_next;
|
|
Marker.aia_context = Addr;
|
|
Addr->aia_next = &Marker;
|
|
|
|
if (Addr->aia_age == ARPADDR_OLD_LOCAL) {
|
|
if (Addr->aia_context != NULL) {
|
|
SetAddrControl *SAC;
|
|
AddAddrNotifyEvent *DelayedEvent;
|
|
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 {
|
|
IpAddress = Addr->aia_addr;
|
|
CTEFreeLock(&Interface->ai_lock, tblhandle);
|
|
SendARPRequest(Interface, IpAddress, ARP_RESOLVING_GLOBAL,
|
|
NULL, TRUE);
|
|
CTEGetLock(&Interface->ai_lock, &tblhandle);
|
|
}
|
|
//
|
|
// We are done scanning the list
|
|
// Remove the Marker
|
|
//
|
|
|
|
Addr = Marker.aia_next;
|
|
|
|
((ARPIPAddr *)(Marker.aia_context))->aia_next = Marker.aia_next;
|
|
|
|
} else {
|
|
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) {
|
|
CTEGetLockAtDPC(&Current->ate_lock);
|
|
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, (IP_STATUS) 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;
|
|
CTEFreeLockFromDPC(&Current->ate_lock);
|
|
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.
|
|
CTEFreeLockFromDPC(&Current->ate_lock);
|
|
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();
|
|
|
|
UNREFERENCED_PARAMETER(Event);
|
|
|
|
|
|
//
|
|
// 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++] = (uchar)(CurrentByte / 100) + '0';
|
|
CurrentByte %= 100;
|
|
IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
} else if (CurrentByte > 9) {
|
|
IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
}
|
|
IPAddrBuffer[IPAddrCharCount++] = (uchar)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 = (PCHAR) 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 = (PCHAR) HWAddrBuffer;
|
|
ansiString.Length = (ushort) ((NotifyStruct->ans_hwaddrlen * 3) - 1);
|
|
ansiString.MaximumLength = (ushort)(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)
|
|
{
|
|
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++] = (uchar)(CurrentByte / 100) + '0';
|
|
CurrentByte %= 100;
|
|
IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
} else if (CurrentByte > 9) {
|
|
IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
}
|
|
IPAddrBuffer[IPAddrCharCount++] = (uchar) (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));
|
|
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 = DISPATCH_LEVEL, 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 = FALSE;
|
|
BOOLEAN PopUP = TRUE;
|
|
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);
|
|
|
|
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) {
|
|
DebugNotifyStruct->ans_addr = *SPAddr;
|
|
DebugNotifyStruct->ans_shutoff = Shutoff;
|
|
DebugNotifyStruct->ans_hwaddrlen = (uint) ARPHdr->ah_hlen;
|
|
RtlCopyMemory(DebugNotifyStruct->ans_hwaddr, SHAddr,
|
|
ARPHdr->ah_hlen);
|
|
DebugConflictProc(DebugNotifyStruct);
|
|
CTEFreeMem(DebugNotifyStruct);
|
|
}
|
|
}
|
|
|
|
if ((SAC != NULL) && !SAC->StaticAddr) {
|
|
// This is a dhcp adapter.
|
|
// Don't display a warning dialog in this case - DHCP will
|
|
// alert the user
|
|
//
|
|
|
|
PopUP = FALSE;
|
|
}
|
|
|
|
|
|
// 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 (!PopUP) {
|
|
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);
|
|
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);
|
|
|
|
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 = 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) {
|
|
|
|
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_operstatus = INTERFACE_UNINIT;
|
|
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_mediastatus = TRUE;
|
|
|
|
if ((Status = DoNDISRequest(Adapter, NdisRequestQueryInformation,
|
|
OID_GEN_MEDIA_CONNECT_STATUS, &MediaStatus, sizeof(MediaStatus),
|
|
NULL, TRUE)) == NDIS_STATUS_SUCCESS) {
|
|
if (MediaStatus == NdisMediaStateDisconnected) {
|
|
Adapter->ai_mediastatus = FALSE;
|
|
}
|
|
}
|
|
|
|
ARPUpdateOperStatus(Adapter);
|
|
|
|
// 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;
|
|
}
|
|
} else {
|
|
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_operstatus = INTERFACE_UNINIT;
|
|
}
|
|
}
|
|
|
|
//** 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.
|
|
|
|
UNREFERENCED_PARAMETER(ErrorStatus);
|
|
|
|
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;
|
|
ulong Proc;
|
|
|
|
Proc = KeGetCurrentProcessorNumber();
|
|
Interface->ai_qlen[Proc].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;
|
|
|
|
TmpMdl->MappedSystemVa = (PVOID) (((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;
|
|
|
|
UNREFERENCED_PARAMETER(Status);
|
|
|
|
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)
|
|
{
|
|
RequestBlock *rb = STRUCT_OF(RequestBlock, pRequest, Request);
|
|
|
|
DBG_UNREFERENCED_PARAMETER(Handle);
|
|
|
|
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( (PLONG) &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 || Interface->ai_IPSecOffloadFlags) {
|
|
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_operstatus == INTERFACE_UP) &&
|
|
HeaderSize >= (uint) Interface->ai_hdrsize) {
|
|
|
|
// Per RFC 1213 and its successors, the inoctets count includes
|
|
// the MAC header bytes.
|
|
Interface->ai_inoctets += HeaderSize + 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, (PUINT) 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)
|
|
{
|
|
UNREFERENCED_PARAMETER(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_operstatus != INTERFACE_INIT)) {
|
|
|
|
IPStatus(ai->ai_context, GStatus, Status, StatusSize, NULL);
|
|
|
|
switch (GStatus) {
|
|
|
|
//reflect media connect/disconnect status in
|
|
//operstatus for query purpose
|
|
|
|
case NDIS_STATUS_MEDIA_CONNECT:
|
|
|
|
ai->ai_mediastatus = TRUE;
|
|
ARPUpdateOperStatus(ai);
|
|
break;
|
|
|
|
case NDIS_STATUS_MEDIA_DISCONNECT:
|
|
|
|
ai->ai_mediastatus = FALSE;
|
|
ARPUpdateOperStatus(ai);
|
|
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)
|
|
{
|
|
UNREFERENCED_PARAMETER(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;
|
|
long QueueLength = 0;
|
|
uint i;
|
|
|
|
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_mediastatus == TRUE) {
|
|
|
|
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;
|
|
if (AI->ai_operstatus == INTERFACE_UP) {
|
|
IFE->if_operstatus = IF_OPER_STATUS_OPERATIONAL;
|
|
} else {
|
|
// DOWN,INIT, and UNINIT all count as non-operational
|
|
IFE->if_operstatus = IF_OPER_STATUS_NON_OPERATIONAL;
|
|
}
|
|
IFE->if_lastchange = AI->ai_lastchange;
|
|
IFE->if_inoctets = AI->ai_inoctets;
|
|
IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX] + AI->ai_inpcount[AI_PROMIS_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;
|
|
|
|
for (i=0; i < (uint)KeNumberProcessors; i++) {
|
|
QueueLength += AI->ai_qlen[i].ai_qlen;
|
|
}
|
|
|
|
IFE->if_outqlen = max(0, QueueLength);
|
|
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, (PUCHAR) 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) {
|
|
int 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;
|
|
} else if (ID->toi_id == IF_QUERY_SET_OFFLOAD_ID) {
|
|
|
|
IFOffloadCapability IFOC;
|
|
|
|
if (BufferSize < sizeof(IFOC)) {
|
|
return TDI_BUFFER_TOO_SMALL;
|
|
}
|
|
if (QueryAndSetOffload(AI)) {
|
|
|
|
IFOC.ifoc_OffloadFlags = AI->ai_OffloadFlags;
|
|
IFOC.ifoc_IPSecOffloadFlags = AI->ai_IPSecOffloadFlags;
|
|
|
|
if (CopyToNdisSafe(Buffer, NULL, (uchar*)&IFOC, sizeof(IFOC),
|
|
&Offset)) {
|
|
return TDI_SUCCESS;
|
|
}
|
|
}
|
|
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 UNALIGNED *IFE = (IFEntry UNALIGNED *) Buffer;
|
|
IPNetToMediaEntry UNALIGNED *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;
|
|
ARPUpdateOperStatus(Interface);
|
|
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;
|
|
ARPUpdateOperStatus(Interface);
|
|
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 UNALIGNED *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 UNALIGNED *) 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 (!VALID_MASK(Mask))
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) ||
|
|
IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) ||
|
|
IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) ||
|
|
IP_LOOPBACK(AddAddr) ||
|
|
CLASSD_ADDR(AddAddr) ||
|
|
CLASSE_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 UNALIGNED *) 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 = (IP_STATUS) STATUS_UNSUCCESSFUL;
|
|
TmpArpContB = ArpContB->next;
|
|
(*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL);
|
|
ArpContB = TmpArpContB;
|
|
}
|
|
|
|
CurrentATE->ate_resolveonly = NULL;
|
|
}
|
|
|
|
|
|
RemoveARPTableEntry(PrevATE, CurrentATE);
|
|
Interface->ai_count--;
|
|
CTEFreeLockFromDPC(&CurrentATE->ate_lock);
|
|
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;
|
|
|
|
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 = (IP_STATUS) STATUS_UNSUCCESSFUL;
|
|
TmpArpContB = ArpContB->next;
|
|
(*rtn) (ArpContB, (IP_STATUS) 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_qlen) {
|
|
CTEFreeMem(Interface->ai_qlen);
|
|
}
|
|
|
|
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_operstatus == INTERFACE_UNINIT) {
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - our interface %lx atinst %lx going away \n",Interface, Interface->ai_atinst));
|
|
ATEntity->tei_instance = (ULONG) INVALID_ENTITY_INSTANCE;
|
|
}
|
|
} else {
|
|
// we are not on the list.
|
|
// insert ourself iff we are not going away.
|
|
if (Interface->ai_operstatus != INTERFACE_UNINIT) {
|
|
// 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_operstatus == INTERFACE_UNINIT) {
|
|
// KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - our interface %lx ifinst %lx going away \n",Interface, Interface->ai_ifinst));
|
|
IFEntity->tei_instance = (ULONG) INVALID_ENTITY_INSTANCE;
|
|
}
|
|
} else {
|
|
// we are not on the list.
|
|
// insert ourself iff we are not going away.
|
|
if (Interface->ai_operstatus != INTERFACE_UNINIT) {
|
|
// 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;
|
|
uint PrevIPSecOffLoad=ai->ai_IPSecOffloadFlags;
|
|
|
|
//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];
|
|
|
|
|
|
|
|
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;
|
|
|
|
//
|
|
// If MaxOffLoadSize is zero, reject this request.
|
|
//
|
|
|
|
if (TcpLargeSend->MaxOffLoadSize) {
|
|
|
|
ai->ai_OffloadFlags |= TCP_LARGE_SEND_OFFLOAD;
|
|
|
|
// 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_IPSecOffloadFlags |= IPSEC_OFFLOAD_CRYPTO_ONLY;
|
|
|
|
//
|
|
// Do Support first
|
|
//
|
|
|
|
if (pIPSecCaps->Supported.AH_ESP_COMBINED) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_ESP;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_ESP\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.TRANSPORT_TUNNEL_COMBINED) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_TUNNEL;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TPT_TUNNEL\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.V4_OPTIONS) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_V4_OPTIONS;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"V4_OPTIONS\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED) {
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_ENCAPTYPE_IKE) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_IKE;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_IKE\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TUN_UDPESP_ENCAPTYPE_IKE) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_IKE) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_IKE) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_ENCAPTYPE_OTHER) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_OTHER;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_OTHER\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TUN_UDPESP_ENCAPTYPE_OTHER) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_OTHER) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER\n"));
|
|
}
|
|
if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_OTHER) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER\n"));
|
|
}
|
|
}
|
|
//
|
|
// Do V4AH next
|
|
//
|
|
|
|
if (pIPSecCaps->V4AH.MD5) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_MD5;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"MD5\n"));
|
|
}
|
|
if (pIPSecCaps->V4AH.SHA_1) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_SHA_1;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"SHA\n"));
|
|
}
|
|
if (pIPSecCaps->V4AH.Transport) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_TPT;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_TRANSPORT\n"));
|
|
}
|
|
if (pIPSecCaps->V4AH.Tunnel) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_TUNNEL;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_TUNNEL\n"));
|
|
}
|
|
if (pIPSecCaps->V4AH.Send) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_XMT;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_XMT\n"));
|
|
}
|
|
if (pIPSecCaps->V4AH.Receive) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_RCV;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_RCV\n"));
|
|
}
|
|
//
|
|
// Do V4ESP next
|
|
//
|
|
|
|
if (pIPSecCaps->V4ESP.DES) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_DES;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_DES\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.RESERVED) {
|
|
pIPSecCaps->V4ESP.RESERVED = 0;
|
|
//ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_DES_40;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_DES_40\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.TRIPLE_DES) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_3_DES;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_3_DES\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.NULL_ESP) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_NONE;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_NONE\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.Transport) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_TPT;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_TRANSPORT\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.Tunnel) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_TUNNEL;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_TUNNEL\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.Send) {
|
|
ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_XMT;
|
|
KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_XMT\n"));
|
|
}
|
|
if (pIPSecCaps->V4ESP.Receive) {
|
|
ai->ai_IPSecOffloadFlags |= 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;
|
|
|
|
}
|
|
|
|
// Enable the capabilities by setting them.
|
|
|
|
if (PrevOffLoad) {
|
|
ai->ai_OffloadFlags &= PrevOffLoad;
|
|
}
|
|
if (PrevIPSecOffLoad) {
|
|
ai->ai_IPSecOffloadFlags &= PrevIPSecOffLoad;
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (ai->ai_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) {
|
|
ChksumBuf->V4Transmit.IpChecksum = 1;
|
|
}
|
|
if (ai->ai_OffloadFlags & TCP_RCV_CHECKSUM_OFFLOAD) {
|
|
ChksumBuf->V4Receive.TcpChecksum = 1;
|
|
}
|
|
if (ai->ai_OffloadFlags & IP_RCV_CHECKSUM_OFFLOAD) {
|
|
ChksumBuf->V4Receive.IpChecksum = 1;
|
|
}
|
|
//
|
|
// Enable Options capability if present.
|
|
//
|
|
if (ai->ai_OffloadFlags & IP_CHECKSUM_OPT_OFFLOAD) {
|
|
ChksumBuf->V4Transmit.IpOptionsSupported = 1;
|
|
}
|
|
|
|
if (ai->ai_OffloadFlags & TCP_CHECKSUM_OPT_OFFLOAD) {
|
|
ChksumBuf->V4Transmit.TcpOptionsSupported = 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_IPSecOffloadFlags & 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_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_ESP) {
|
|
pIPSecCaps->Supported.AH_ESP_COMBINED = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_TUNNEL) {
|
|
pIPSecCaps->Supported.TRANSPORT_TUNNEL_COMBINED = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_V4_OPTIONS) {
|
|
pIPSecCaps->Supported.V4_OPTIONS = 1;
|
|
}
|
|
|
|
pIPSecCaps->Supported.RESERVED = 0;
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_IKE) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_ENCAPTYPE_IKE;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TUN_UDPESP_ENCAPTYPE_IKE;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_IKE;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_IKE;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OTHER) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_ENCAPTYPE_OTHER;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TUN_UDPESP_ENCAPTYPE_OTHER;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_OTHER;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER) {
|
|
pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_OTHER;
|
|
}
|
|
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_MD5) {
|
|
pIPSecCaps->V4AH.MD5 = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_SHA_1) {
|
|
pIPSecCaps->V4AH.SHA_1 = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TPT) {
|
|
pIPSecCaps->V4AH.Transport = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TUNNEL) {
|
|
pIPSecCaps->V4AH.Tunnel = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_XMT) {
|
|
pIPSecCaps->V4AH.Send = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_RCV) {
|
|
pIPSecCaps->V4AH.Receive = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_DES) {
|
|
pIPSecCaps->V4ESP.DES = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_3_DES) {
|
|
pIPSecCaps->V4ESP.TRIPLE_DES = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_NONE) {
|
|
pIPSecCaps->V4ESP.NULL_ESP = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TPT) {
|
|
pIPSecCaps->V4ESP.Transport = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TUNNEL) {
|
|
pIPSecCaps->V4ESP.Tunnel = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_XMT) {
|
|
pIPSecCaps->V4ESP.Send = 1;
|
|
}
|
|
if (ai->ai_IPSecOffloadFlags & 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;
|
|
ai->ai_IPSecOffloadFlags = 0;
|
|
}
|
|
|
|
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;
|
|
NDIS_STATUS Status;
|
|
BOOLEAN stat;
|
|
uint Needed = 0;
|
|
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;
|
|
ai->ai_IPSecOffloadFlags = 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];
|
|
uint mss;
|
|
uint speed;
|
|
uint MacOpts;
|
|
uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize;
|
|
uint OID;
|
|
uint PF;
|
|
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_operstatus = INTERFACE_INIT;
|
|
ai->ai_adminstate = IF_STATUS_UNKNOWN;
|
|
ai->ai_mediastatus = FALSE;
|
|
ai->ai_lastchange = GetTimeTicks();
|
|
ai->ai_bcast = IP_LOCAL_BCST;
|
|
ai->ai_atinst = ai->ai_ifinst = (UINT)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 *));
|
|
|
|
// Allocate the Counters Structure with best effort in avoiding false sharing
|
|
ai->ai_qlen = CTEAllocMem(KeNumberProcessors * sizeof(PP_AI_COUNTERS));
|
|
if (ai->ai_qlen == (PPP_AI_COUNTERS) NULL) {
|
|
FreeARPInterface(ai);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(ai->ai_qlen, KeNumberProcessors * sizeof(PP_AI_COUNTERS));
|
|
|
|
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( (PLONG) &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 = (uint)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);
|
|
|
|
// Non-Zero Value indicates FFP support
|
|
if (Version.FFPVersion) {
|
|
// Set the FFP startup parameters
|
|
FFPSupportParams Info;
|
|
|
|
Info.NdisProtocolType = NDIS_PROTOCOL_ID_TCP_IP;
|
|
Info.FastForwardingCacheSize = FFPRegFastForwardingCacheSize;
|
|
Info.FFPControlFlags = 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;
|
|
ai->ai_IPSecOffloadFlags = 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;
|
|
|
|
UNREFERENCED_PARAMETER(Adapter);
|
|
UNREFERENCED_PARAMETER(IpHandlers);
|
|
|
|
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.
|
|
IP_STATUS Status; // State of IPAddInterface call.
|
|
LLIPBindInfo BindInfo; // Binding information for IP.
|
|
NDIS_HANDLE Handle;
|
|
NDIS_STRING IPConfigName;
|
|
|
|
DBG_UNREFERENCED_PARAMETER(BindContext);
|
|
|
|
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_IPSecOffloadFlags = Interface->ai_IPSecOffloadFlags;
|
|
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_adminstate = IF_STATUS_DOWN;
|
|
ARPUpdateOperStatus(Interface);
|
|
|
|
// Mark the interface as going away so it will disappear from the
|
|
// entity list.
|
|
Interface->ai_operstatus = INTERFACE_UNINIT;
|
|
|
|
#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)
|
|
{
|
|
|
|
#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;
|
|
NDIS_STATUS 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);
|
|
}
|
|
|
|
|