diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/tcpip/ip/arp.c | 4839 |
1 files changed, 4839 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/arp.c b/private/ntos/tdi/tcpip/ip/arp.c new file mode 100644 index 000000000..3c8253566 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arp.c @@ -0,0 +1,4839 @@ +/********************************************************************/ +/** 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 + |