mirror of https://github.com/lianthony/NT4.0
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.
4839 lines
162 KiB
4839 lines
162 KiB
/********************************************************************/
|
|
/** Microsoft LAN Manager **/
|
|
/** Copyright(c) Microsoft Corp., 1990-1992 **/
|
|
/********************************************************************/
|
|
/* :ts=4 */
|
|
|
|
//*** arp.c - ARP VxD 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 "oscfg.h"
|
|
#ifdef VXD
|
|
#include <string.h>
|
|
#endif
|
|
#include "ndis.h"
|
|
#include "cxport.h"
|
|
#include "ip.h"
|
|
#include "ipdef.h"
|
|
#include "llipif.h"
|
|
#include "arp.h"
|
|
#include "arpdef.h"
|
|
#include "tdiinfo.h"
|
|
#include "ipinfo.h"
|
|
#include "llinfo.h"
|
|
#include "tdistat.h"
|
|
#include "iproute.h"
|
|
#include "iprtdef.h"
|
|
#include "arpinfo.h"
|
|
#include "ipinit.h"
|
|
|
|
#ifndef CHICAGO
|
|
#ifndef _PNP_POWER
|
|
#define NDIS_MAJOR_VERSION 0x03
|
|
#define NDIS_MINOR_VERSION 0
|
|
#else
|
|
#define NDIS_MAJOR_VERSION 0x04
|
|
#define NDIS_MINOR_VERSION 0
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef NDIS_API
|
|
#define NDIS_API
|
|
#endif
|
|
|
|
|
|
static ulong ARPLookahead = LOOKAHEAD_SIZE;
|
|
|
|
static uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06";
|
|
static uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70";
|
|
static uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00";
|
|
static uchar ARCBcst[] = "\x00\x00\xd5";
|
|
|
|
static uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00";
|
|
static uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00";
|
|
static uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06";
|
|
|
|
#ifdef NT
|
|
static WCHAR ARPName[] = TCP_NAME;
|
|
#else // NT
|
|
static uchar ARPName[] = TCP_NAME;
|
|
#endif // NT
|
|
|
|
NDIS_HANDLE ARPHandle; // Our NDIS protocol handle.
|
|
|
|
uint ArpCacheLife;
|
|
uint sArpAlwaysSourceRoute; // True if we always send ARP requests
|
|
// with source route info on token ring.
|
|
uint sIPAlwaysSourceRoute;
|
|
extern uchar TrRii;
|
|
|
|
extern PDRIVER_OBJECT IPDriverObject;
|
|
|
|
extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint);
|
|
extern void IPTDComplete(void *, PNDIS_PACKET, NDIS_STATUS, uint);
|
|
extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS);
|
|
extern void IPStatus(void *, NDIS_STATUS, void *, uint);
|
|
extern void IPRcvComplete(void);
|
|
extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
|
|
uint *StartOffset);
|
|
|
|
extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS);
|
|
extern void IPULUnloadNotify(void);
|
|
|
|
#ifdef _PNP_POWER
|
|
extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNP,
|
|
void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo);
|
|
extern void IPDelInterface(void *Context);
|
|
|
|
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);
|
|
|
|
#endif
|
|
|
|
|
|
// Tables for bitswapping.
|
|
|
|
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
|
|
};
|
|
|
|
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.
|
|
ushort IFieldSize[] = {
|
|
516,
|
|
1500,
|
|
2052,
|
|
4472,
|
|
8191
|
|
};
|
|
|
|
#define LF_BIT_SHIFT 4
|
|
#define MAX_LF_BITS 4
|
|
|
|
#ifdef NT
|
|
#ifdef ALLOC_PRAGMA
|
|
//
|
|
// Disposable init code.
|
|
//
|
|
void FreeARPInterface(ARPInterface *Interface);
|
|
void ARPOpen(void *Context);
|
|
|
|
#pragma alloc_text(INIT, ARPInit)
|
|
#ifndef _PNP_POWER
|
|
#pragma alloc_text(INIT, FreeARPInterface)
|
|
#pragma alloc_text(INIT, ARPOpen)
|
|
#pragma alloc_text(INIT, ARPRegister)
|
|
#else
|
|
#pragma alloc_text(PAGE, ARPOpen)
|
|
#pragma alloc_text(PAGE, ARPRegister)
|
|
|
|
#endif
|
|
|
|
//
|
|
// Paged code
|
|
//
|
|
void NotifyConflictProc(CTEEvent *Event, void *Context);
|
|
|
|
#pragma alloc_text(PAGE, NotifyConflictProc)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
#endif // NT
|
|
|
|
#ifdef VXD
|
|
extern void EnableInts(void);
|
|
#endif
|
|
|
|
//* DoNDISRequest - Submit a 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, and submits the request.
|
|
//
|
|
// 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 buffer to be passed.
|
|
// Length - Length of data in the buffer.
|
|
// Needed - On return, filled in with bytes needed in buffer.
|
|
//
|
|
// Exit:
|
|
//
|
|
NDIS_STATUS
|
|
DoNDISRequest(ARPInterface *Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID,
|
|
void *Info, uint Length, uint *Needed)
|
|
{
|
|
NDIS_REQUEST Request; // Request structure we'll use.
|
|
NDIS_STATUS Status;
|
|
|
|
// Now fill it in.
|
|
Request.RequestType = RT;
|
|
if (RT == NdisRequestSetInformation) {
|
|
Request.DATA.SET_INFORMATION.Oid = OID;
|
|
Request.DATA.SET_INFORMATION.InformationBuffer = Info;
|
|
Request.DATA.SET_INFORMATION.InformationBufferLength = Length;
|
|
} else {
|
|
Request.DATA.QUERY_INFORMATION.Oid = OID;
|
|
Request.DATA.QUERY_INFORMATION.InformationBuffer = Info;
|
|
Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;
|
|
}
|
|
|
|
// Initialize the block structure.
|
|
CTEInitBlockStruc(&Adapter->ai_block);
|
|
#ifdef VXD
|
|
EnableInts();
|
|
#endif
|
|
|
|
// Submit the request.
|
|
NdisRequest(&Status, Adapter->ai_handle, &Request);
|
|
|
|
// Wait for it to finish
|
|
if (Status == NDIS_STATUS_PENDING)
|
|
Status = (NDIS_STATUS)CTEBlock(&Adapter->ai_block);
|
|
|
|
if (Needed != NULL)
|
|
*Needed = Request.DATA.QUERY_INFORMATION.BytesNeeded;
|
|
|
|
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.
|
|
//
|
|
void
|
|
FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer)
|
|
{
|
|
CTELockHandle lhandle;
|
|
uchar **Header; // header buffer to be freed.
|
|
uint Size;
|
|
|
|
Size = NdisBufferLength(Buffer);
|
|
|
|
if (Size <= Interface->ai_sbsize) {
|
|
#ifdef VXD
|
|
// A small buffer, put him on the list.
|
|
NDIS_BUFFER_LINKAGE(Buffer) = Interface->ai_sblist;
|
|
Interface->ai_sblist = Buffer;
|
|
#else
|
|
ExInterlockedPushEntrySList(
|
|
&Interface->ai_sblist,
|
|
STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next),
|
|
&Interface->ai_lock
|
|
);
|
|
|
|
#endif
|
|
|
|
return;
|
|
} else {
|
|
// A big buffer. Get the buffer pointer, link it on, and free the
|
|
// NDIS buffer.
|
|
Header = (uchar **)NdisBufferVirtualAddress(Buffer);
|
|
|
|
CTEGetLock(&Interface->ai_lock, &lhandle);
|
|
*Header = Interface->ai_bblist;
|
|
Interface->ai_bblist = (uchar *)Header;
|
|
CTEFreeLock(&Interface->ai_lock, lhandle);
|
|
|
|
NdisFreeBuffer(Buffer);
|
|
}
|
|
}
|
|
|
|
//* GrowARPHeaders - Grow the ARP header buffer list.
|
|
//
|
|
// Called when we need to grow the ARP header buffer list. Called with the
|
|
// interface lock held.
|
|
//
|
|
// Input: Interface - Interface on which to grow.
|
|
//
|
|
// Returns: Pointer to newly allocated buffer, or NULL.
|
|
//
|
|
PNDIS_BUFFER
|
|
GrowARPHeaders(ARPInterface *Interface)
|
|
{
|
|
ARPBufferTracker *NewTracker;
|
|
PNDIS_BUFFER Buffer, ReturnBuffer;
|
|
uchar *Header;
|
|
uint i;
|
|
NDIS_STATUS Status;
|
|
CTELockHandle Handle;
|
|
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
|
|
// Make sure we're allowed to allocate.
|
|
if (Interface->ai_curhdrs >= Interface->ai_maxhdrs)
|
|
goto failure;
|
|
|
|
NewTracker = CTEAllocMem(sizeof(ARPBufferTracker));
|
|
if (NewTracker == NULL)
|
|
goto failure; // We're out of memory.
|
|
|
|
NdisAllocateBufferPool(&Status, &NewTracker->abt_handle,
|
|
ARP_HDRBUF_GROW_SIZE);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEFreeMem(NewTracker);
|
|
goto failure;
|
|
}
|
|
|
|
Header = CTEAllocMem((uint)Interface->ai_sbsize * ARP_HDRBUF_GROW_SIZE);
|
|
if (Header == NULL) {
|
|
NdisFreeBufferPool(NewTracker->abt_handle);
|
|
CTEFreeMem(NewTracker);
|
|
goto failure;
|
|
}
|
|
|
|
// Got the resources we need, allocate the buffers.
|
|
NewTracker->abt_buffer = Header;
|
|
NewTracker->abt_next = Interface->ai_buflist;
|
|
Interface->ai_buflist = NewTracker;
|
|
ReturnBuffer = NULL;
|
|
Interface->ai_curhdrs += ARP_HDRBUF_GROW_SIZE;
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
|
|
for (i = 0; i < ARP_HDRBUF_GROW_SIZE; i++) {
|
|
NdisAllocateBuffer(&Status, &Buffer, NewTracker->abt_handle,
|
|
Header + (i * Interface->ai_sbsize), Interface->ai_sbsize);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
CTEAssert(FALSE);
|
|
break;
|
|
}
|
|
if (i != 0) {
|
|
FreeARPBuffer(Interface, Buffer);
|
|
} else
|
|
ReturnBuffer = Buffer;
|
|
}
|
|
|
|
// Update for what we didn't allocate, if any.
|
|
CTEInterlockedAddUlong(&Interface->ai_curhdrs, i - ARP_HDRBUF_GROW_SIZE,
|
|
&Interface->ai_lock);
|
|
|
|
return ReturnBuffer;
|
|
|
|
failure:
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
return NULL;
|
|
}
|
|
|
|
//* 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
|
|
GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar size)
|
|
{
|
|
CTELockHandle lhandle; // Lock handle
|
|
NDIS_STATUS Status;
|
|
PNDIS_BUFFER Buffer; // NDIS buffer allocated.
|
|
|
|
if (size <= Interface->ai_sbsize) {
|
|
#ifdef VXD
|
|
Buffer = Interface->ai_sblist;
|
|
if (Buffer != NULL) {
|
|
Interface->ai_sblist = NDIS_BUFFER_LINKAGE(Buffer);
|
|
NDIS_BUFFER_LINKAGE(Buffer) = NULL;
|
|
NdisBufferLength(Buffer) = size;
|
|
*BufPtr = NdisBufferVirtualAddress(Buffer);
|
|
return Buffer;
|
|
#else
|
|
PSINGLE_LIST_ENTRY BufferLink;
|
|
|
|
BufferLink = ExInterlockedPopEntrySList(
|
|
&Interface->ai_sblist,
|
|
&Interface->ai_lock
|
|
);
|
|
if (BufferLink != NULL) {
|
|
Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
|
|
NDIS_BUFFER_LINKAGE(Buffer) = NULL;
|
|
NdisBufferLength(Buffer) = size;
|
|
*BufPtr = NdisBufferVirtualAddress(Buffer);
|
|
return Buffer;
|
|
#endif
|
|
|
|
} else {
|
|
Buffer = GrowARPHeaders(Interface);
|
|
if (Buffer != NULL) {
|
|
NDIS_BUFFER_LINKAGE(Buffer) = NULL;
|
|
NdisBufferLength(Buffer) = size;
|
|
*BufPtr = NdisBufferVirtualAddress(Buffer);
|
|
}
|
|
return Buffer;
|
|
}
|
|
} else {
|
|
// Need a 'big' buffer.
|
|
CTEGetLock(&Interface->ai_lock, &lhandle);
|
|
if ((*BufPtr = Interface->ai_bblist) != (uchar *)NULL) {
|
|
Interface->ai_bblist = *(uchar **)*BufPtr;
|
|
CTEFreeLock(&Interface->ai_lock, lhandle); // Got a buffer.
|
|
NdisAllocateBuffer(&Status, &Buffer, Interface->ai_bpool, *BufPtr,
|
|
size);
|
|
if (Status == NDIS_STATUS_SUCCESS)
|
|
return Buffer;
|
|
else { // Couldn't get NDIS buffer, free our buffer.
|
|
CTEGetLock(&Interface->ai_lock, &lhandle);
|
|
*(uchar **)&**BufPtr = Interface->ai_bblist;
|
|
Interface->ai_bblist = *BufPtr;
|
|
CTEFreeLock(&Interface->ai_lock, lhandle);
|
|
return (PNDIS_BUFFER)NULL;
|
|
}
|
|
}
|
|
|
|
// Couldn't get a header buffer, free lock and return NULL.
|
|
CTEFreeLock(&Interface->ai_lock, lhandle);
|
|
return (PNDIS_BUFFER)NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//* BitSwap - Bit swap two strings.
|
|
//
|
|
// A routine to bitswap two strings.
|
|
//
|
|
// Input: Dest - Destination of swap.
|
|
// Src - Src string to be swapped.
|
|
// Length - Length in bytes to swap.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
BitSwap(uchar *Dest, uchar *Src, uint Length)
|
|
{
|
|
uint i;
|
|
uchar Temp, TempSrc;
|
|
|
|
for (i = 0; i < Length; i++, Dest++, Src++) {
|
|
TempSrc = *Src;
|
|
Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f];
|
|
*Dest = Temp;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//* SendARPPacket - Build a header, and send a packet.
|
|
//
|
|
// A utility routine to build and ARP header and send a packet. We assume
|
|
// the media specific header has been built.
|
|
//
|
|
// Entry: Interface - Interface for NDIS drive.
|
|
// Packet - Pointer to packet to be sent
|
|
// Header - Pointer to header to fill in.
|
|
// Opcode - Opcode for packet.
|
|
// Address - Source HW address.
|
|
// SrcAddr - Address to use as our source h/w address.
|
|
// Destination - Destination IP address.
|
|
// Src - Source IP address.
|
|
// HWType - Hardware type.
|
|
// CheckIF - TRUE iff we are to check the I/F status before
|
|
// sending.
|
|
//
|
|
// Returns: NDIS_STATUS of send.
|
|
//
|
|
NDIS_STATUS
|
|
SendARPPacket(ARPInterface *Interface, PNDIS_PACKET Packet, ARPHeader *Header, ushort Opcode,
|
|
uchar *Address, uchar *SrcAddr, IPAddr Destination, IPAddr Src,
|
|
ushort HWType, uint CheckIF)
|
|
{
|
|
NDIS_STATUS Status;
|
|
PNDIS_BUFFER Buffer;
|
|
uint PacketDone;
|
|
uchar *AddrPtr;
|
|
|
|
Header->ah_hw = HWType;
|
|
Header->ah_pro = net_short(ARP_ETYPE_IP);
|
|
Header->ah_hlen = Interface->ai_addrlen;
|
|
Header->ah_plen = sizeof(IPAddr);
|
|
Header->ah_opcode = Opcode;
|
|
AddrPtr = Header->ah_shaddr;
|
|
|
|
if (SrcAddr == NULL)
|
|
SrcAddr = Interface->ai_addr;
|
|
|
|
CTEMemCopy(AddrPtr, SrcAddr, Interface->ai_addrlen);
|
|
|
|
AddrPtr += Interface->ai_addrlen;
|
|
*(IPAddr UNALIGNED *)AddrPtr = Src;
|
|
AddrPtr += sizeof(IPAddr);
|
|
|
|
if (Address != (uchar *)NULL)
|
|
CTEMemCopy(AddrPtr, Address, Interface->ai_addrlen);
|
|
else
|
|
CTEMemSet(AddrPtr, 0, Interface->ai_addrlen);
|
|
|
|
AddrPtr += Interface->ai_addrlen;
|
|
*(IPAddr UNALIGNED *)AddrPtr = Destination;
|
|
|
|
PacketDone = FALSE;
|
|
|
|
if (!CheckIF || Interface->ai_state == INTERFACE_UP) {
|
|
|
|
Interface->ai_qlen++;
|
|
NdisSend(&Status, Interface->ai_handle, Packet);
|
|
|
|
if (Status != NDIS_STATUS_PENDING) {
|
|
PacketDone = TRUE;
|
|
Interface->ai_qlen--;
|
|
#ifdef VXD
|
|
CTEAssert(*(int *)&Interface->ai_qlen >= 0);
|
|
#endif
|
|
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.
|
|
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;
|
|
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:
|
|
DEBUGCHK;
|
|
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)
|
|
NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT;
|
|
|
|
// Copy broadcast address into packet.
|
|
CTEMemCopy(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));
|
|
}
|
|
|
|
CTEMemCopy(&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.
|
|
CTEMemCopy(&MHeader[MHeaderSize], SNAPAddr, SNAPLength);
|
|
|
|
// Media header is filled in. Now do ARP packet itself.
|
|
NdisChainBufferAtFront(Packet, Buffer);
|
|
return SendARPPacket(Interface, Packet,(ARPHeader *)&MHeader[MHeaderSize + SNAPLength],
|
|
net_short(ARP_REQUEST), (uchar *)NULL, SrcAddr, Destination, Src,
|
|
HWType, CheckIF);
|
|
}
|
|
|
|
//* SendARPReply - Reply to an ARP request.
|
|
//
|
|
// Called by our receive packet handler when we need to reply. We build a packet
|
|
// and buffer and call SendARPPacket to send it.
|
|
//
|
|
// Entry: Interface - Pointer to interface to reply on.
|
|
// Destination - IPAddress to reply to.
|
|
// Src - Source address to reply from.
|
|
// HWAddress - Hardware address to reply to.
|
|
// SourceRoute - Source Routing information, if any.
|
|
// SourceRouteSize - Size in bytes of soure routing.
|
|
// UseSNAP - Whether or not to use SNAP for this reply.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
SendARPReply(ARPInterface *Interface, IPAddr Destination, IPAddr Src, uchar *HWAddress,
|
|
RC UNALIGNED *SourceRoute, uint SourceRouteSize, uint UseSNAP)
|
|
{
|
|
PNDIS_PACKET Packet; // Buffer and packet to be used.
|
|
PNDIS_BUFFER Buffer;
|
|
uchar *Header; // Pointer to media header.
|
|
NDIS_STATUS Status;
|
|
uchar Size = 0; // Size of media header buffer.
|
|
ushort HWType;
|
|
ENetHeader *EH;
|
|
FDDIHeader *FH;
|
|
ARCNetHeader *AH;
|
|
TRHeader *TRH;
|
|
|
|
// Allocate a packet for this.
|
|
NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
Interface->ai_outdiscards++;
|
|
return;
|
|
}
|
|
|
|
((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
|
|
(Interface->ai_outpcount[AI_UCAST_INDEX])++;
|
|
|
|
Size = Interface->ai_hdrsize;
|
|
|
|
if (UseSNAP)
|
|
Size += Interface->ai_snapsize;
|
|
|
|
if (Interface->ai_media == NdisMedium802_5)
|
|
Size += 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;
|
|
CTEMemCopy(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(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);
|
|
CTEMemCopy(Header + sizeof(ENetHeader), ARPSNAP,
|
|
sizeof(SNAPHeader));
|
|
}
|
|
break;
|
|
case NdisMedium802_5:
|
|
TRH = (TRHeader *)Header;
|
|
TRH->tr_ac = ARP_AC;
|
|
TRH->tr_fc = ARP_FC;
|
|
CTEMemCopy(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
|
|
if (SourceRouteSize) {// If we have source route info, deal with
|
|
// it.
|
|
CTEMemCopy(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;
|
|
}
|
|
CTEMemCopy(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;
|
|
CTEMemCopy(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(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;
|
|
NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT;
|
|
HWType = net_short(ARP_HW_ARCNET);
|
|
break;
|
|
default:
|
|
DEBUGCHK;
|
|
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.
|
|
#ifdef DEBUG
|
|
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;
|
|
#ifdef DEBUG
|
|
Found = TRUE;
|
|
#endif
|
|
break;
|
|
}
|
|
else
|
|
CurrentAC = (ARPContext *)CurrentAC->ac_next->rce_context;
|
|
|
|
CTEAssert(Found);
|
|
}
|
|
//* ARPLookup - Look up an entry in the ARP table.
|
|
//
|
|
// Called to look up an entry in an interface's ARP table. If we find it, we'll
|
|
// lock the entry and return a pointer to it, otherwise we return NULL. We
|
|
// assume that the caller has the ARP table locked when we are called.
|
|
//
|
|
// The ARP table entry is structured as a hash table of pointers to
|
|
// ARPTableEntrys.After hashing on the IP address, a linear search is done to
|
|
// lookup the entry.
|
|
//
|
|
// If we find the entry, we lock it for the caller. If we don't find
|
|
// the entry, we leave the ARP table locked so that the caller may atomically
|
|
// insert a new entry without worrying about a duplicate being inserted between
|
|
// the time the table was checked and the time the caller went to insert the
|
|
// entry.
|
|
//
|
|
// Entry: Interface - The interface to be searched upon.
|
|
// Address - The IP address we're looking up.
|
|
// Handle - Pointer to lock handle to be used to lock entry.
|
|
//
|
|
// Returns: Pointer to ARPTableEntry if found, or NULL if not.
|
|
//
|
|
ARPTableEntry *
|
|
ARPLookup(ARPInterface *Interface, IPAddr Address, CTELockHandle *Handle)
|
|
{
|
|
int i = ARP_HASH(Address); // Index into hash table.
|
|
ARPTableEntry *Current; // Current ARP Table entry being
|
|
// examined.
|
|
|
|
Current = (*Interface->ai_ARPTbl)[i];
|
|
|
|
while (Current != (ARPTableEntry *)NULL) {
|
|
CTEGetLock(&Current->ate_lock, Handle);
|
|
if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match.
|
|
return Current;
|
|
}
|
|
CTEFreeLock(&Current->ate_lock, *Handle);
|
|
Current = Current->ate_next;
|
|
}
|
|
// If we got here, we didn't find the entry. Leave the table locked and
|
|
// return the handle.
|
|
return (ARPTableEntry *)NULL;
|
|
}
|
|
|
|
//* IsBCastOnIF- See it an address is a broadcast address on an interface.
|
|
//
|
|
// Called to see if a particular address is a broadcast address on an
|
|
// interface. We'll check the global, net, and subnet broadcasts. We assume
|
|
// the caller holds the lock on the interface.
|
|
//
|
|
// Entry: Interface - Interface to check.
|
|
// Addr - Address to check.
|
|
//
|
|
// Returns: TRUE if it it a broadcast, FALSE otherwise.
|
|
//
|
|
uint
|
|
IsBCastOnIF(ARPInterface *Interface, IPAddr Addr)
|
|
{
|
|
IPAddr BCast;
|
|
IPMask Mask;
|
|
ARPIPAddr *ARPAddr;
|
|
IPAddr LocalAddr;
|
|
|
|
// First get the interface broadcast address.
|
|
BCast = Interface->ai_bcast;
|
|
|
|
// First check for global broadcast.
|
|
if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr))
|
|
return TRUE;
|
|
|
|
// Now walk the local addresses, and check for net/subnet bcast on each
|
|
// one.
|
|
ARPAddr = &Interface->ai_ipaddr;
|
|
do {
|
|
// See if this one is valid.
|
|
LocalAddr = ARPAddr->aia_addr;
|
|
if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
|
|
// He's valid.
|
|
Mask = ARPAddr->aia_mask;
|
|
|
|
// First check for subnet bcast.
|
|
if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
|
|
return TRUE;
|
|
|
|
// Now check all nets broadcast.
|
|
Mask = IPNetMask(LocalAddr);
|
|
if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
|
|
return TRUE;
|
|
}
|
|
|
|
ARPAddr = ARPAddr->aia_next;
|
|
|
|
} while (ARPAddr != NULL);
|
|
|
|
// If we're here, it's not a broadcast.
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
//* ARPSendBCast - See if this is a bcast or mcast frame, and send it.
|
|
//
|
|
// Called when we have a packet to send and we want to see if it's a broadcast
|
|
// or multicast frame on this interface. We'll search the local addresses and
|
|
// see if we can determine if it is. If it is, we'll send it here. Otherwise
|
|
// we return FALSE, and the caller will try to resolve the address.
|
|
//
|
|
// Entry: Interface - A pointer to an AI structure.
|
|
// Dest - Destination of datagram.
|
|
// Packet - Packet to be sent.
|
|
// Status - Place to return status of send attempt.
|
|
//
|
|
// Returns: TRUE if is was a bcast or mcast send, FALSE otherwise.
|
|
//
|
|
uint
|
|
ARPSendBCast(ARPInterface *Interface, IPAddr Dest, PNDIS_PACKET Packet,
|
|
PNDIS_STATUS Status)
|
|
{
|
|
uint IsBCast;
|
|
CTELockHandle Handle;
|
|
PNDIS_BUFFER ARPBuffer; // ARP Header buffer.
|
|
uchar *BufAddr; // Address of NDIS buffer
|
|
NDIS_STATUS MyStatus;
|
|
ENetHeader *Hdr;
|
|
FDDIHeader *FHdr;
|
|
TRHeader *TRHdr;
|
|
SNAPHeader UNALIGNED *SNAPPtr;
|
|
RC UNALIGNED *RCPtr;
|
|
ARCNetHeader *AHdr;
|
|
uint DataLength;
|
|
|
|
// Get the lock, and see if it's a broadcast.
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
IsBCast = IsBCastOnIF(Interface, Dest);
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
|
|
if (IsBCast) {
|
|
if (Interface->ai_state == INTERFACE_UP) {
|
|
uchar Size;
|
|
|
|
Size = Interface->ai_hdrsize + Interface->ai_snapsize;
|
|
|
|
if (Interface->ai_media == NdisMedium802_5)
|
|
Size += sizeof(RC);
|
|
|
|
ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size);
|
|
if (ARPBuffer != NULL) {
|
|
uint UNALIGNED *Temp;
|
|
|
|
// Got the buffer we need.
|
|
switch (Interface->ai_media) {
|
|
|
|
case NdisMedium802_3:
|
|
|
|
Hdr = (ENetHeader *)BufAddr;
|
|
if (!CLASSD_ADDR(Dest))
|
|
CTEMemCopy(Hdr, ENetBcst, ARP_802_ADDR_LENGTH);
|
|
else {
|
|
CTEMemCopy(Hdr, ENetMcst, ARP_802_ADDR_LENGTH);
|
|
Temp = (uint UNALIGNED *)&Hdr->eh_daddr[2];
|
|
*Temp |= (Dest & ARP_MCAST_MASK);
|
|
}
|
|
|
|
CTEMemCopy(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));
|
|
CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
|
|
SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
|
|
}
|
|
|
|
break;
|
|
|
|
case NdisMedium802_5:
|
|
|
|
// This is token ring. We'll have to screw around with
|
|
// source routing.
|
|
|
|
// BUGBUG Need to support 'real' TR functional address
|
|
// for multicast - see RFC 1469.
|
|
|
|
TRHdr = (TRHeader *)BufAddr;
|
|
|
|
CTEMemCopy(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr));
|
|
CTEMemCopy(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));
|
|
}
|
|
CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
|
|
SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
|
|
|
|
break;
|
|
case NdisMediumFddi:
|
|
FHdr = (FDDIHeader *)BufAddr;
|
|
|
|
if (!CLASSD_ADDR(Dest))
|
|
CTEMemCopy(FHdr, FDDIBcst,
|
|
offsetof(FDDIHeader, fh_saddr));
|
|
else {
|
|
CTEMemCopy(FHdr, FDDIMcst,
|
|
offsetof(FDDIHeader, fh_saddr));
|
|
Temp = (uint UNALIGNED *)&FHdr->fh_daddr[2];
|
|
*Temp |= (Dest & ARP_MCAST_MASK);
|
|
}
|
|
|
|
CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr,
|
|
ARP_802_ADDR_LENGTH);
|
|
|
|
SNAPPtr = (SNAPHeader UNALIGNED *)(BufAddr + sizeof(FDDIHeader));
|
|
CTEMemCopy(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:
|
|
DEBUGCHK;
|
|
*Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
|
|
FreeARPBuffer(Interface, ARPBuffer);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
(Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
|
|
Interface->ai_qlen++;
|
|
NdisChainBufferAtFront(Packet, ARPBuffer);
|
|
NdisSend(&MyStatus, Interface->ai_handle, Packet);
|
|
|
|
*Status = MyStatus;
|
|
|
|
if (MyStatus != NDIS_STATUS_PENDING) { // Send finished
|
|
// immediately.
|
|
if (MyStatus == NDIS_STATUS_SUCCESS) {
|
|
Interface->ai_outoctets += Packet->Private.TotalLength;
|
|
} else {
|
|
if (MyStatus == NDIS_STATUS_RESOURCES)
|
|
Interface->ai_outdiscards++;
|
|
else
|
|
Interface->ai_outerrors++;
|
|
}
|
|
|
|
Interface->ai_qlen--;
|
|
#ifdef VXD
|
|
CTEAssert(*(int *)&Interface->ai_qlen >= 0);
|
|
#endif
|
|
NdisUnchainBufferAtFront(Packet, &ARPBuffer);
|
|
FreeARPBuffer(Interface, ARPBuffer);
|
|
}
|
|
} else
|
|
*Status = NDIS_STATUS_RESOURCES;
|
|
} else
|
|
*Status = NDIS_STATUS_ADAPTER_NOT_READY;
|
|
|
|
return TRUE;
|
|
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
//* 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; // ARP Header buffer.
|
|
uchar *BufAddr; // Address of NDIS buffer
|
|
NDIS_STATUS Status; // Status of send.
|
|
|
|
if (Interface->ai_state == INTERFACE_UP) {
|
|
|
|
if (entry->ate_state == ARP_GOOD) { // Entry is valid
|
|
|
|
entry->ate_useticks = ArpCacheLife;
|
|
if ((ARPBuffer = GetARPBuffer(Interface, &BufAddr,
|
|
entry->ate_addrlength)) != (PNDIS_BUFFER)NULL) {
|
|
|
|
// Everything's in good shape, copy header and send packet.
|
|
|
|
(Interface->ai_outpcount[AI_UCAST_INDEX])++;
|
|
Interface->ai_qlen++;
|
|
CTEMemCopy(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);
|
|
ShortDataSize = (ushort)(DataSize + sizeof(SNAPHeader));
|
|
Header = (ENetHeader *)BufAddr;
|
|
Header->eh_type = net_short(ShortDataSize);
|
|
}
|
|
|
|
CTEFreeLock(&entry->ate_lock, lhandle);
|
|
NdisChainBufferAtFront(Packet, ARPBuffer);
|
|
NdisSend(&Status, Interface->ai_handle, Packet);
|
|
if (Status != NDIS_STATUS_PENDING) { // Send finished
|
|
// immediately.
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
Interface->ai_outoctets += Packet->Private.TotalLength;
|
|
} else {
|
|
if (Status == NDIS_STATUS_RESOURCES)
|
|
Interface->ai_outdiscards++;
|
|
else
|
|
Interface->ai_outerrors++;
|
|
}
|
|
|
|
Interface->ai_qlen--;
|
|
#ifdef VXD
|
|
CTEAssert(*(int *)&Interface->ai_qlen >= 0);
|
|
#endif
|
|
NdisUnchainBufferAtFront(Packet, &ARPBuffer);
|
|
FreeARPBuffer(Interface, ARPBuffer);
|
|
}
|
|
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're 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 {
|
|
DEBUGCHK;
|
|
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)
|
|
{
|
|
ARPTableEntry *NewEntry, *Entry;
|
|
CTELockHandle TableHandle;
|
|
int i = ARP_HASH(Destination);
|
|
int Size;
|
|
|
|
// First look for it, and if we don't find it return try to create one.
|
|
CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
|
|
if ((Entry = ARPLookup(Interface, Destination, Handle)) !=
|
|
(ARPTableEntry *)NULL) {
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, *Handle);
|
|
*Handle = TableHandle;
|
|
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 = CTEAllocMem(Size)) == (ARPTableEntry *)NULL) {
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
|
|
return (ARPTableEntry *)NULL;
|
|
}
|
|
|
|
CTEMemSet(NewEntry, 0, 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;
|
|
|
|
NewEntry->ate_rce = NULL;
|
|
|
|
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.
|
|
// ARPLookup returns with the table lock held if it fails.
|
|
NewEntry->ate_next = (*Interface->ai_ARPTbl)[i];
|
|
(*Interface->ai_ARPTbl)[i] = NewEntry;
|
|
Interface->ai_count++;
|
|
CTEGetLock(&NewEntry->ate_lock, Handle);
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, *Handle);
|
|
*Handle = TableHandle;
|
|
return NewEntry;
|
|
}
|
|
|
|
|
|
//* ARPTransmit - Send a frame.
|
|
//
|
|
// The main ARP transmit routine, called by the upper layer. This routine
|
|
// takes as input a buf desc chain, RCE, and size. We validate the cached
|
|
// information in the RCE. If it is valid, we use it to send the frame. Otherwise
|
|
// we do a table lookup. If we find it in the table, we'll update the RCE and continue.
|
|
// Otherwise we'll queue the packet and start an ARP resolution.
|
|
//
|
|
// Entry: Context - A pointer to the AI structure.
|
|
// Packet - A pointer to the BufDesc chain to be sent.
|
|
// Destination - IP address of destination we're trying to reach,
|
|
// RCE - A pointer to an RCE which may have cached information.
|
|
//
|
|
// Returns: Status of the transmit - success, an error, or pending.
|
|
//
|
|
NDIS_STATUS
|
|
ARPTransmit(void *Context, PNDIS_PACKET Packet, IPAddr Destination,
|
|
RouteCacheEntry *RCE)
|
|
{
|
|
ARPInterface *ai = (ARPInterface *)Context; // Set up as AI pointer.
|
|
ARPContext *ac; // ARP context pointer.
|
|
ARPTableEntry *entry; // Pointer to ARP tbl. entry
|
|
CTELockHandle lhandle; // Lock handle
|
|
CTELockHandle tlhandle; // Lock handle for ARP table.
|
|
NDIS_STATUS Status;
|
|
|
|
CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
|
|
if (RCE != (RouteCacheEntry *)NULL) { // Have a valid RCE.
|
|
ac = (ARPContext *)RCE->rce_context; // Get pointer to context
|
|
entry = ac->ac_ate;
|
|
if (entry != (ARPTableEntry *)NULL) { // Have a valid ATE.
|
|
CTEGetLockAtDPC(&entry->ate_lock, &lhandle); // Lock this structure
|
|
if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) {
|
|
CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
|
|
return ARPSendData(ai, Packet, entry, tlhandle); // Send the data
|
|
}
|
|
|
|
// 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);
|
|
CTEFreeLock(&entry->ate_lock, lhandle);
|
|
// Fall through to 'no valid entry' code.
|
|
}
|
|
}
|
|
|
|
// Here we have no valid ATE, either because the RCE is NULL or the ATE
|
|
// specified by the RCE was invalid. We'll try and find one in the table. If
|
|
// we find one, we'll fill in this RCE and send the packet. Otherwise we'll
|
|
// try to create one. At this point we hold the lock on the ARP table.
|
|
|
|
if ((entry = ARPLookup(ai, Destination, &lhandle)) != (ARPTableEntry *)NULL) {
|
|
// Found a matching entry. ARPLookup returns with the ATE lock held.
|
|
if (RCE != (RouteCacheEntry *)NULL) {
|
|
ac->ac_next = entry->ate_rce; // Fill in context for next time.
|
|
entry->ate_rce = RCE;
|
|
ac->ac_ate = entry;
|
|
}
|
|
CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
|
|
return ARPSendData(ai, Packet, entry, tlhandle);
|
|
}
|
|
|
|
// No valid entry in the ARP table. First we'll see if we're sending to a
|
|
// broadcast address or multicast address. If not, we'll try to create
|
|
// an entry in the table and get an ARP resolution going. ARPLookup returns
|
|
// with the table lock held when it fails, we'll free it here.
|
|
CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
|
|
|
|
if (ARPSendBCast(ai, Destination, Packet, &Status))
|
|
return Status;
|
|
|
|
entry = CreateARPTableEntry(ai, Destination, &lhandle);
|
|
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) {
|
|
entry->ate_packet = Packet;
|
|
CTEFreeLock(&entry->ate_lock, lhandle);
|
|
SendARPRequest(ai, Destination, entry->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
|
|
return ARPSendData(ai, Packet, entry, lhandle);
|
|
|
|
} else {
|
|
if (entry->ate_state == ARP_GOOD) // Yow! A valid entry.
|
|
return ARPSendData(ai, Packet, entry, lhandle);
|
|
else { // An invalid entry!
|
|
CTEFreeLock(&entry->ate_lock, lhandle);
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
}
|
|
} else // Couldn't create an entry.
|
|
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;
|
|
}
|
|
|
|
//* 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
|
|
ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset, uint ByteOffset,
|
|
uint BytesWanted, PNDIS_PACKET Packet, uint *Transferred)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context;
|
|
NDIS_STATUS Status;
|
|
|
|
NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset+MyOffset,
|
|
BytesWanted, Packet, Transferred);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//* ARPClose - Close an adapter.
|
|
//
|
|
// Called by IP when it wants to close an adapter, presumably due to an error condition.
|
|
// We'll close the adapter, but we won't free any memory.
|
|
//
|
|
// Entry: Context - Context value we gave him earlier.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
ARPClose(void *Context)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context;
|
|
NDIS_STATUS Status;
|
|
CTELockHandle LockHandle;
|
|
NDIS_HANDLE Handle;
|
|
|
|
Interface->ai_operstate = IF_STATUS_DOWN;
|
|
Interface->ai_state = INTERFACE_DOWN;
|
|
CTEInitBlockStruc(&Interface->ai_block);
|
|
|
|
CTEGetLock(&Interface->ai_lock, &LockHandle);
|
|
if (Interface->ai_handle != (NDIS_HANDLE)NULL) {
|
|
Handle = Interface->ai_handle;
|
|
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);
|
|
}
|
|
}
|
|
|
|
//* 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
|
|
ARPInvalidate(void *Context, RouteCacheEntry *RCE)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context;
|
|
ARPTableEntry *ATE;
|
|
CTELockHandle Handle, ATEHandle;
|
|
ARPContext *AC = (ARPContext *)RCE->rce_context;
|
|
|
|
CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
|
|
if ((ATE = AC->ac_ate) == (ARPTableEntry *)NULL) {
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE.
|
|
return;
|
|
}
|
|
|
|
CTEGetLock(&ATE->ate_lock, &ATEHandle);
|
|
ARPRemoveRCE(ATE, RCE);
|
|
CTEMemSet(RCE->rce_context, 0, RCE_CONTEXT_SIZE);
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, ATEHandle);
|
|
CTEFreeLock(&ATE->ate_lock, Handle);
|
|
|
|
}
|
|
|
|
//* ARPSetMCastList - Set the multicast address list for the adapter.
|
|
//
|
|
// Called to try and set the multicast reception list for the adapter.
|
|
// We allocate a buffer big enough to hold the new address list, and format
|
|
// the address list into the buffer. Then we submit the NDIS request to set
|
|
// the list. If we can't set the list because the multicast address list is
|
|
// full we'll put the card into all multicast mode.
|
|
//
|
|
// Input: Interface - Interface on which to set list.
|
|
//
|
|
// Returns: NDIS_STATUS of attempt.
|
|
//
|
|
NDIS_STATUS
|
|
ARPSetMCastList(ARPInterface *Interface)
|
|
{
|
|
CTELockHandle Handle;
|
|
uchar *MCastBuffer, *CurrentPtr;
|
|
uint MCastSize;
|
|
NDIS_STATUS Status;
|
|
uint i;
|
|
ARPMCastAddr *AddrPtr;
|
|
IPAddr UNALIGNED *Temp;
|
|
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH;
|
|
if (MCastSize != 0)
|
|
MCastBuffer = CTEAllocMem(MCastSize);
|
|
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++) {
|
|
CTEAssert(AddrPtr != NULL);
|
|
|
|
if (Interface->ai_media == NdisMedium802_3) {
|
|
|
|
CTEMemCopy(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH);
|
|
Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2);
|
|
*Temp |= AddrPtr->ama_addr;
|
|
} else
|
|
if (Interface->ai_media == NdisMediumFddi) {
|
|
CTEMemCopy(CurrentPtr, ((FDDIHeader *)FDDIMcst)->fh_daddr,
|
|
ARP_802_ADDR_LENGTH);
|
|
Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2);
|
|
*Temp |= AddrPtr->ama_addr;
|
|
} else
|
|
DEBUGCHK;
|
|
|
|
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);
|
|
} else
|
|
if (Interface->ai_media == NdisMediumFddi) {
|
|
Status = DoNDISRequest(Interface, NdisRequestSetInformation,
|
|
OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL);
|
|
} else
|
|
DEBUGCHK;
|
|
|
|
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);
|
|
}
|
|
|
|
} 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) {
|
|
// This is an interface that supports mcast addresses.
|
|
Addr &= ARP_MCAST_MASK;
|
|
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
|
|
if (AddrPtr != NULL) {
|
|
// We found one. Dec. his refcnt, and if it's 0 delete him.
|
|
(AddrPtr->ama_refcnt)--;
|
|
if (AddrPtr->ama_refcnt == 0) {
|
|
// He's done.
|
|
PrevPtr->ama_next = AddrPtr->ama_next;
|
|
(Interface->ai_mcastcnt)--;
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
CTEFreeMem(AddrPtr);
|
|
ARPSetMCastList(Interface);
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
}
|
|
} else
|
|
Status = FALSE;
|
|
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
//* ARPAddMCast - Add a multicast address.
|
|
//
|
|
// Called when we want to start receiving a multicast address. We'll mask
|
|
// the address and look it up in our address list. If we find it, we'll just
|
|
// bump the reference count. Otherwise we'll try to create one and put him
|
|
// on the list. In that case we'll need to set the multicast address list for
|
|
// the adapter.
|
|
//
|
|
// Input: Interface - Interface to set on.
|
|
// Addr - Address to set.
|
|
//
|
|
// Returns: TRUE if we succeed, FALSE if we fail.
|
|
//
|
|
uint
|
|
ARPAddMCast(ARPInterface *Interface, IPAddr Addr)
|
|
{
|
|
ARPMCastAddr *AddrPtr, *PrevPtr;
|
|
CTELockHandle Handle;
|
|
uint Status = TRUE;
|
|
|
|
|
|
if (Interface->ai_state != INTERFACE_UP)
|
|
return FALSE;
|
|
|
|
// BUGBUG 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) {
|
|
// 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, 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 = CTEAllocMem(sizeof(ARPMCastAddr));
|
|
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);
|
|
if (!Status)
|
|
DEBUGCHK;
|
|
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 pointer)
|
|
// 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
|
|
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 = ARPADDR_NEW_LOCAL;
|
|
if (Interface->ai_state == INTERFACE_UP) {
|
|
Interface->ai_ipaddr.aia_context = Context2;
|
|
ArpForSelf = TRUE;
|
|
} else {
|
|
Interface->ai_ipaddr.aia_context = NULL;
|
|
}
|
|
RetStatus = TRUE;
|
|
} else {
|
|
ARPIPAddr *NewAddr;
|
|
|
|
NewAddr = CTEAllocMem(sizeof(ARPIPAddr));
|
|
if (NewAddr != (ARPIPAddr *)NULL) {
|
|
NewAddr->aia_addr = Address;
|
|
NewAddr->aia_mask = Mask;
|
|
NewAddr->aia_age = ARPADDR_NEW_LOCAL;
|
|
NewAddr->aia_next = Interface->ai_ipaddr.aia_next;
|
|
if (Interface->ai_state == INTERFACE_UP) {
|
|
NewAddr->aia_context = Context2;
|
|
ArpForSelf = TRUE;
|
|
} else {
|
|
NewAddr->aia_context = NULL;
|
|
}
|
|
|
|
Interface->ai_ipaddr.aia_next = NewAddr;
|
|
RetStatus = TRUE;
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
// ARP for the address we've added, to see it it already exists.
|
|
if (RetStatus == TRUE && ArpForSelf == TRUE) {
|
|
SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL,
|
|
NULL, TRUE);
|
|
return IP_PENDING;
|
|
}
|
|
|
|
return RetStatus;
|
|
} else if (Type == LLIP_ADDR_PARP) {
|
|
ARPPArpAddr *NewPArp;
|
|
|
|
// He's adding a proxy arp address.
|
|
NewPArp = CTEAllocMem(sizeof(ARPPArpAddr));
|
|
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
|
|
ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context;
|
|
CTELockHandle Handle;
|
|
ARPIPAddr *DelAddr, *PrevAddr;
|
|
ARPPArpAddr *DelPAddr, *PrevPAddr;
|
|
|
|
if (Type == LLIP_ADDR_LOCAL) {
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
|
|
if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) {
|
|
Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR;
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
return TRUE;
|
|
} else {
|
|
PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next);
|
|
DelAddr = PrevAddr->aia_next;
|
|
while (DelAddr != NULL)
|
|
if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address))
|
|
break;
|
|
else {
|
|
PrevAddr = DelAddr;
|
|
DelAddr = DelAddr->aia_next;
|
|
}
|
|
|
|
if (DelAddr != NULL) {
|
|
PrevAddr->aia_next = DelAddr->aia_next;
|
|
CTEFreeMem(DelAddr);
|
|
}
|
|
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;
|
|
}
|
|
|
|
//* ARPTimeout - ARP timeout routine.
|
|
//
|
|
// This is the timeout routine that is called periodically. We scan the ARP table, looking
|
|
// for invalid entries that can be removed.
|
|
//
|
|
// Entry: Timer - Pointer to the timer that just fired.
|
|
// Context - Pointer to the interface to be timed out.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void
|
|
ARPTimeout(CTEEvent *Timer, void *Context)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context; // Our interface.
|
|
ARPTable *Table;
|
|
ARPTableEntry *Current, *Previous;
|
|
int i; // Index variable.
|
|
ulong Now = CTESystemUpTime(), ValidTime;
|
|
CTELockHandle tblhandle, entryhandle;
|
|
uchar Deleted;
|
|
PNDIS_PACKET PList = (PNDIS_PACKET)NULL;
|
|
ARPIPAddr *Addr;
|
|
|
|
// Walk down the list of addresses, decrementing the age.
|
|
CTEGetLock(&Interface->ai_lock, &tblhandle);
|
|
|
|
Addr = &Interface->ai_ipaddr;
|
|
|
|
do {
|
|
if (Addr->aia_age != ARPADDR_OLD_LOCAL) {
|
|
(Addr->aia_age)--;
|
|
if (Addr->aia_age == ARPADDR_OLD_LOCAL) {
|
|
if (Addr->aia_context != NULL) {
|
|
SetAddrControl *SAC;
|
|
SetAddrRtn Rtn;
|
|
|
|
SAC = (SetAddrControl *)Addr->aia_context;
|
|
Rtn = (SetAddrRtn)SAC->sac_rtn;
|
|
CTEFreeLock(&Interface->ai_lock, tblhandle);
|
|
(*Rtn)(SAC, IP_SUCCESS);
|
|
CTEGetLock(&Interface->ai_lock, &tblhandle);
|
|
Addr->aia_context = NULL;
|
|
}
|
|
} else {
|
|
CTEFreeLock(&Interface->ai_lock, tblhandle);
|
|
SendARPRequest(Interface, Addr->aia_addr, ARP_RESOLVING_GLOBAL,
|
|
NULL, TRUE);
|
|
CTEGetLock(&Interface->ai_lock, &tblhandle);
|
|
}
|
|
}
|
|
|
|
Addr = Addr->aia_next;
|
|
} while (Addr != NULL);
|
|
|
|
CTEFreeLock(&Interface->ai_lock, tblhandle);
|
|
|
|
// Loop through the ARP table for this interface, and delete stale entries.
|
|
CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
|
|
Table = Interface->ai_ARPTbl;
|
|
for (i = 0; i < ARP_TABLE_SIZE;i++) {
|
|
Previous = (ARPTableEntry *)((uchar *)&((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next));
|
|
Current = (*Table)[i];
|
|
while (Current != (ARPTableEntry *)NULL) {
|
|
CTEGetLock(&Current->ate_lock, &entryhandle);
|
|
Deleted = 0;
|
|
|
|
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 < ARP_MIN_VALID_TIMEOUT) {
|
|
ValidTime = ARP_MIN_VALID_TIMEOUT;
|
|
}
|
|
}
|
|
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 (Packet != (PNDIS_PACKET)NULL) {
|
|
((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link
|
|
= PList;
|
|
PList = Packet;
|
|
}
|
|
RemoveARPTableEntry(Previous, Current);
|
|
Interface->ai_count--;
|
|
Deleted = 1;
|
|
} 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.
|
|
CTEAssert(Interface->ai_media == NdisMedium802_5);
|
|
|
|
Now = CTESystemUpTime();
|
|
Current->ate_valid = Now;
|
|
Current->ate_state = ARP_RESOLVING_GLOBAL;
|
|
CTEFreeLock(&Current->ate_lock, entryhandle);
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
|
|
// Send a global request.
|
|
SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
|
|
NULL, TRUE);
|
|
CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
|
|
|
|
// Since we've freed the locks, we need to start over from
|
|
// the start of this chain.
|
|
Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]),
|
|
ate_next);
|
|
Current = (*Table)[i];
|
|
continue;
|
|
|
|
}
|
|
}
|
|
|
|
// If we deleted the entry, leave the previous pointer alone, advance the
|
|
// current pointer, and free the memory. Otherwise move both pointers forward.
|
|
// We can free the entry lock now because the next pointers are protected by
|
|
// the table lock, and we've removed it from the list so nobody else should
|
|
// find it anyway.
|
|
CTEFreeLock(&Current->ate_lock, entryhandle);
|
|
if (Deleted) {
|
|
ARPTableEntry *Temp = Current;
|
|
Current = Current->ate_next;
|
|
CTEFreeMem(Temp);
|
|
} else {
|
|
Previous = Current;
|
|
Current = Current->ate_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
|
|
|
|
while (PList != (PNDIS_PACKET)NULL) {
|
|
PNDIS_PACKET Packet = PList;
|
|
|
|
PList = ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link;
|
|
IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
|
|
}
|
|
|
|
CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface);
|
|
}
|
|
|
|
//* 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;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
#ifdef VXD
|
|
|
|
|
|
#ifndef CHICAGO
|
|
extern void DisplayPopup(uchar *Msg);
|
|
uchar CMsg1[] = "The system has detected a conflict for IP address ";
|
|
uchar CMsg2[] = " with the system having hardware address ";
|
|
uchar CMsg3[] = ". The local interface has been disabled";
|
|
|
|
uchar CMsg[sizeof(CMsg1) - 1 + sizeof(CMsg2) - 1 + sizeof(CMsg3) - 1 +
|
|
((sizeof(IPAddr) * 4) - 1) + ((ARP_802_ADDR_LENGTH * 3) - 1)
|
|
+ 1 + 1];
|
|
#else
|
|
extern void NotifyConflictProc(CTEEvent *Event, void *Context);
|
|
extern void DisplayConflictPopup(uchar *IPAddr, uchar *HWAddr, uint Shutoff);
|
|
#endif // CHICAGO
|
|
|
|
#endif // NT
|
|
|
|
//* 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
|
|
#ifndef CHICAGO
|
|
NotifyConflictProc(CTEEvent *Event, void *Context)
|
|
#else
|
|
DisplayConflictProc(void *Context)
|
|
#endif
|
|
{
|
|
#ifdef VXD
|
|
uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
|
|
uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
|
|
uint i;
|
|
uint IPAddrCharCount;
|
|
ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context;
|
|
|
|
#ifndef CHICAGO
|
|
uint TotalSize;
|
|
|
|
CTEMemCopy(CMsg, CMsg1, sizeof(CMsg1) - 1);
|
|
TotalSize = sizeof(CMsg1) - 1;
|
|
#endif
|
|
|
|
// Convert the IP address into a string.
|
|
IPAddrCharCount = 0;
|
|
|
|
for (i = 0; i < sizeof(IPAddr); i++) {
|
|
uint CurrentByte;
|
|
|
|
CurrentByte = NotifyStruct->ans_addr & 0xff;
|
|
if (CurrentByte > 99) {
|
|
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
|
|
CurrentByte %= 100;
|
|
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
} else if (CurrentByte > 9) {
|
|
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
}
|
|
|
|
IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
|
|
if (i != (sizeof(IPAddr) - 1))
|
|
IPAddrBuffer[IPAddrCharCount++] = '.';
|
|
|
|
NotifyStruct->ans_addr >>= 8;
|
|
}
|
|
|
|
#ifndef CHICAGO
|
|
CTEMemCopy(&CMsg[TotalSize], IPAddrBuffer, IPAddrCharCount);
|
|
TotalSize += IPAddrCharCount;
|
|
|
|
CTEMemCopy(&CMsg[TotalSize], CMsg2, sizeof(CMsg2) - 1);
|
|
TotalSize += sizeof(CMsg2) - 1;
|
|
#else
|
|
IPAddrBuffer[IPAddrCharCount] = '\0';
|
|
#endif
|
|
|
|
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] = ':';
|
|
}
|
|
|
|
#ifndef CHICAGO
|
|
CTEMemCopy(&CMsg[TotalSize], HWAddrBuffer,
|
|
(NotifyStruct->ans_hwaddrlen * 3) - 1);
|
|
TotalSize += (NotifyStruct->ans_hwaddrlen * 3) - 1;
|
|
|
|
if (NotifyStruct->ans_shutoff) {
|
|
CTEMemCopy(&CMsg[TotalSize], CMsg3, sizeof(CMsg3) - 1);
|
|
TotalSize += sizeof(CMsg3) - 1;
|
|
}
|
|
|
|
CMsg[TotalSize] = '.';
|
|
CMsg[TotalSize+1] = '\0';
|
|
|
|
DisplayPopup(CMsg);
|
|
#else
|
|
HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0';
|
|
DisplayConflictPopup(IPAddrBuffer, HWAddrBuffer, NotifyStruct->ans_shutoff);
|
|
CTEFreeMem(NotifyStruct);
|
|
#endif
|
|
|
|
#else // VXD
|
|
|
|
ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context;
|
|
PWCHAR stringList[2];
|
|
uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
|
|
uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
|
|
WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)];
|
|
WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
|
|
uint i;
|
|
uint IPAddrCharCount;
|
|
UNICODE_STRING unicodeString;
|
|
ANSI_STRING ansiString;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Convert the IP address into a string.
|
|
//
|
|
IPAddrCharCount = 0;
|
|
|
|
for (i = 0; i < sizeof(IPAddr); i++) {
|
|
uint CurrentByte;
|
|
|
|
CurrentByte = NotifyStruct->ans_addr & 0xff;
|
|
if (CurrentByte > 99) {
|
|
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
|
|
CurrentByte %= 100;
|
|
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
} else if (CurrentByte > 9) {
|
|
IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
|
|
CurrentByte %= 10;
|
|
}
|
|
|
|
IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
|
|
if (i != (sizeof(IPAddr) - 1))
|
|
IPAddrBuffer[IPAddrCharCount++] = '.';
|
|
|
|
NotifyStruct->ans_addr >>= 8;
|
|
}
|
|
|
|
//
|
|
// Convert the hardware address into a string.
|
|
//
|
|
for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
|
|
uchar CurrentHalf;
|
|
|
|
CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
|
|
HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
|
|
(CurrentHalf - 10) + 'A');
|
|
CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
|
|
HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
|
|
(CurrentHalf - 10) + 'A');
|
|
if (i != (NotifyStruct->ans_hwaddrlen - 1))
|
|
HWAddrBuffer[(i*3)+2] = ':';
|
|
}
|
|
|
|
//
|
|
// Unicode the strings.
|
|
//
|
|
*unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL;
|
|
|
|
unicodeString.Buffer = unicodeIPAddrBuffer;
|
|
unicodeString.Length = 0;
|
|
unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1);
|
|
ansiString.Buffer = IPAddrBuffer;
|
|
ansiString.Length = IPAddrCharCount;
|
|
ansiString.MaximumLength = IPAddrCharCount;
|
|
|
|
RtlAnsiStringToUnicodeString(
|
|
&unicodeString,
|
|
&ansiString,
|
|
FALSE
|
|
);
|
|
|
|
stringList[0] = unicodeIPAddrBuffer;
|
|
|
|
unicodeString.Buffer = unicodeHWAddrBuffer;
|
|
unicodeString.Length = 0;
|
|
unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3);
|
|
ansiString.Buffer = HWAddrBuffer;
|
|
ansiString.Length = (NotifyStruct->ans_hwaddrlen * 3) - 1;
|
|
ansiString.MaximumLength = NotifyStruct->ans_hwaddrlen * 3;
|
|
|
|
RtlAnsiStringToUnicodeString(
|
|
&unicodeString,
|
|
&ansiString,
|
|
FALSE
|
|
);
|
|
|
|
stringList[1] = unicodeHWAddrBuffer;
|
|
|
|
//
|
|
// Kick off a popup and log an event.
|
|
//
|
|
if (NotifyStruct->ans_shutoff) {
|
|
CTELogEvent(
|
|
IPDriverObject,
|
|
EVENT_TCPIP_ADDRESS_CONFLICT1,
|
|
0,
|
|
2,
|
|
stringList,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
IoRaiseInformationalHardError(
|
|
STATUS_IP_ADDRESS_CONFLICT1,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
CTELogEvent(
|
|
IPDriverObject,
|
|
EVENT_TCPIP_ADDRESS_CONFLICT2,
|
|
0,
|
|
2,
|
|
stringList,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
IoRaiseInformationalHardError(
|
|
STATUS_IP_ADDRESS_CONFLICT2,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
CTEFreeMem(NotifyStruct);
|
|
|
|
#endif // VXD
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//* HandleARPPacket - Process an incoming ARP packet.
|
|
//
|
|
// This is the main routine to process an incoming ARP packet. We look at all ARP frames,
|
|
// and update our cache entry for the source address if one exists. Else, if we are the
|
|
// target we create an entry if one doesn't exist. Finally, we'll handle the opcode,
|
|
// responding if this is a request or sending pending packets if this is a response.
|
|
//
|
|
// Entry: Interface - Pointer to interface structure for this adapter.
|
|
// Header - Pointer to header buffer.
|
|
// HeaderSize - Size of header buffer.
|
|
// ARPHdr - ARP packet header.
|
|
// ARPHdrSize - Size of ARP header.
|
|
// ProtOffset - Offset into original data field of arp header.
|
|
// Will be non-zero if we're using SNAP.
|
|
//
|
|
// Returns: An NDIS_STATUS value to be returned to the NDIS driver.
|
|
//
|
|
NDIS_STATUS
|
|
HandleARPPacket(ARPInterface *Interface, void *Header, uint HeaderSize,
|
|
ARPHeader UNALIGNED *ARPHdr, uint ARPHdrSize, uint ProtOffset)
|
|
{
|
|
ARPTableEntry *Entry; // Entry in ARP table
|
|
CTELockHandle LHandle, TableHandle;
|
|
RC UNALIGNED *SourceRoute = (RC UNALIGNED *)NULL; // Pointer to Source Route info, if any.
|
|
uint SourceRouteSize = 0;
|
|
ulong Now = CTESystemUpTime();
|
|
uchar LocalAddr;
|
|
uint LocalAddrAge;
|
|
uchar *SHAddr, *DHAddr;
|
|
IPAddr UNALIGNED *SPAddr, *DPAddr;
|
|
ENetHeader *ENetHdr;
|
|
TRHeader *TRHdr;
|
|
FDDIHeader *FHdr;
|
|
ARCNetHeader *AHdr;
|
|
ushort MaxMTU;
|
|
uint UseSNAP;
|
|
SetAddrControl *SAC;
|
|
SetAddrRtn Rtn = NULL;
|
|
|
|
// 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;
|
|
|
|
if (IP_ADDR_EQUAL(*SPAddr, NULL_IP_ADDR))
|
|
return NDIS_STATUS_NOT_RECOGNIZED;
|
|
|
|
// First, let's see if we have an address conflict.
|
|
LocalAddrAge = IsLocalAddr(Interface, *SPAddr);
|
|
if (LocalAddrAge != ARPADDR_NOT_LOCAL) {
|
|
// The source IP address is one of ours. See if the source h/w address
|
|
// is ours also.
|
|
if (ARPHdr->ah_hlen != Interface->ai_addrlen ||
|
|
CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) {
|
|
|
|
uint Shutoff;
|
|
ARPNotifyStruct *NotifyStruct;
|
|
|
|
// This isn't from us; we must have an address conflict somewhere.
|
|
// We always log an error about this. If what triggered this is a
|
|
// response and the address in conflict is young, we'll turn off
|
|
// the interface.
|
|
if (LocalAddrAge != ARPADDR_OLD_LOCAL &&
|
|
ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) {
|
|
// Send an arp request with the owner's address to reset the
|
|
// caches.
|
|
|
|
CTEGetLock(&Interface->ai_lock, &LHandle);
|
|
Interface->ai_state = INTERFACE_DOWN;
|
|
Interface->ai_adminstate = IF_STATUS_DOWN;
|
|
if (Interface->ai_ipaddr.aia_context != NULL) {
|
|
SAC = (SetAddrControl *)Interface->ai_ipaddr.aia_context;
|
|
Rtn = (SetAddrRtn)SAC->sac_rtn;
|
|
Interface->ai_ipaddr.aia_context = NULL;
|
|
}
|
|
CTEFreeLock(&Interface->ai_lock, LHandle);
|
|
|
|
SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL,
|
|
SHAddr, FALSE); // Send a request.
|
|
|
|
Shutoff = TRUE;
|
|
|
|
if (Rtn != NULL) {
|
|
//
|
|
// this is a dhcp adapter. report the conflict to
|
|
// CompleteIPSetNTEAddrRequest so IOCTL_IP_SET_ADDRESS will
|
|
// be completed
|
|
//
|
|
|
|
(*Rtn)(SAC, IP_GENERAL_FAILURE);
|
|
|
|
//
|
|
// don't display a warning dialog in this case - DHCP will
|
|
// alert the user
|
|
//
|
|
|
|
goto no_dialog;
|
|
}
|
|
} else {
|
|
if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) &&
|
|
(IsLocalAddr(Interface, *DPAddr) != ARPADDR_NOT_LOCAL)) {
|
|
// Send a response.
|
|
SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr,
|
|
SourceRoute, SourceRouteSize, UseSNAP);
|
|
}
|
|
Shutoff = FALSE;
|
|
}
|
|
|
|
// Now allocate a structure, and schedule an event to notify
|
|
// the user.
|
|
NotifyStruct = CTEAllocMem(offsetof(ARPNotifyStruct, ans_hwaddr) +
|
|
ARPHdr->ah_hlen);
|
|
if (NotifyStruct != NULL) {
|
|
NotifyStruct->ans_addr = *SPAddr;
|
|
NotifyStruct->ans_shutoff = Shutoff;
|
|
NotifyStruct->ans_hwaddrlen = (uint)ARPHdr->ah_hlen;
|
|
CTEMemCopy(NotifyStruct->ans_hwaddr, SHAddr,
|
|
ARPHdr->ah_hlen);
|
|
CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc);
|
|
CTEScheduleEvent(&NotifyStruct->ans_event, NotifyStruct);
|
|
}
|
|
|
|
|
|
no_dialog:
|
|
;
|
|
|
|
}
|
|
return NDIS_STATUS_NOT_RECOGNIZED;
|
|
}
|
|
|
|
CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
|
|
MaxMTU = Interface->ai_mtu;
|
|
|
|
LocalAddr = ARPLocalAddr(Interface, *DPAddr);
|
|
Entry = ARPLookup(Interface, *SPAddr, &LHandle);
|
|
if (Entry == (ARPTableEntry *)NULL) {
|
|
|
|
// Didn't find him, create one if it's for us. The call to ARPLookup
|
|
// returned with the ARPTblLock held, so we need to free it.
|
|
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
|
|
if (LocalAddr)
|
|
Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle);
|
|
else
|
|
return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us.
|
|
} else {
|
|
CTEFreeLock(&Interface->ai_ARPTblLock, LHandle);
|
|
LHandle = TableHandle;
|
|
}
|
|
|
|
// At this point, entry should be valid, and we hold the lock on the entry
|
|
// in LHandle.
|
|
|
|
if (Entry != (ARPTableEntry *)NULL) {
|
|
PNDIS_PACKET Packet; // Packet to be sent.
|
|
|
|
// If the entry is already static, we'll want to leave it as static.
|
|
if (Entry->ate_valid == ALWAYS_VALID)
|
|
Now = 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;
|
|
|
|
CTEMemCopy(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(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) {
|
|
CTEMemCopy(&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.
|
|
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;
|
|
CTEMemCopy(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr,
|
|
ARP_802_ADDR_LENGTH);
|
|
if (SourceRoute != (RC UNALIGNED *)NULL) {
|
|
uchar MaxIFieldBits;
|
|
|
|
// We have source routing information.
|
|
CTEMemCopy(&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;
|
|
|
|
}
|
|
CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)+SourceRouteSize],
|
|
ARPSNAP, sizeof(SNAPHeader));
|
|
Entry->ate_state = ARP_GOOD;
|
|
Entry->ate_valid = Now;
|
|
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;
|
|
CTEMemCopy(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr,
|
|
ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(&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.
|
|
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:
|
|
DEBUGCHK;
|
|
break;
|
|
}
|
|
|
|
// 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.
|
|
CTEAssert(Entry->ate_state == ARP_GOOD);
|
|
|
|
Entry->ate_packet = NULL;
|
|
|
|
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.
|
|
CTEAssert(Interface->ai_media == NdisMedium802_5);
|
|
IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE,
|
|
&LAM, sizeof(LLIPAddrMTUChange));
|
|
|
|
}
|
|
|
|
// At this point we've updated the entry (if we had one), and we've free
|
|
// 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))
|
|
!= NDIS_STATUS_SUCCESS) {
|
|
Adapter->ai_state = INTERFACE_DOWN;
|
|
return;
|
|
}
|
|
|
|
if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
|
|
OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint),
|
|
NULL)) == NDIS_STATUS_SUCCESS) {
|
|
Adapter->ai_adminstate = IF_STATUS_UP;
|
|
Adapter->ai_operstate = IF_STATUS_UP;
|
|
Adapter->ai_state = INTERFACE_UP;
|
|
// Now walk through any addresses we have, and ARP for them.
|
|
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 = ARPADDR_NEW_LOCAL;
|
|
CTEFreeLock(&Adapter->ai_lock, Handle);
|
|
OldAddr = Addr;
|
|
SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL,
|
|
NULL, TRUE);
|
|
CTEGetLock(&Adapter->ai_lock, &Handle);
|
|
}
|
|
Addr = &Adapter->ai_ipaddr;
|
|
while (Addr != OldAddr && Addr != NULL)
|
|
Addr = Addr->aia_next;
|
|
if (Addr != NULL)
|
|
Addr = Addr->aia_next;
|
|
} while (Addr != NULL);
|
|
|
|
CTEFreeLock(&Adapter->ai_lock, Handle);
|
|
|
|
} else
|
|
Adapter->ai_state = INTERFACE_DOWN;
|
|
|
|
}
|
|
|
|
//** ARPOAComplete - ARP Open adapter complete handler.
|
|
//
|
|
// This routine is called by the NDIS driver when an open adapter
|
|
// call completes. Presumably somebody is blocked waiting for this, so
|
|
// we'll wake him up now.
|
|
//
|
|
// Entry:
|
|
// Handle - The binding handle we specified (really a pointer to an AI).
|
|
// Status - Final status of command.
|
|
// ErrorStatus - Final error status.
|
|
//
|
|
// Exit: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus)
|
|
{
|
|
ARPInterface *ai = (ARPInterface *)Handle; // For compiler.
|
|
|
|
CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status.
|
|
|
|
}
|
|
|
|
//** ARPCAComplete - ARP close adapter complete handler.
|
|
//
|
|
// This routine is called by the NDIS driver when a close adapter
|
|
// call completes.
|
|
//
|
|
// Entry:
|
|
// Handle - The binding handle we specified (really a pointer to an AI).
|
|
// Status - Final status of command.
|
|
//
|
|
// Exit: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
|
|
{
|
|
ARPInterface *ai = (ARPInterface *)Handle; // For compiler.
|
|
|
|
CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status.
|
|
|
|
}
|
|
|
|
//** ARPSendComplete - ARP send complete handler.
|
|
//
|
|
// This routine is called by the NDIS driver when a send completes.
|
|
// This is a pretty time critical operation, we need to get through here
|
|
// quickly. We'll strip our buffer off and put it back, and call the upper
|
|
// later send complete handler.
|
|
//
|
|
// Entry:
|
|
// Handle - The binding handle we specified (really a pointer to an AI).
|
|
// Packet - A pointer to the packet that was sent.
|
|
// Status - Final status of command.
|
|
//
|
|
// Exit: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Handle;
|
|
PacketContext *PC = (PacketContext *)Packet->ProtocolReserved;
|
|
PNDIS_BUFFER Buffer;
|
|
|
|
Interface->ai_qlen--;
|
|
#ifdef VXD
|
|
CTEAssert(*(int *)&Interface->ai_qlen >= 0);
|
|
#endif
|
|
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
Interface->ai_outoctets += Packet->Private.TotalLength;
|
|
} else {
|
|
if (Status == NDIS_STATUS_RESOURCES)
|
|
Interface->ai_outdiscards++;
|
|
else
|
|
Interface->ai_outerrors++;
|
|
}
|
|
|
|
// Get first buffer on packet.
|
|
NdisUnchainBufferAtFront(Packet, &Buffer);
|
|
#ifdef DEBUG
|
|
if (Buffer == (PNDIS_BUFFER)NULL) // No buffer!
|
|
DEBUGCHK;
|
|
#endif
|
|
|
|
FreeARPBuffer(Interface, Buffer); // Free it up.
|
|
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)
|
|
{
|
|
}
|
|
|
|
//** ARPRequestComplete - ARP request complete handler.
|
|
//
|
|
// This routine is called by the NDIS driver when a general request
|
|
// completes. ARP blocks on all requests, so we'll just wake up
|
|
// whoever's blocked on this request.
|
|
//
|
|
// 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 Request,
|
|
NDIS_STATUS Status)
|
|
{
|
|
ARPInterface *ai = (ARPInterface *)Handle;
|
|
|
|
CTESignal(&ai->ai_block, (uint)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; // Interface for this driver.
|
|
ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *)Header;
|
|
SNAPHeader UNALIGNED *SNAPHdr;
|
|
ushort type; // Protocol type
|
|
uint ProtOffset; // Offset in Data to non-media info.
|
|
uint NUCast; // TRUE if the frame is not
|
|
// a unicast frame.
|
|
|
|
if (Interface->ai_state == INTERFACE_UP &&
|
|
HeaderSize >= (uint)Interface->ai_hdrsize) {
|
|
|
|
Interface->ai_inoctets += TotalSize;
|
|
|
|
if (Interface->ai_media != NdisMediumArcnet878_2) {
|
|
if (Interface->ai_media == NdisMedium802_3 &&
|
|
(type = net_short(EHdr->eh_type)) >= MIN_ETYPE)
|
|
ProtOffset = 0;
|
|
else {
|
|
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 {
|
|
// BUGBUG 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;
|
|
}
|
|
|
|
NUCast = ((*((uchar UNALIGNED *)EHdr + Interface->ai_bcastoff) &
|
|
Interface->ai_bcastmask) == Interface->ai_bcastval) ?
|
|
AI_NONUCAST_INDEX : AI_UCAST_INDEX;
|
|
|
|
if (type == ARP_ETYPE_IP) {
|
|
|
|
(Interface->ai_inpcount[NUCast])++;
|
|
IPRcv(Interface->ai_context, (uchar *)Data+ProtOffset,
|
|
Size-ProtOffset, TotalSize-ProtOffset, Context, ProtOffset,
|
|
NUCast);
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
if (type == ARP_ETYPE_ARP) {
|
|
(Interface->ai_inpcount[NUCast])++;
|
|
return HandleARPPacket(Interface, Header, HeaderSize,
|
|
(ARPHeader *)((uchar *)Data+ProtOffset), Size-ProtOffset,
|
|
ProtOffset);
|
|
} else {
|
|
Interface->ai_uknprotos++;
|
|
return NDIS_STATUS_NOT_RECOGNIZED;
|
|
}
|
|
}
|
|
} else {
|
|
// Interface is marked as down.
|
|
return NDIS_STATUS_NOT_RECOGNIZED;
|
|
}
|
|
}
|
|
|
|
//** ARPRcvComplete - ARP receive complete handler.
|
|
//
|
|
// This routine is called by the NDIS driver after some number of
|
|
// receives. In some sense, it indicates 'idle time'.
|
|
//
|
|
// Entry:
|
|
// Handle - The binding handle we specified (really a pointer to an AI).
|
|
//
|
|
// Exit: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPRcvComplete(NDIS_HANDLE Handle)
|
|
{
|
|
IPRcvComplete();
|
|
|
|
}
|
|
|
|
|
|
//** ARPStatus - ARP status handler.
|
|
//
|
|
// Called by the NDIS driver when some sort of status change occurs.
|
|
// We take action depending on the type of status.
|
|
//
|
|
// Entry:
|
|
// Handle - The binding handle we specified (really a pointer to an AI).
|
|
// GStatus - General type of status that caused the call.
|
|
// Status - Pointer to a buffer of status specific information.
|
|
// StatusSize - Size of the status buffer.
|
|
//
|
|
// Exit: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint
|
|
StatusSize)
|
|
{
|
|
ARPInterface *ai = (ARPInterface *)Handle;
|
|
|
|
IPStatus(ai->ai_context, GStatus, Status, StatusSize);
|
|
|
|
}
|
|
|
|
//** 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)
|
|
{
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
#ifndef NT
|
|
NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = {
|
|
NDIS_MAJOR_VERSION,
|
|
NDIS_MINOR_VERSION,
|
|
0,
|
|
ARPOAComplete,
|
|
ARPCAComplete,
|
|
ARPSendComplete,
|
|
ARPTDComplete,
|
|
ARPResetComplete,
|
|
ARPRequestComplete,
|
|
ARPRcv,
|
|
ARPRcvComplete,
|
|
ARPStatus,
|
|
ARPStatusComplete,
|
|
#ifdef CHICAGO
|
|
ARPBindAdapter,
|
|
ARPUnbindAdapter,
|
|
ARPUnloadProtocol,
|
|
#endif
|
|
{ sizeof(TCP_NAME),
|
|
sizeof(TCP_NAME),
|
|
0
|
|
}
|
|
};
|
|
#else // NT
|
|
NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = {
|
|
NDIS_MAJOR_VERSION,
|
|
NDIS_MINOR_VERSION,
|
|
0,
|
|
ARPOAComplete,
|
|
ARPCAComplete,
|
|
ARPSendComplete,
|
|
ARPTDComplete,
|
|
ARPResetComplete,
|
|
ARPRequestComplete,
|
|
ARPRcv,
|
|
ARPRcvComplete,
|
|
ARPStatus,
|
|
ARPStatusComplete,
|
|
{ sizeof(TCP_NAME),
|
|
sizeof(TCP_NAME),
|
|
0
|
|
#ifdef _PNP_POWER
|
|
},
|
|
NULL,
|
|
ARPBindAdapter,
|
|
ARPUnbindAdapter,
|
|
NULL
|
|
#else
|
|
}
|
|
#endif
|
|
};
|
|
#endif
|
|
|
|
//* 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;
|
|
}
|
|
|
|
CTEMemCopy(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
|
|
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;
|
|
|
|
|
|
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 != 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;
|
|
(void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset);
|
|
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);
|
|
|
|
(void)CopyToNdis(Buffer, (uchar *)&AI->ai_parpcount,
|
|
sizeof(uint), &Offset);
|
|
|
|
CTEFreeLock(&AI->ai_lock, Handle);
|
|
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);
|
|
Buffer = CopyToNdis(Buffer, (uchar *)TempPArp,
|
|
sizeof(ProxyArpEntry), &Offset);
|
|
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;
|
|
(void)CopyToNdis(Buffer, (uchar *)AXI, sizeof(AddrXlatInfo),
|
|
&Offset);
|
|
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);
|
|
Buffer = CopyToNdis(Buffer, InfoBuff,
|
|
sizeof(IPNetToMediaEntry), &Offset);
|
|
} 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;
|
|
|
|
|
|
// 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;
|
|
break;
|
|
case NdisMedium802_5:
|
|
IFE->if_type = IF_TYPE_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;
|
|
IFE->if_speed = AI->ai_speed;
|
|
IFE->if_physaddrlen = AI->ai_addrlen;
|
|
CTEMemCopy(IFE->if_physaddr,AI->ai_addr, AI->ai_addrlen);
|
|
IFE->if_adminstatus = (uint)AI->ai_adminstate;
|
|
IFE->if_operstatus = (uint)AI->ai_operstate;
|
|
IFE->if_lastchange = AI->ai_lastchange;
|
|
IFE->if_inoctets = AI->ai_inoctets;
|
|
IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX];
|
|
IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX];
|
|
IFE->if_indiscards = AI->ai_indiscards;
|
|
IFE->if_inerrors = AI->ai_inerrors;
|
|
IFE->if_inunknownprotos = AI->ai_uknprotos;
|
|
IFE->if_outoctets = AI->ai_outoctets;
|
|
IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX];
|
|
IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX];
|
|
IFE->if_outdiscards = AI->ai_outdiscards;
|
|
IFE->if_outerrors = AI->ai_outerrors;
|
|
IFE->if_outqlen = AI->ai_qlen;
|
|
IFE->if_descrlen = AI->ai_desclen;
|
|
Buffer = CopyToNdis(Buffer, (uchar *)IFE, IFE_FIXED_SIZE, &Offset);
|
|
|
|
// 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) {
|
|
(void)CopyToNdis(Buffer, AI->ai_desc, AI->ai_desclen, &Offset);
|
|
}
|
|
*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;
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
ARPSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context;
|
|
CTELockHandle Handle, EntryHandle;
|
|
int Status;
|
|
IFEntry *IFE = (IFEntry *)Buffer;
|
|
IPNetToMediaEntry *IPNME;
|
|
ARPTableEntry *PrevATE, *CurrentATE;
|
|
ARPTable *Table;
|
|
ENetHeader *Header;
|
|
uint Entity, Instance;
|
|
|
|
Entity = ID->toi_entity.tei_entity;
|
|
Instance = ID->toi_entity.tei_instance;
|
|
|
|
// First, make sure it's possibly an ID we can handle.
|
|
if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) &&
|
|
(Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) {
|
|
return TDI_INVALID_REQUEST;
|
|
}
|
|
|
|
if (ID->toi_type != INFO_TYPE_PROVIDER) {
|
|
return TDI_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Might be able to handle this.
|
|
if (Entity == IF_ENTITY) {
|
|
|
|
// It's for the I/F level, see if it's for the statistics.
|
|
if (ID->toi_class != INFO_CLASS_PROTOCOL)
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
if (ID->toi_id == IF_MIB_STATS_ID) {
|
|
// It's for the stats. Make sure it's a valid size.
|
|
if (Size >= IFE_FIXED_SIZE) {
|
|
// It's a valid size. See what he wants to do.
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
switch (IFE->if_adminstatus) {
|
|
case IF_STATUS_UP:
|
|
// He's marking it up. If the operational state is
|
|
// alse up, mark the whole interface as up.
|
|
Interface->ai_adminstate = IF_STATUS_UP;
|
|
if (Interface->ai_operstate == IF_STATUS_UP)
|
|
Interface->ai_state = INTERFACE_UP;
|
|
Status = TDI_SUCCESS;
|
|
break;
|
|
case IF_STATUS_DOWN:
|
|
// He's taking it down. Mark both the admin state and
|
|
// the interface state down.
|
|
Interface->ai_adminstate = IF_STATUS_DOWN;
|
|
Interface->ai_state = INTERFACE_DOWN;
|
|
Status = TDI_SUCCESS;
|
|
break;
|
|
case IF_STATUS_TESTING:
|
|
// He's trying to cause up to do testing, which we
|
|
// don't support. Just return success.
|
|
Status = TDI_SUCCESS;
|
|
break;
|
|
default:
|
|
Status = TDI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
return Status;
|
|
} else
|
|
return TDI_INVALID_PARAMETER;
|
|
} else {
|
|
return TDI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
// Not for the interface level. See if it's an implementation or protocol
|
|
// class.
|
|
if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
|
|
ProxyArpEntry *PArpEntry;
|
|
ARPIPAddr *Addr;
|
|
IPAddr AddAddr;
|
|
IPMask Mask;
|
|
|
|
// It's for the implementation. It should be the proxy-ARP ID.
|
|
if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry))
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
PArpEntry = (ProxyArpEntry *)Buffer;
|
|
AddAddr = PArpEntry->pae_addr;
|
|
Mask = PArpEntry->pae_mask;
|
|
|
|
// See if he's trying to add or delete a proxy arp entry.
|
|
if (PArpEntry->pae_status == PAE_STATUS_VALID) {
|
|
// We're trying to add an entry. We won't allow an entry
|
|
// to be added that we believe to be invalid or conflicting
|
|
// with our local addresses.
|
|
|
|
if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) ||
|
|
IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) ||
|
|
IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) ||
|
|
CLASSD_ADDR(AddAddr))
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
// Walk through the list of addresses on the interface, and see
|
|
// if they would match the AddAddr. If so, fail the request.
|
|
CTEGetLock(&Interface->ai_lock, &Handle);
|
|
|
|
if (IsBCastOnIF(Interface, AddAddr & Mask)) {
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
return TDI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Addr = &Interface->ai_ipaddr;
|
|
do {
|
|
if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
|
|
if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr))
|
|
break;
|
|
}
|
|
Addr = Addr->aia_next;
|
|
} while (Addr != NULL);
|
|
|
|
CTEFreeLock(&Interface->ai_lock, Handle);
|
|
if (Addr != NULL)
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
// At this point, we believe we're ok. Try to add the address.
|
|
if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL))
|
|
return TDI_SUCCESS;
|
|
else
|
|
return TDI_NO_RESOURCES;
|
|
} else {
|
|
if (PArpEntry->pae_status == PAE_STATUS_INVALID) {
|
|
// He's trying to delete a proxy ARP address.
|
|
if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask))
|
|
return TDI_SUCCESS;
|
|
}
|
|
return TDI_INVALID_PARAMETER;
|
|
}
|
|
|
|
}
|
|
|
|
if (ID->toi_class != INFO_CLASS_PROTOCOL)
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
|
|
if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID &&
|
|
Size >= sizeof(IPNetToMediaEntry)) {
|
|
// He does want to set an ARP table entry. See if he's trying to
|
|
// create or delete one.
|
|
|
|
IPNME = (IPNetToMediaEntry *)Buffer;
|
|
if (IPNME->inme_type == INME_TYPE_INVALID) {
|
|
uint Index = ARP_HASH(IPNME->inme_addr);
|
|
|
|
// We're trying to delete an entry. See if we can find it,
|
|
// and then delete it.
|
|
CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
|
|
Table = Interface->ai_ARPTbl;
|
|
PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next);
|
|
CurrentATE = (*Table)[Index];
|
|
while (CurrentATE != (ARPTableEntry *)NULL) {
|
|
if (CurrentATE->ate_dest == IPNME->inme_addr) {
|
|
// Found him. Break out of the loop.
|
|
break;
|
|
} else {
|
|
PrevATE = CurrentATE;
|
|
CurrentATE = CurrentATE->ate_next;
|
|
}
|
|
}
|
|
|
|
if (CurrentATE != NULL) {
|
|
CTEGetLock(&CurrentATE->ate_lock, &EntryHandle);
|
|
RemoveARPTableEntry(PrevATE, CurrentATE);
|
|
Interface->ai_count--;
|
|
CTEFreeLock(&CurrentATE->ate_lock, EntryHandle);
|
|
|
|
if (CurrentATE->ate_packet != NULL)
|
|
IPSendComplete(Interface->ai_context, CurrentATE->ate_packet,
|
|
NDIS_STATUS_SUCCESS);
|
|
|
|
CTEFreeMem(CurrentATE);
|
|
Status = 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);
|
|
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;
|
|
CTEMemCopy(&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;
|
|
CTEMemCopy(&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:
|
|
DEBUGCHK;
|
|
break;
|
|
}
|
|
|
|
|
|
// Copy in the source and destination addresses.
|
|
|
|
if (Interface->ai_media != NdisMediumArcnet878_2) {
|
|
CTEMemCopy(Header->eh_daddr, IPNME->inme_physaddr,
|
|
ARP_802_ADDR_LENGTH);
|
|
CTEMemCopy(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;
|
|
|
|
CTEFreeLock(&CurrentATE->ate_lock, Handle);
|
|
return TDI_SUCCESS;
|
|
|
|
}
|
|
|
|
return TDI_INVALID_PARAMETER;
|
|
|
|
|
|
}
|
|
|
|
|
|
static uint ARPPackets = ARP_DEFAULT_PACKETS;
|
|
static uint ARPBuffers = ARP_DEFAULT_BUFFERS;
|
|
|
|
#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.
|
|
|
|
// BUGBUG - Get configuration information dynamically.
|
|
|
|
#ifdef NT
|
|
RtlInitUnicodeString(&(ARPCharacteristics.Name), ARPName);
|
|
#else // NT
|
|
ARPCharacteristics.Name.Buffer = ARPName;
|
|
#endif // NT
|
|
|
|
NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *)
|
|
&ARPCharacteristics, sizeof(ARPCharacteristics));
|
|
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
return(1);
|
|
}
|
|
else {
|
|
return(0);
|
|
}
|
|
}
|
|
#pragma END_INIT
|
|
|
|
#ifndef CHICAGO
|
|
#pragma BEGIN_INIT
|
|
#else
|
|
#pragma code_seg("_LTEXT", "LCODE")
|
|
#endif
|
|
|
|
//* 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;
|
|
ARPBufferTracker *Tracker;
|
|
ARPTable *Table; // ARP table.
|
|
uint i; // Index variable.
|
|
ARPTableEntry *ATE;
|
|
CTELockHandle LockHandle;
|
|
NDIS_HANDLE Handle;
|
|
|
|
CTEStopTimer(&Interface->ai_timer);
|
|
|
|
// 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];
|
|
RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]),
|
|
ate_next),ATE);
|
|
CTEFreeMem(ATE);
|
|
}
|
|
}
|
|
CTEFreeMem(Table);
|
|
}
|
|
|
|
Interface->ai_ARPTbl = NULL;
|
|
|
|
if (Interface->ai_ppool != (NDIS_HANDLE)NULL)
|
|
NdisFreePacketPool(Interface->ai_ppool);
|
|
|
|
if (Interface->ai_bpool != (NDIS_HANDLE)NULL)
|
|
NdisFreeBufferPool(Interface->ai_bpool);
|
|
|
|
Tracker = Interface->ai_buflist;
|
|
while (Tracker != NULL) {
|
|
Interface->ai_buflist = Tracker->abt_next;
|
|
NdisFreeBufferPool(Tracker->abt_handle);
|
|
CTEFreeMem(Tracker->abt_buffer);
|
|
CTEFreeMem(Tracker);
|
|
Tracker = Interface->ai_buflist;
|
|
}
|
|
|
|
if (Interface->ai_bbbase != (uchar *)NULL)
|
|
CTEFreeMem(Interface->ai_bbbase);
|
|
|
|
// 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
|
|
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
|
|
ARPGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Context;
|
|
uint ECount;
|
|
uint MyATBase;
|
|
uint MyIFBase;
|
|
uint i;
|
|
|
|
ECount = *Count;
|
|
|
|
// Walk down the list, looking for existing AT or IF entities, and
|
|
// adjust our base instance accordingly.
|
|
|
|
MyATBase = 0;
|
|
MyIFBase = 0;
|
|
for (i = 0; i < ECount; i++, EntityList++) {
|
|
if (EntityList->tei_entity == AT_ENTITY)
|
|
MyATBase = MAX(MyATBase, EntityList->tei_instance + 1);
|
|
else
|
|
if (EntityList->tei_entity == IF_ENTITY)
|
|
MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1);
|
|
}
|
|
|
|
// EntityList points to the start of where we want to begin filling in.
|
|
// Make sure we have enough room. We need one for the ICMP instance,
|
|
// and one for the CL_NL instance.
|
|
|
|
if ((ECount + 2) > MAX_TDI_ENTITIES)
|
|
return FALSE;
|
|
|
|
// At this point we've figure out our base instance. Save for later use.
|
|
Interface->ai_atinst = MyATBase;
|
|
Interface->ai_ifinst = MyIFBase;
|
|
|
|
// Now fill it in.
|
|
EntityList->tei_entity = AT_ENTITY;
|
|
EntityList->tei_instance = MyATBase;
|
|
EntityList++;
|
|
EntityList->tei_entity = IF_ENTITY;
|
|
EntityList->tei_instance = MyIFBase;
|
|
*Count += 2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
extern uint UseEtherSNAP(PNDIS_STRING Name);
|
|
extern void GetAlwaysSourceRoute(uint *pArpAlwaysSourceRoute, uint *pIPAlwaysSourceRoute);
|
|
extern uint GetArpCacheLife(void);
|
|
|
|
|
|
//** 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.
|
|
//
|
|
#ifndef _PNP_POWER
|
|
int
|
|
ARPRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn,
|
|
IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn,
|
|
IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound)
|
|
#else
|
|
int
|
|
ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface)
|
|
#endif
|
|
{
|
|
ARPInterface *ai; // Pointer to interface struct. for this
|
|
// interface.
|
|
NDIS_STATUS Status, OpenStatus; // Status values.
|
|
uint i = 0; // Medium index.
|
|
NDIS_MEDIUM MediaArray[MAX_MEDIA];
|
|
uchar sbsize;
|
|
uchar *buffer; // Pointer to our buffers.
|
|
uint mss;
|
|
uint speed;
|
|
uint Needed;
|
|
uint MacOpts;
|
|
uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize;
|
|
uint OID;
|
|
uint PF;
|
|
PNDIS_BUFFER Buffer;
|
|
|
|
if ((ai = CTEAllocMem(sizeof(ARPInterface))) == (ARPInterface *)NULL)
|
|
return FALSE; // Couldn't allocate memory for this one.
|
|
|
|
#ifdef _PNP_POWER
|
|
*Interface = ai;
|
|
#endif
|
|
|
|
CTEMemSet(ai, 0, sizeof(ARPInterface));
|
|
CTEInitTimer(&ai->ai_timer);
|
|
|
|
#ifdef NT
|
|
ExInitializeSListHead(&ai->ai_sblist);
|
|
#endif
|
|
|
|
|
|
MediaArray[MEDIA_DIX] = NdisMedium802_3;
|
|
MediaArray[MEDIA_TR] = NdisMedium802_5;
|
|
MediaArray[MEDIA_FDDI] = NdisMediumFddi;
|
|
MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2;
|
|
|
|
// Initialize this adapter interface structure.
|
|
ai->ai_state = INTERFACE_INIT;
|
|
ai->ai_adminstate = IF_STATUS_DOWN;
|
|
ai->ai_operstate = IF_STATUS_DOWN;
|
|
ai->ai_bcast = IP_LOCAL_BCST;
|
|
ai->ai_maxhdrs = ARP_DEFAULT_MAXHDRS;
|
|
|
|
#ifndef _PNP_POWER
|
|
ai->ai_index = NumIFBound + 1;
|
|
ai->ai_context = IPContext;
|
|
Info->lip_context = ai;
|
|
Info->lip_transmit = ARPTransmit;
|
|
Info->lip_transfer = ARPXferData;
|
|
Info->lip_close = ARPClose;
|
|
Info->lip_addaddr = ARPAddAddr;
|
|
Info->lip_deladdr = ARPDeleteAddr;
|
|
Info->lip_invalidate = ARPInvalidate;
|
|
Info->lip_open = ARPOpen;
|
|
Info->lip_qinfo = ARPQueryInfo;
|
|
Info->lip_setinfo = ARPSetInfo;
|
|
Info->lip_getelist = ARPGetEList;
|
|
|
|
Info->lip_index = ai->ai_index;
|
|
#endif
|
|
|
|
// 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;
|
|
|
|
// Allocate the buffer and packet pools.
|
|
NdisAllocatePacketPool(&Status, &ai->ai_ppool, ARPPackets, sizeof(struct PCCommon));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
FreeARPInterface(ai);
|
|
return FALSE;
|
|
}
|
|
|
|
NdisAllocateBufferPool(&Status, &ai->ai_bpool, ARPBuffers);
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
FreeARPInterface(ai);
|
|
return FALSE;
|
|
}
|
|
|
|
// Allocate the ARP table
|
|
if ((ai->ai_ARPTbl = (ARPTable *)CTEAllocMem(ARP_TABLE_SIZE * sizeof(ARPTableEntry *))) ==
|
|
(ARPTable *)NULL) {
|
|
FreeARPInterface(ai);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// NULL out the pointers
|
|
//
|
|
CTEMemSet(ai->ai_ARPTbl, 0, ARP_TABLE_SIZE * sizeof(ARPTableEntry *));
|
|
|
|
CTEInitBlockStruc(&ai->ai_block);
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
sbsize = ARP_MAX_MEDIA_ENET;
|
|
hdrsize = sizeof(ENetHeader);
|
|
if (!UseEtherSNAP(Adapter)) {
|
|
snapsize = 0;
|
|
} else {
|
|
snapsize = sizeof(SNAPHeader);
|
|
sbsize += sizeof(SNAPHeader);
|
|
}
|
|
|
|
PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED |
|
|
NDIS_PACKET_TYPE_MULTICAST;
|
|
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;
|
|
sbsize = ARP_MAX_MEDIA_TR;
|
|
hdrsize = sizeof(TRHeader);
|
|
snapsize = sizeof(SNAPHeader);
|
|
PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
|
|
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;
|
|
sbsize = ARP_MAX_MEDIA_FDDI;
|
|
hdrsize = sizeof(FDDIHeader);
|
|
snapsize = sizeof(SNAPHeader);
|
|
PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED |
|
|
NDIS_PACKET_TYPE_MULTICAST;
|
|
break;
|
|
case NdisMediumArcnet878_2:
|
|
addrlen = 1;
|
|
bcastmask = ARC_BCAST_MASK;
|
|
bcastval = ARC_BCAST_VAL;
|
|
bcastoff = ARC_BCAST_OFF;
|
|
OID = OID_ARCNET_CURRENT_ADDRESS;
|
|
sbsize = ARP_MAX_MEDIA_ARC;
|
|
hdrsize = sizeof(ARCNetHeader);
|
|
snapsize = 0;
|
|
PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
|
|
break;
|
|
default:
|
|
DEBUGCHK;
|
|
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);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
FreeARPInterface(ai);
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef _PNP_POWER
|
|
Info->lip_addrlen = addrlen;
|
|
Info->lip_addr = ai->ai_addr;
|
|
#endif
|
|
|
|
// Read the maximum frame size.
|
|
if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
|
|
OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL)) != 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;
|
|
|
|
#ifndef _PNP_POWER
|
|
Info->lip_mss = mss;
|
|
#endif
|
|
|
|
// Read the speed for local purposes.
|
|
if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
|
|
OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL)) == NDIS_STATUS_SUCCESS) {
|
|
ai->ai_speed = speed * 100L;
|
|
#ifndef _PNP_POWER
|
|
Info->lip_speed = ai->ai_speed;
|
|
#endif
|
|
}
|
|
|
|
// Read and save the options.
|
|
Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS,
|
|
&MacOpts, sizeof(MacOpts), NULL);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
#ifndef _PNP_POWER
|
|
Info->lip_flags = 0;
|
|
#else
|
|
*Flags = 0;
|
|
#endif
|
|
else
|
|
#ifndef _PNP_POWER
|
|
Info->lip_flags =
|
|
#else
|
|
*Flags =
|
|
#endif
|
|
(MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0;
|
|
|
|
// Read and store the vendor description string.
|
|
Status = DoNDISRequest(ai, NdisRequestQueryInformation,
|
|
OID_GEN_VENDOR_DESCRIPTION, &ai->ai_desc, 0, &Needed);
|
|
|
|
if ((Status == NDIS_STATUS_INVALID_LENGTH) ||
|
|
(Status == NDIS_STATUS_BUFFER_TOO_SHORT)) {
|
|
// We know the size we need. Allocate a buffer.
|
|
buffer = CTEAllocMem(Needed);
|
|
if (buffer != NULL) {
|
|
Status = DoNDISRequest(ai, NdisRequestQueryInformation,
|
|
OID_GEN_VENDOR_DESCRIPTION, buffer, Needed, NULL);
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
ai->ai_desc = buffer;
|
|
ai->ai_desclen = Needed;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allocate our small and big buffer pools.
|
|
|
|
if ((sbsize & 0x3)) {
|
|
//
|
|
// Must 32 bit align the buffers so pointers to them will be aligned.
|
|
//
|
|
sbsize = ((sbsize >> 2) + 1) << 2;
|
|
}
|
|
|
|
ai->ai_sbsize = sbsize;
|
|
|
|
// Pre-prime the ARP header buffer list.
|
|
Buffer = GrowARPHeaders(ai);
|
|
if (Buffer != NULL) {
|
|
FreeARPBuffer(ai, Buffer);
|
|
}
|
|
|
|
|
|
if ((buffer = CTEAllocMem((sbsize+sizeof(ARPHeader)) * ARPPackets)) == (uchar *)NULL) {
|
|
FreeARPInterface(ai);
|
|
return FALSE;
|
|
}
|
|
|
|
// Link big buffers into the list.
|
|
ai->ai_bbbase = buffer;
|
|
ai->ai_bblist = (uchar *)NULL;
|
|
for (i = 0; i < ARPPackets; i++) {
|
|
*(char **)&*buffer = ai->ai_bblist;
|
|
ai->ai_bblist = buffer;
|
|
buffer += sbsize+sizeof(ARPHeader);
|
|
}
|
|
|
|
// Everything's set up, so get the ARP timer running.
|
|
CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#ifndef CHICAGO
|
|
#pragma END_INIT
|
|
#endif
|
|
|
|
#ifdef _PNP_POWER
|
|
|
|
//* 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
|
|
ARPDynRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn,
|
|
IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn,
|
|
IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound)
|
|
{
|
|
ARPInterface *Interface = (ARPInterface *)Info->lip_context;
|
|
|
|
Interface->ai_context = IPContext;
|
|
Interface->ai_index = NumIFBound;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* ARPBindAdapter - Bind and initialize an adapter.
|
|
//
|
|
// Called in a PNP environment to initialize and bind an adapter. We open
|
|
// the adapter and get it running, and then we call up to IP to tell him
|
|
// about it. IP will initialize, and if all goes well call us back to start
|
|
// receiving.
|
|
//
|
|
// Input: RetStatus - Where to return the status of this call.
|
|
// BindContext - Handle to use for calling BindAdapterComplete.
|
|
// AdapterName - Pointer to name of adapter.
|
|
// SS1 - System specific 1 parameter.
|
|
// SS2 - System specific 2 parameter.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext,
|
|
PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2)
|
|
{
|
|
uint Flags; // MAC binding flags.
|
|
ARPInterface *Interface; // Newly created interface.
|
|
PNDIS_STRING ConfigName; // Name used by IP for config. info.
|
|
IP_STATUS Status; // State of IPAddInterface call.
|
|
LLIPBindInfo BindInfo; // Binding informatio for IP.
|
|
NDIS_HANDLE Handle ;
|
|
|
|
CTERefillMem();
|
|
|
|
if (!OpenIFConfig(SS1, &Handle)) {
|
|
*RetStatus = NDIS_STATUS_FAILURE;
|
|
return;
|
|
}
|
|
|
|
// If IsLLInterfaceValueNull is FALSE then this means that some other ARP module is
|
|
// used for this device so we skip it.
|
|
//
|
|
if (IsLLInterfaceValueNull(Handle) == FALSE) {
|
|
*RetStatus = NDIS_STATUS_FAILURE;
|
|
CloseIFConfig(Handle);
|
|
return ;
|
|
}
|
|
|
|
CloseIFConfig(Handle);
|
|
|
|
|
|
// First, open the adapter and get the info.
|
|
if (!ARPRegister(AdapterName, &Flags, &Interface)) {
|
|
*RetStatus = NDIS_STATUS_FAILURE;
|
|
return;
|
|
}
|
|
|
|
CTERefillMem();
|
|
|
|
// 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_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;
|
|
|
|
Status = IPAddInterface((PNDIS_STRING)SS1, SS2, Interface, ARPDynRegister,
|
|
&BindInfo);
|
|
|
|
if (Status != IP_SUCCESS) {
|
|
// Need to close the binding. FreeARPInterface will do that, as well
|
|
// as freeing resources.
|
|
|
|
FreeARPInterface(Interface);
|
|
*RetStatus = NDIS_STATUS_FAILURE;
|
|
} else
|
|
*RetStatus = NDIS_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//* 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;
|
|
NDIS_HANDLE Handle;
|
|
|
|
// Shut him up, so we don't get any more frames.
|
|
Interface->ai_pfilter = 0;
|
|
DoNDISRequest(Interface, NdisRequestSetInformation,
|
|
OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint),
|
|
NULL);
|
|
|
|
// Mark him as down.
|
|
Interface->ai_state = INTERFACE_DOWN;
|
|
Interface->ai_adminstate = IF_STATUS_DOWN;
|
|
|
|
// 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);
|
|
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) {
|
|
Handle = Interface->ai_handle;
|
|
Interface->ai_handle = NULL;
|
|
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);
|
|
} else {
|
|
CTEFreeLock(&Interface->ai_lock, LockHandle);
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
*RetStatus = Status;
|
|
|
|
if (Status == NDIS_STATUS_SUCCESS) {
|
|
|
|
FreeARPInterface(Interface);
|
|
}
|
|
}
|
|
|
|
extern ulong VIPTerminate;
|
|
|
|
//* ARPUnloadProtocol - Unload.
|
|
//
|
|
// Called when we need to unload. All we do is call up to IP, and return.
|
|
//
|
|
// Input: Nothing.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
void NDIS_API
|
|
ARPUnloadProtocol(void)
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
#ifdef CHICAGO
|
|
|
|
IPULUnloadNotify();
|
|
|
|
if (VIPTerminate) {
|
|
NdisDeregisterProtocol(&Status, ARPHandle);
|
|
CTEUnload(NULL);
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|