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