/********************************************************************/
/** 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