/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: ARP.C - LAN arp module. Abstract: This file implements arp framing for IP layer on the upper edge and interfaces with ndis driver on the lower edge. Author: [Environment:] kernel mode only [Notes:] optional-notes Revision History: --*/ #include "precomp.h" //*** arp.c - ARP routines. // // This file containes all of the ARP related routines, including // table lookup, registration, etc. // // ARP is architected to support multiple protocols, but for now // it in only implemented to take one protocol (IP). This is done // for simplicity and ease of implementation. In the future we may // split ARP out into a seperate driver. #include "arp.h" #include "arpdef.h" #include "iproute.h" #include "iprtdef.h" #include "arpinfo.h" #include "tcpipbuf.h" #include "mdlpool.h" #include "ipifcons.h" #define NDIS_MAJOR_VERSION 0x4 #define NDIS_MINOR_VERSION 0 #ifndef NDIS_API #define NDIS_API #endif #define PPP_HW_ADDR "DEST" #define PPP_HW_ADDR_LEN 4 #if DBG uint fakereset = 0; #endif extern void IPReset(void *Context); UINT cUniAdapters = 0; extern uint EnableBcastArpReply; static ulong ARPLookahead = LOOKAHEAD_SIZE; static const uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06"; static const uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70"; static const uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00"; static const uchar ARCBcst[] = "\x00\x00\xd5"; ulong TRFunctionalMcast = 0; //canonical or non-canonical? static uchar TRMcst[] = "\x10\x40\xc0\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x82\x70"; //#define TR_MCAST_FUNCTIONAL_ADDRESS 0xc00000040000 //canonical form #define TR_MCAST_FUNCTIONAL_ADDRESS 0x030000200000 static uchar TRNetMcst[] = "\x00\x04\x00\x00"; static const uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00"; static const uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00"; static const uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06"; static const uchar ENetPtrnMsk[] = "\x00\x30"; static const uchar ENetSNAPPtrnMsk[] = "\x00\xC0\x3f"; //static const uchar TRPtrnMsk[] = "\x03\x00"; //static const uchar TRSNAPPtrnMsk[] = "\x03\xC0\x3f"; static const uchar TRPtrnMsk[] = "\x00\x00"; //NO AC/FC bits need to be checked static const uchar TRSNAPPtrnMsk[] = "\x00\xC0\x3f"; static const uchar FDDIPtrnMsk[] = "\x01\x00"; static const uchar FDDISNAPPtrnMsk[] = "\x01\x70\x1f"; static const uchar ARCPtrnMsk[] = "\x01"; static const uchar ARPPtrnMsk[] = "\x80\x00\x00\x0F"; static const uchar ARCARPPtrnMsk[] = "\x80\xC0\x03"; NDIS_STATUS __stdcall DoWakeupPattern(void *Context, PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc, ushort protoid, BOOLEAN AddPattern); NDIS_STATUS ARPWakeupPattern(ARPInterface *Interface, IPAddr Address, BOOLEAN AddPattern); NDIS_STATUS AddrNotifyLink(ARPInterface *Interface); static WCHAR ARPName[] = TCP_NAME; NDIS_HANDLE ARPHandle; // Our NDIS protocol handle. uint ArpCacheLife; extern uint ArpMinValidCacheLife; uint sArpAlwaysSourceRoute; // True if we always send ARP requests uint ArpRetryCount; // retries for arp request with source // route info on token ring. uint sIPAlwaysSourceRoute; extern uchar TrRii; extern PDRIVER_OBJECT IPDriverObject; extern DisableTaskOffload; extern NDIS_STATUS __stdcall IPPnPEvent(void *, PNET_PNP_EVENT PnPEvent); extern NDIS_STATUS GetIPConfigValue(NDIS_HANDLE Handle, PUNICODE_STRING IPConfig); extern VOID IPUnload(IN PDRIVER_OBJECT DriverObject); extern BOOLEAN CopyToNdisSafe( PNDIS_BUFFER DestBuf, PNDIS_BUFFER *ppNextBuf, uchar *SrcBuf, uint Size, uint *StartOffset); extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS); extern void IPULUnloadNotify(void); extern void NotifyOfUnload(void); extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE * Handle); extern int IsLLInterfaceValueNull(NDIS_HANDLE Handle); extern void CloseIFConfig(NDIS_HANDLE Handle); BOOLEAN QueryAndSetOffload(ARPInterface *ai); ARPTableEntry *CreateARPTableEntry(ARPInterface *Interface, IPAddr Destination, CTELockHandle *Handle, void *UserArp); NDIS_STATUS NDIS_API ARPRcvIndicationNew(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize, void *Data, uint Size, uint TotalSize, PNDIS_BUFFER pNdisBuffer, PINT pClientCnt); void CompleteIPSetNTEAddrRequestDelayed(CTEEvent *WorkerThreadEvent, PVOID Context); // Tables for bitswapping. const uchar SwapTableLo[] = { 0, // 0 0x08, // 1 0x04, // 2 0x0c, // 3 0x02, // 4 0x0a, // 5, 0x06, // 6, 0x0e, // 7, 0x01, // 8, 0x09, // 9, 0x05, // 10, 0x0d, // 11, 0x03, // 12, 0x0b, // 13, 0x07, // 14, 0x0f // 15 }; const uchar SwapTableHi[] = { 0, // 0 0x80, // 1 0x40, // 2 0xc0, // 3 0x20, // 4 0xa0, // 5, 0x60, // 6, 0xe0, // 7, 0x10, // 8, 0x90, // 9, 0x50, // 10, 0xd0, // 11, 0x30, // 12, 0xb0, // 13, 0x70, // 14, 0xf0 // 15 }; // Table of source route maximum I-field lengths for token ring. const ushort IFieldSize[] = { 516, 1500, 2052, 4472, 8191 }; #define LF_BIT_SHIFT 4 #define MAX_LF_BITS 4 // // Disposable init or paged code. // void FreeARPInterface(ARPInterface * Interface); void ARPOpen(void *Context); void NotifyConflictProc(CTEEvent * Event, void *Context); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, ARPInit) #pragma alloc_text(PAGE, ARPOpen) #pragma alloc_text(PAGELK, ARPRegister) #pragma alloc_text(PAGE, NotifyConflictProc) #endif // ALLOC_PRAGMA LIST_ENTRY ArpInterfaceList; CACHE_LINE_KSPIN_LOCK ArpInterfaceListLock; HANDLE ArpEnetHeaderPool; HANDLE ArpAuxHeaderPool; #define BUFSIZE_ENET_HEADER_POOL sizeof(ENetHeader) + sizeof(ARPHeader) #define BUFSIZE_AUX_HEADER_POOL ARP_MAX_MEDIA_TR + (2 * sizeof(ARPHeader)) // // Support Structs for DoNDISRequest (BLOCKING & NON-BLOCKING) // typedef struct _RequestBlock { NDIS_REQUEST Request; // Request structure we'll use ULONG Blocking; // ? Is this Request Blocking ? CTEBlockStruc Block; // Structure for blocking on. No longer use // ai_block since multiple requests can // occur simultaneously. // ai_block is now only used for blocking on // opening and closing the NDIS adapter. ULONG RefCount; // Reference count (only used for blocking). // Reference counting is required for Windows ME since KeWaitForSingleObject // can fail (when the event is NOT set) and we need to protect the memory // until completion. } RequestBlock; // This prototype enables DoNDISRequest to compile without errors void NDIS_API ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST pRequest, NDIS_STATUS Status); //* FillARPControlBlock // // A utility routine to transfer a physical address into an ARPControlBlock, // taking into account different MAC address formats. // // Entry: // Interface - the ARPInterface which identifies the media // Entry - the ARP entry containing the MAC address // ArpContB - the control-block to be filled // __inline NDIS_STATUS FillARPControlBlock(ARPInterface* Interface, ARPTableEntry* Entry, ARPControlBlock* ArpContB) { ENetHeader *EHdr; TRHeader *TRHdr; FDDIHeader *FHdr; ARCNetHeader *AHdr; uint Size = 0; NDIS_STATUS Status; if (Interface->ai_media == NdisMediumArcnet878_2) { if (!ArpContB->PhyAddrLen) { return NDIS_STATUS_BUFFER_OVERFLOW; } Status = NDIS_STATUS_SUCCESS; } else if (ArpContB->PhyAddrLen < ARP_802_ADDR_LENGTH) { Size = ArpContB->PhyAddrLen; Status = NDIS_STATUS_BUFFER_OVERFLOW; } else { Size = ARP_802_ADDR_LENGTH; Status = NDIS_STATUS_SUCCESS; } switch (Interface->ai_media) { case NdisMedium802_3: EHdr = (ENetHeader *) Entry->ate_addr; RtlCopyMemory(ArpContB->PhyAddr, EHdr->eh_daddr, Size); ArpContB->PhyAddrLen = Size; break; case NdisMedium802_5: TRHdr = (TRHeader *) Entry->ate_addr; RtlCopyMemory(ArpContB->PhyAddr, TRHdr->tr_daddr, Size); ArpContB->PhyAddrLen = Size; break; case NdisMediumFddi: FHdr = (FDDIHeader *) Entry->ate_addr; RtlCopyMemory(ArpContB->PhyAddr, FHdr->fh_daddr, Size); ArpContB->PhyAddrLen = Size; break; case NdisMediumArcnet878_2: AHdr = (ARCNetHeader *) Entry->ate_addr; ArpContB->PhyAddr[0] = AHdr->ah_daddr; ArpContB->PhyAddrLen = 1; break; default: ASSERT(0); } return Status; } //* DoNDISRequest - Submit a (NON) BLOCKING request to an NDIS driver // // This is a utility routine to submit a general request to an NDIS // driver. The caller specifes the request code (OID), a buffer and // a length. This routine allocates a request structure, fills it in, & // submits the request. // // If the call is non-blocking, any memory allocated is deallocated // in ARPRequestComplete. Also as this callback is shared by both // DoNDISRequest blocking and non-blocking, we suffix the request // with a ULONG that tells ARPRequestComplete if this request is a // blocking request or not. If the request is non blocking, then the // ARPRequestComplete reclaims the memory allocated on the heap // // Important: // Allocate Info, which points to the Information Buffer passed to // NdisRequest, on the HEAP, if this request does not block. This // memory is automatically deallocated by ARPRequestComplete // // If the call is blocking, the request memory can be allocated on the // STACK. When we complete the request, the request on the stack // will automatically get unwound. // // Entry: // Adapter - A pointer to the ARPInterface adapter structure. // Request - Type of request to be done (Set or Query) // OID - Value to be set/queried. // Info - A pointer to the info buffer // Length - Length of data in the buffer // Needed - On return, filled in with bytes needed in buffer // Blocking - Whether NdisRequest is completed synchronously // // Exit: // Status - BLOCKING req - SUCCESS or some NDIS error code // NON-BLOCKING - SUCCESS, PENDING or some error // NDIS_STATUS DoNDISRequest(ARPInterface * Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID, VOID * Info, UINT Length, UINT * Needed, BOOLEAN Blocking) { RequestBlock *pReqBlock; NDIS_STATUS Status; DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST, (DTEXT("+DoNDISRequest(%x, %x, %x, %x, %d, %x, %x\n"), Adapter, RT, OID, Info, Length, Needed, Blocking)); if ((Adapter->ai_adminstate == INTERFACE_DOWN) || (Adapter->ai_handle == NULL)) { return NDIS_STATUS_ADAPTER_NOT_READY; } // Both blocking and non-blocking requests are allocated from NPP. The // blocking case is to protect against wait failure. pReqBlock = CTEAllocMemN(sizeof(RequestBlock), 'NiCT'); if (pReqBlock == NULL) { return NDIS_STATUS_RESOURCES; } if (Blocking) { // Initialize the structure to block on CTEInitBlockStruc(&pReqBlock->Block); // Reference count is initialize to two. One for the completion in // ARPRequestComplete and one for when the CTEBlock completes. // N.B. This ensures that we don't touch freed memory if // the CTEBlock fails on Windows ME. pReqBlock->RefCount = 2; DEBUGMSG(DBG_INFO && DBG_ARP && DBG_REQUEST, (DTEXT("DoNDISRequset block: pReqBlock %x OID %x\n"), pReqBlock, OID)); } else { DEBUGMSG(DBG_INFO && DBG_ARP && DBG_REQUEST, (DTEXT("DoNDISRequest async: pReqBlock %x OID %x\n"), pReqBlock, OID)); } // Now fill the request's info buffer (same for BLOCKING & NON-BLOCKING) pReqBlock->Block.cbs_status = NDIS_STATUS_SUCCESS; pReqBlock->Request.RequestType = RT; if (RT == NdisRequestSetInformation) { pReqBlock->Request.DATA.SET_INFORMATION.Oid = OID; pReqBlock->Request.DATA.SET_INFORMATION.InformationBuffer = Info; pReqBlock->Request.DATA.SET_INFORMATION.InformationBufferLength = Length; } else { pReqBlock->Request.DATA.QUERY_INFORMATION.Oid = OID; pReqBlock->Request.DATA.QUERY_INFORMATION.InformationBuffer = Info; pReqBlock->Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length; } pReqBlock->Blocking = Blocking; // Submit the request. if (Adapter->ai_handle != NULL) { #if MILLEN // On Millennium, the AOL adapter returns with registers trashed. // We will work around by saving and restoring registers. // _asm { push esi push edi push ebx } #endif // MILLEN NdisRequest(&Status, Adapter->ai_handle, &pReqBlock->Request); #if MILLEN _asm { pop ebx pop edi pop esi } #endif // MILLEN } else { Status = NDIS_STATUS_FAILURE; } if (Blocking) { if (Status == NDIS_STATUS_PENDING) { CTEBlockTracker Tracker; Status = (NDIS_STATUS) CTEBlockWithTracker(&pReqBlock->Block, &Tracker, Adapter); #if MILLEN // If Status == -1, it means the wait failed -- due to system reasons. // Put in a reasonable failure. if (Status == -1) { Status = NDIS_STATUS_FAILURE; } #endif // MILLEN } else { // Since we aren't blocking, remove refcount for ARPRequestComplete. InterlockedDecrement( (PLONG) &pReqBlock->RefCount); } if (Needed != NULL) *Needed = pReqBlock->Request.DATA.QUERY_INFORMATION.BytesNeeded; if (InterlockedDecrement( (PLONG) &pReqBlock->RefCount) == 0) { CTEFreeMem(pReqBlock); } } else { if (Status != NDIS_STATUS_PENDING) { if (Needed != NULL) *Needed = pReqBlock->Request.DATA.QUERY_INFORMATION.BytesNeeded; ARPRequestComplete(Adapter->ai_handle, &pReqBlock->Request, Status); } } DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST, (DTEXT("-DoNDISRequest [%x]\n"), Status)); return Status; } //* FreeARPBuffer - Free a header and buffer descriptor pair. // // Called when we're done with a buffer. We'll free the buffer and the // buffer descriptor pack to the interface. // // Entry: Interface - Interface buffer/bd came frome. // Buffer - NDIS_BUFFER to be freed. // // Returns: Nothing. // __inline VOID FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer) { UNREFERENCED_PARAMETER(Interface); MdpFree(Buffer); } //* GetARPBuffer - Get a buffer and descriptor // // Returns a pointer to an NDIS_BUFFER and a pointer to a buffer // of the specified size. // // Entry: Interface - Pointer to ARPInterface structure to allocate buffer from. // BufPtr - Pointer to where to return buf address. // Size - Size in bytes of buffer needed. // // Returns: Pointer to NDIS_BUFFER if successfull, NULL if not // PNDIS_BUFFER GetARPBufferAtDpcLevel(ARPInterface *Interface, uchar **BufPtr, uchar Size) { PNDIS_BUFFER Mdl = NULL; UNREFERENCED_PARAMETER(Interface); #if DBG *BufPtr = NULL; #endif UNREFERENCED_PARAMETER(Interface); if (Size <= BUFSIZE_ENET_HEADER_POOL) { Mdl = MdpAllocateAtDpcLevel(ArpEnetHeaderPool, BufPtr); } else if (Size <= BUFSIZE_AUX_HEADER_POOL) { Mdl = MdpAllocateAtDpcLevel(ArpAuxHeaderPool, BufPtr); } if (Mdl) { NdisAdjustBufferLength(Mdl, Size); } return Mdl; } #if MILLEN #define GetARPBuffer GetARPBufferAtDpcLevel #else __inline PNDIS_BUFFER GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar Size) { KIRQL OldIrql; PNDIS_BUFFER Mdl; OldIrql = KeRaiseIrqlToDpcLevel(); Mdl = GetARPBufferAtDpcLevel(Interface, BufPtr, Size); KeLowerIrql(OldIrql); return Mdl; } #endif //* BitSwap - Bit swap two strings. // // A routine to bitswap two strings. // // Input: Dest - Destination of swap. // Src - Src string to be swapped. // Length - Length in bytes to swap. // // Returns: Nothing. // void BitSwap(uchar * Dest, uchar * Src, uint Length) { uint i; uchar Temp, TempSrc; for (i = 0; i < Length; i++, Dest++, Src++) { TempSrc = *Src; Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f]; *Dest = Temp; } } //* SendARPPacket - Build a header, and send a packet. // // A utility routine to build and ARP header and send a packet. We assume // the media specific header has been built. // // Entry: Interface - Interface for NDIS drive. // Packet - Pointer to packet to be sent // Header - Pointer to header to fill in. // Opcode - Opcode for packet. // Address - Source HW address. // SrcAddr - Address to use as our source h/w address. // Destination - Destination IP address. // Src - Source IP address. // HWType - Hardware type. // CheckIF - TRUE iff we are to check the I/F status before // sending. // // Returns: NDIS_STATUS of send. // NDIS_STATUS SendARPPacket(ARPInterface * Interface, PNDIS_PACKET Packet, ARPHeader * Header, ushort Opcode, uchar * Address, uchar * SrcAddr, IPAddr Destination, IPAddr Src, ushort HWType, uint CheckIF) { NDIS_STATUS Status; PNDIS_BUFFER Buffer; uint PacketDone; uchar *AddrPtr; ulong Proc; Header->ah_hw = HWType; Header->ah_pro = net_short(ARP_ETYPE_IP); Header->ah_hlen = Interface->ai_addrlen; Header->ah_plen = sizeof(IPAddr); Header->ah_opcode = Opcode; AddrPtr = Header->ah_shaddr; if (SrcAddr == NULL) SrcAddr = Interface->ai_addr; RtlCopyMemory(AddrPtr, SrcAddr, Interface->ai_addrlen); AddrPtr += Interface->ai_addrlen; *(IPAddr UNALIGNED *) AddrPtr = Src; AddrPtr += sizeof(IPAddr); if (Address != (uchar *) NULL) RtlCopyMemory(AddrPtr, Address, Interface->ai_addrlen); else RtlZeroMemory(AddrPtr, Interface->ai_addrlen); AddrPtr += Interface->ai_addrlen; *(IPAddr UNALIGNED *) AddrPtr = Destination; PacketDone = FALSE; if (!CheckIF || (Interface->ai_operstatus == INTERFACE_UP)) { Proc = KeGetCurrentProcessorNumber(); Interface->ai_qlen[Proc].ai_qlen++; NdisSend(&Status, Interface->ai_handle, Packet); if (Status != NDIS_STATUS_PENDING) { PacketDone = TRUE; Interface->ai_qlen[Proc].ai_qlen--; if (Status == NDIS_STATUS_SUCCESS) Interface->ai_outoctets += Packet->Private.TotalLength; else { if (Status == NDIS_STATUS_RESOURCES) Interface->ai_outdiscards++; else Interface->ai_outerrors++; } } } else { PacketDone = TRUE; Status = NDIS_STATUS_ADAPTER_NOT_READY; } if (PacketDone) { NdisUnchainBufferAtFront(Packet, &Buffer); FreeARPBuffer(Interface, Buffer); NdisFreePacket(Packet); } return Status; } //* SendARPRequest - Send an ARP packet // // Called when we need to ARP an IP address, or respond to a request. We'll send out // the packet, and the receiving routines will process the response. // // Entry: Interface - Interface to send the request on. // Destination - The IP address to be ARPed. // Type - Either RESOLVING_GLOBAL or RESOLVING_LOCAL // SrcAddr - NULL if we're sending from ourselves, the value // to use otherwise. // CheckIF - Flag passed through to SendARPPacket(). // // Returns: Status of attempt to send ARP request. // NDIS_STATUS SendARPRequest(ARPInterface * Interface, IPAddr Destination, uchar Type, uchar * SrcAddr, uint CheckIF) { uchar *MHeader; // Pointer to media header. PNDIS_BUFFER Buffer; // NDIS buffer descriptor. uchar MHeaderSize; // Size of media header. const uchar *MAddr; // Pointer to media address structure. uint SAddrOffset; // Offset into media address of source address. uchar SRFlag = 0; // Source routing flag. uchar SNAPLength = 0; const uchar *SNAPAddr; // Address of SNAP header. PNDIS_PACKET Packet; // Packet for sending. NDIS_STATUS Status; ushort HWType; IPAddr Src; CTELockHandle Handle; ARPIPAddr *Addr; // First, get a source address we can use. CTEGetLock(&Interface->ai_lock, &Handle); Addr = &Interface->ai_ipaddr; Src = NULL_IP_ADDR; do { if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { // // This is a valid address. See if it is the same as the // target address - i.e. arp'ing for ourselves. If it is, // we want to use that as our source address. // if (IP_ADDR_EQUAL(Addr->aia_addr, Destination)) { Src = Addr->aia_addr; break; } // See if the target is on this subnet. if (IP_ADDR_EQUAL( Addr->aia_addr & Addr->aia_mask, Destination & Addr->aia_mask )) { // // See if we've already found a suitable candidate on the // same subnet. If we haven't, we'll use this one. // if (!IP_ADDR_EQUAL( Addr->aia_addr & Addr->aia_mask, Src & Addr->aia_mask )) { Src = Addr->aia_addr; } } else { // He's not on our subnet. If we haven't already found a valid // address save this one in case we don't find a match for the // subnet. if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { Src = Addr->aia_addr; } } } Addr = Addr->aia_next; } while (Addr != NULL); CTEFreeLock(&Interface->ai_lock, Handle); // If we didn't find a source address, give up. if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) return NDIS_STATUS_SUCCESS; NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool); if (Status != NDIS_STATUS_SUCCESS) { Interface->ai_outdiscards++; return Status; } ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK; (Interface->ai_outpcount[AI_NONUCAST_INDEX])++; // Figure out what type of media this is, and do the appropriate thing. switch (Interface->ai_media) { case NdisMedium802_3: MHeaderSize = ARP_MAX_MEDIA_ENET; MAddr = ENetBcst; if (Interface->ai_snapsize == 0) { SNAPAddr = (uchar *) NULL; HWType = net_short(ARP_HW_ENET); } else { SNAPLength = sizeof(SNAPHeader); SNAPAddr = ARPSNAP; HWType = net_short(ARP_HW_802); } SAddrOffset = offsetof(struct ENetHeader, eh_saddr); break; case NdisMedium802_5: // Token ring. We have logic for dealing with the second transmit // of an arp request. MAddr = TRBcst; SAddrOffset = offsetof(struct TRHeader, tr_saddr); SNAPLength = sizeof(SNAPHeader); SNAPAddr = ARPSNAP; MHeaderSize = sizeof(TRHeader); HWType = net_short(ARP_HW_802); if (Type == ARP_RESOLVING_GLOBAL) { MHeaderSize += sizeof(RC); SRFlag = TR_RII; } break; case NdisMediumFddi: MHeaderSize = sizeof(FDDIHeader); MAddr = FDDIBcst; SNAPAddr = ARPSNAP; SNAPLength = sizeof(SNAPHeader); SAddrOffset = offsetof(struct FDDIHeader, fh_saddr); HWType = net_short(ARP_HW_ENET); break; case NdisMediumArcnet878_2: MHeaderSize = ARP_MAX_MEDIA_ARC; MAddr = ARCBcst; SNAPAddr = (uchar *) NULL; SAddrOffset = offsetof(struct ARCNetHeader, ah_saddr); HWType = net_short(ARP_HW_ARCNET); break; default: ASSERT(0); Interface->ai_outerrors++; return NDIS_STATUS_UNSUPPORTED_MEDIA; } if ((Buffer = GetARPBuffer(Interface, &MHeader, (uchar) (sizeof(ARPHeader) + MHeaderSize + SNAPLength))) == (PNDIS_BUFFER) NULL) { NdisFreePacket(Packet); Interface->ai_outdiscards++; return NDIS_STATUS_RESOURCES; } if (Interface->ai_media == NdisMediumArcnet878_2) { NdisAdjustBufferLength(Buffer, NdisBufferLength(Buffer) - ARCNET_ARPHEADER_ADJUSTMENT); } // Copy broadcast address into packet. RtlCopyMemory(MHeader, MAddr, MHeaderSize); // Fill in source address. if (SrcAddr == NULL) { SrcAddr = Interface->ai_addr; } if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize != 0) { ENetHeader *Hdr = (ENetHeader *) MHeader; // Using SNAP on ethernet. Adjust the etype to a length. Hdr->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader)); } RtlCopyMemory(&MHeader[SAddrOffset], SrcAddr, Interface->ai_addrlen); if ((Interface->ai_media == NdisMedium802_5) && (Type == ARP_RESOLVING_GLOBAL)) { // Turn on source routing. MHeader[SAddrOffset] |= SRFlag; MHeader[SAddrOffset + Interface->ai_addrlen] |= TrRii; } // Copy in SNAP header, if any. RtlCopyMemory(&MHeader[MHeaderSize], SNAPAddr, SNAPLength); // Media header is filled in. Now do ARP packet itself. NdisChainBufferAtFront(Packet, Buffer); return SendARPPacket(Interface, Packet, (ARPHeader *) & MHeader[MHeaderSize + SNAPLength], net_short(ARP_REQUEST), (uchar *) NULL, SrcAddr, Destination, Src, HWType, CheckIF); } //* SendARPReply - Reply to an ARP request. // // Called by our receive packet handler when we need to reply. We build a packet // and buffer and call SendARPPacket to send it. // // Entry: Interface - Pointer to interface to reply on. // Destination - IPAddress to reply to. // Src - Source address to reply from. // HWAddress - Hardware address to reply to. // SourceRoute - Source Routing information, if any. // SourceRouteSize - Size in bytes of soure routing. // UseSNAP - Whether or not to use SNAP for this reply. // // Returns: Nothing. // void SendARPReply(ARPInterface * Interface, IPAddr Destination, IPAddr Src, uchar * HWAddress, RC UNALIGNED * SourceRoute, uint SourceRouteSize, uint UseSNAP) { PNDIS_PACKET Packet; // Buffer and packet to be used. PNDIS_BUFFER Buffer; uchar *Header; // Pointer to media header. NDIS_STATUS Status; uchar Size = 0; // Size of media header buffer. ushort HWType; ENetHeader *EH; FDDIHeader *FH; ARCNetHeader *AH; TRHeader *TRH; // Allocate a packet for this. NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool); if (Status != NDIS_STATUS_SUCCESS) { Interface->ai_outdiscards++; return; } ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK; (Interface->ai_outpcount[AI_UCAST_INDEX])++; Size = Interface->ai_hdrsize; if (UseSNAP) Size = Size + (uchar) Interface->ai_snapsize; if (Interface->ai_media == NdisMedium802_5) Size = Size + (uchar) SourceRouteSize; if ((Buffer = GetARPBuffer(Interface, &Header, (uchar) (Size + sizeof(ARPHeader)))) == (PNDIS_BUFFER) NULL) { Interface->ai_outdiscards++; NdisFreePacket(Packet); return; } // Decide how to build the header based on the media type. switch (Interface->ai_media) { case NdisMedium802_3: EH = (ENetHeader *) Header; RtlCopyMemory(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH); RtlCopyMemory(EH->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); if (!UseSNAP) { EH->eh_type = net_short(ARP_ETYPE_ARP); HWType = net_short(ARP_HW_ENET); } else { // Using SNAP on ethernet. EH->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader)); HWType = net_short(ARP_HW_802); RtlCopyMemory(Header + sizeof(ENetHeader), ARPSNAP, sizeof(SNAPHeader)); } break; case NdisMedium802_5: TRH = (TRHeader *) Header; TRH->tr_ac = ARP_AC; TRH->tr_fc = ARP_FC; RtlCopyMemory(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH); RtlCopyMemory(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); if (SourceRouteSize) { // If we have source route info, deal with // it. RtlCopyMemory(Header + sizeof(TRHeader), SourceRoute, SourceRouteSize); // Convert to directed response. ((RC *) & Header[sizeof(TRHeader)])->rc_blen &= RC_LENMASK; ((RC *) & Header[sizeof(TRHeader)])->rc_dlf ^= RC_DIR; TRH->tr_saddr[0] |= TR_RII; } RtlCopyMemory(Header + sizeof(TRHeader) + SourceRouteSize, ARPSNAP, sizeof(SNAPHeader)); HWType = net_short(ARP_HW_802); break; case NdisMediumFddi: FH = (FDDIHeader *) Header; FH->fh_pri = ARP_FDDI_PRI; RtlCopyMemory(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH); RtlCopyMemory(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); RtlCopyMemory(Header + sizeof(FDDIHeader), ARPSNAP, sizeof(SNAPHeader)); HWType = net_short(ARP_HW_ENET); break; case NdisMediumArcnet878_2: AH = (ARCNetHeader *) Header; AH->ah_saddr = Interface->ai_addr[0]; AH->ah_daddr = *HWAddress; AH->ah_prot = ARP_ARCPROT_ARP; NdisAdjustBufferLength(Buffer, NdisBufferLength(Buffer) - ARCNET_ARPHEADER_ADJUSTMENT); HWType = net_short(ARP_HW_ARCNET); break; default: ASSERT(0); Interface->ai_outerrors++; FreeARPBuffer(Interface, Buffer); NdisFreePacket(Packet); return; } NdisChainBufferAtFront(Packet, Buffer); SendARPPacket(Interface, Packet, (ARPHeader *) (Header + Size), net_short(ARP_RESPONSE), HWAddress, NULL, Destination, Src, HWType, TRUE); } //* ARPRemoveRCE - Remove an RCE from the ATE list. // // This funtion removes a specified RCE from a given ATE. It assumes the ate_lock // is held by the caller. // // Entry: ATE - ATE from which RCE is to be removed. // RCE - RCE to be removed. // // Returns: Nothing // void ARPRemoveRCE(ARPTableEntry * ATE, RouteCacheEntry * RCE) { ARPContext *CurrentAC; // Current ARP Context being checked. #if DBG uint Found = FALSE; #endif CurrentAC = (ARPContext *) (((char *)&ATE->ate_rce) - offsetof(struct ARPContext, ac_next)); while (CurrentAC->ac_next != (RouteCacheEntry *) NULL) if (CurrentAC->ac_next == RCE) { ARPContext *DummyAC = (ARPContext *) RCE->rce_context; CurrentAC->ac_next = DummyAC->ac_next; DummyAC->ac_ate = (ARPTableEntry *) NULL; DummyAC->ac_next = NULL; #if DBG Found = TRUE; #endif break; } else CurrentAC = (ARPContext *) CurrentAC->ac_next->rce_context; ASSERT(Found); } //* ARPLookup - Look up an entry in the ARP table. // // Called to look up an entry in an interface's ARP table. If we find it, we'll // lock the entry and return a pointer to it, otherwise we return NULL. We // assume that the caller has the ARP table locked when we are called. // // The ARP table entry is structured as a hash table of pointers to // ARPTableEntrys.After hashing on the IP address, a linear search is done to // lookup the entry. // // If we find the entry, we lock it for the caller. If we don't find // the entry, we leave the ARP table locked so that the caller may atomically // insert a new entry without worrying about a duplicate being inserted between // the time the table was checked and the time the caller went to insert the // entry. // // Entry: Interface - The interface to be searched upon. // Address - The IP address we're looking up. // // Returns: Pointer to ARPTableEntry if found, or NULL if not. // ARPTableEntry * ARPLookup(ARPInterface * Interface, IPAddr Address) { int i = ARP_HASH(Address); // Index into hash table. ARPTableEntry *Current; // Current ARP Table entry being // examined. Current = (*Interface->ai_ARPTbl)[i]; while (Current != (ARPTableEntry *) NULL) { CTEGetLockAtDPC(&Current->ate_lock); if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match. return Current; } CTEFreeLockFromDPC(&Current->ate_lock); Current = Current->ate_next; } // If we got here, we didn't find the entry. Leave the table locked and // return the handle. return(ARPTableEntry *) NULL; } //* IsBCastOnIF- See it an address is a broadcast address on an interface. // // Called to see if a particular address is a broadcast address on an // interface. We'll check the global, net, and subnet broadcasts. We assume // the caller holds the lock on the interface. // // Entry: Interface - Interface to check. // Addr - Address to check. // // Returns: TRUE if it it a broadcast, FALSE otherwise. // uint IsBCastOnIF(ARPInterface * Interface, IPAddr Addr) { IPAddr BCast; IPMask Mask; ARPIPAddr *ARPAddr; IPAddr LocalAddr; // First get the interface broadcast address. BCast = Interface->ai_bcast; // First check for global broadcast. if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr)) return TRUE; // Now walk the local addresses, and check for net/subnet bcast on each // one. ARPAddr = &Interface->ai_ipaddr; do { // See if this one is valid. LocalAddr = ARPAddr->aia_addr; if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { // He's valid. Mask = ARPAddr->aia_mask; // First check for subnet bcast. if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr)) return TRUE; // Now check all nets broadcast. Mask = IPNetMask(LocalAddr); if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr)) return TRUE; } ARPAddr = ARPAddr->aia_next; } while (ARPAddr != NULL); // If we're here, it's not a broadcast. return FALSE; } //* ARPSendBCast - See if this is a bcast or mcast frame, and send it. // // Called when we have a packet to send and we want to see if it's a broadcast // or multicast frame on this interface. We'll search the local addresses and // see if we can determine if it is. If it is, we'll send it here. Otherwise // we return FALSE, and the caller will try to resolve the address. // // Entry: Interface - A pointer to an AI structure. // Dest - Destination of datagram. // Packet - Packet to be sent. // Status - Place to return status of send attempt. // // Returns: TRUE if is was a bcast or mcast send, FALSE otherwise. // uint ARPSendBCast(ARPInterface * Interface, IPAddr Dest, PNDIS_PACKET Packet, PNDIS_STATUS Status) { uint IsBCast; CTELockHandle Handle; PNDIS_BUFFER ARPBuffer; // ARP Header buffer. uchar *BufAddr; // Address of NDIS buffer NDIS_STATUS MyStatus; ENetHeader *Hdr; FDDIHeader *FHdr; TRHeader *TRHdr; SNAPHeader UNALIGNED *SNAPPtr; RC UNALIGNED *RCPtr; ARCNetHeader *AHdr; uint DataLength; ulong Proc; // Get the lock, and see if it's a broadcast. CTEGetLock(&Interface->ai_lock, &Handle); IsBCast = IsBCastOnIF(Interface, Dest); CTEFreeLock(&Interface->ai_lock, Handle); if (IsBCast) { if (Interface->ai_operstatus == INTERFACE_UP) { uchar Size; Size = Interface->ai_hdrsize + Interface->ai_snapsize; if (Interface->ai_media == NdisMedium802_5) Size += sizeof(RC); ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size); if (ARPBuffer != NULL) { uint UNALIGNED *Temp; // Got the buffer we need. switch (Interface->ai_media) { case NdisMedium802_3: Hdr = (ENetHeader *) BufAddr; if (!CLASSD_ADDR(Dest)) RtlCopyMemory(Hdr, ENetBcst, ARP_802_ADDR_LENGTH); else { RtlCopyMemory(Hdr, ENetMcst, ARP_802_ADDR_LENGTH); Temp = (uint UNALIGNED *) & Hdr->eh_daddr[2]; *Temp |= (Dest & ARP_MCAST_MASK); } RtlCopyMemory(Hdr->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); if (Interface->ai_snapsize == 0) { // No snap on this interface, so just use ETypr. Hdr->eh_type = net_short(ARP_ETYPE_IP); } else { ushort ShortDataLength; // We're using SNAP. Find the size of the packet. NdisQueryPacket(Packet, NULL, NULL, NULL, &DataLength); ShortDataLength = (ushort) (DataLength + sizeof(SNAPHeader)); Hdr->eh_type = net_short(ShortDataLength); SNAPPtr = (SNAPHeader UNALIGNED *) (BufAddr + sizeof(ENetHeader)); RtlCopyMemory(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); } break; case NdisMedium802_5: // This is token ring. We'll have to mess around with // source routing. // for multicast - see RFC 1469. // Handle RFC 1469. if (!CLASSD_ADDR(Dest) || (!TRFunctionalMcast)) { TRHdr = (TRHeader *) BufAddr; RtlCopyMemory(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr)); RtlCopyMemory(TRHdr->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); } else { TRHdr = (TRHeader *) BufAddr; RtlCopyMemory(TRHdr, TRMcst, offsetof(TRHeader, tr_saddr)); RtlCopyMemory(TRHdr->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); } if (sIPAlwaysSourceRoute) { TRHdr->tr_saddr[0] |= TR_RII; RCPtr = (RC UNALIGNED *) ((uchar *) TRHdr + sizeof(TRHeader)); RCPtr->rc_blen = TrRii | RC_LEN; RCPtr->rc_dlf = RC_BCST_LEN; SNAPPtr = (SNAPHeader UNALIGNED *) ((uchar *) RCPtr + sizeof(RC)); } else { // // Adjust the size of the buffer to account for the // fact that we don't have the RC field. // NdisAdjustBufferLength(ARPBuffer, (Size - sizeof(RC))); SNAPPtr = (SNAPHeader UNALIGNED *) ((uchar *) TRHdr + sizeof(TRHeader)); } RtlCopyMemory(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); break; case NdisMediumFddi: FHdr = (FDDIHeader *) BufAddr; if (!CLASSD_ADDR(Dest)) RtlCopyMemory(FHdr, FDDIBcst, offsetof(FDDIHeader, fh_saddr)); else { RtlCopyMemory(FHdr, FDDIMcst, offsetof(FDDIHeader, fh_saddr)); Temp = (uint UNALIGNED *) & FHdr->fh_daddr[2]; *Temp |= (Dest & ARP_MCAST_MASK); } RtlCopyMemory(FHdr->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); SNAPPtr = (SNAPHeader UNALIGNED *) (BufAddr + sizeof(FDDIHeader)); RtlCopyMemory(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); break; case NdisMediumArcnet878_2: AHdr = (ARCNetHeader *) BufAddr; AHdr->ah_saddr = Interface->ai_addr[0]; AHdr->ah_daddr = 0; AHdr->ah_prot = ARP_ARCPROT_IP; break; default: ASSERT(0); *Status = NDIS_STATUS_UNSUPPORTED_MEDIA; FreeARPBuffer(Interface, ARPBuffer); return FALSE; } (Interface->ai_outpcount[AI_NONUCAST_INDEX])++; Proc = KeGetCurrentProcessorNumber(); Interface->ai_qlen[Proc].ai_qlen++; NdisChainBufferAtFront(Packet, ARPBuffer); NdisSend(&MyStatus, Interface->ai_handle, Packet); *Status = MyStatus; if (MyStatus != NDIS_STATUS_PENDING) { // Send finished // immediately. if (MyStatus == NDIS_STATUS_SUCCESS) { Interface->ai_outoctets += Packet->Private.TotalLength; } else { if (MyStatus == NDIS_STATUS_RESOURCES) Interface->ai_outdiscards++; else Interface->ai_outerrors++; } Proc = KeGetCurrentProcessorNumber(); Interface->ai_qlen[Proc].ai_qlen--; NdisUnchainBufferAtFront(Packet, &ARPBuffer); FreeARPBuffer(Interface, ARPBuffer); } } else *Status = NDIS_STATUS_RESOURCES; } else *Status = NDIS_STATUS_ADAPTER_NOT_READY; return TRUE; } else return FALSE; } //* ARPResolveIP - resolves IP address // // Called by IP layer when it needs to find physical address of the host // given the interface and dest IP address // Entry: Interface - A pointer to the AI structure. // ArpControlBlock - A pointer to the BufDesc chain to be sent. // // Returns: Status. // NDIS_STATUS ARPResolveIP(void *Context, IPAddr Destination, void *ArpControlBlock) { ARPInterface *ai = (ARPInterface *) Context; // Set up as AI pointer. ARPControlBlock *ArpContB = (ARPControlBlock *) ArpControlBlock; ARPTableEntry *entry; // Pointer to ARP tbl. entry CTELockHandle Handle; // Lock handle NDIS_STATUS Status; uchar ate_state; CTEGetLock(&ai->ai_ARPTblLock, &Handle); // Check if we already got the mapping. if ((entry = ARPLookup(ai, Destination)) != NULL) { // Found a matching entry. ARPLookup returns with the ATE lock held. if (entry->ate_state != ARP_GOOD) { Status = NDIS_STATUS_FAILURE; } else { Status = FillARPControlBlock(ai, entry, ArpContB); } CTEFreeLockFromDPC(&entry->ate_lock); CTEFreeLock(&ai->ai_ARPTblLock, Handle); return Status; } // We need to send arp request. CTEFreeLock(&ai->ai_ARPTblLock, Handle); entry = CreateARPTableEntry(ai, Destination, &Handle, ArpContB); if (entry != NULL) { if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry. // Someone else could have raced in and created the entry between // the time we free the lock and the time we called // CreateARPTableEntry(). We check this by looking at the packet // on the entry. If there is no old packet we'll ARP. If there is, // we'll call ARPSendData to figure out what to do. if (entry->ate_packet == NULL) { ate_state = entry->ate_state; CTEFreeLock(&entry->ate_lock, Handle); SendARPRequest(ai, Destination, ate_state, NULL, TRUE); // We don't know the state of the entry - we've freed the lock // and yielded, and it could conceivably have timed out by now, // or SendARPRequest could have failed, etc. We could take the // lock, check the status from SendARPRequest, see if it's // still the same packet, and then make a decision on the // return value, but it's easiest just to return pending. If // SendARPRequest failed, the entry will time out anyway. return NDIS_STATUS_PENDING; } else { CTEFreeLock(&entry->ate_lock, Handle); return NDIS_STATUS_PENDING; } } else if (entry->ate_state == ARP_GOOD) { // Yow! A valid entry. Status = FillARPControlBlock(ai, entry, ArpContB); //remove ArpContB from ate_resolveonly queue. if (entry->ate_resolveonly) { ARPControlBlock *TmpArpContB, *PrvArpContB = NULL; TmpArpContB = entry->ate_resolveonly; while (TmpArpContB && (ArpContB != TmpArpContB)) { PrvArpContB = TmpArpContB; TmpArpContB = TmpArpContB->next; } if (TmpArpContB == ArpContB) { if (PrvArpContB) { PrvArpContB->next = ArpContB->next; } else { entry->ate_resolveonly = NULL; } } } CTEFreeLock(&entry->ate_lock, Handle); return Status; } else { // An invalid entry! CTEFreeLock(&entry->ate_lock, Handle); return NDIS_STATUS_RESOURCES; } } else { // Couldn't create an entry. return NDIS_STATUS_RESOURCES; } } //* ARPSendData - Send a frame to a specific destination address. // // Called when we need to send a frame to a particular address, after the // ATE has been looked up. We take in an ATE and a packet, validate the state of the // ATE, and either send or ARP for the address if it's not done resolving. We assume // the lock on the ATE is held where we're called, and we'll free it before returning. // // Entry: Interface - A pointer to the AI structure. // Packet - A pointer to the BufDesc chain to be sent. // entry - A pointer to the ATE for the send. // lhandle - Pointer to a lock handle for the ATE. // // Returns: Status of the transmit - success, an error, or pending. // NDIS_STATUS ARPSendData(ARPInterface * Interface, PNDIS_PACKET Packet, ARPTableEntry * entry, CTELockHandle lhandle) { PNDIS_BUFFER ARPBuffer = NULL; // ARP Header buffer. uchar *BufAddr = NULL; // Address of NDIS buffer NDIS_STATUS Status; // Status of send. ulong Proc; #if BACK_FILL PMDL TmpMdl = NULL; #endif if (Interface->ai_operstatus == INTERFACE_UP) { if (entry->ate_state == ARP_GOOD) { // Entry is valid entry->ate_useticks = ArpCacheLife; #if BACK_FILL if (Interface->ai_media == NdisMedium802_3) { NdisQueryPacket(Packet, NULL, NULL, &TmpMdl, NULL); if (TmpMdl->MdlFlags & MDL_NETWORK_HEADER) { TmpMdl->MappedSystemVa = (PVOID) (((ULONG_PTR) TmpMdl->MappedSystemVa) - entry->ate_addrlength); TmpMdl->ByteOffset -= entry->ate_addrlength; TmpMdl->ByteCount += entry->ate_addrlength; ARPBuffer = (PNDIS_BUFFER) TmpMdl; BufAddr = TmpMdl->MappedSystemVa; } else { TmpMdl = NULL; } } if (ARPBuffer == (PNDIS_BUFFER) NULL) { ARPBuffer = GetARPBufferAtDpcLevel(Interface, &BufAddr, entry->ate_addrlength); } #else ARPBuffer = GetARPBufferAtDpcLevel(Interface, &BufAddr, entry->ate_addrlength); #endif if (ARPBuffer != (PNDIS_BUFFER) NULL) { // Everything's in good shape, copy header and send packet. (Interface->ai_outpcount[AI_UCAST_INDEX])++; Proc = KeGetCurrentProcessorNumber(); Interface->ai_qlen[Proc].ai_qlen++; RtlCopyMemory(BufAddr, entry->ate_addr, entry->ate_addrlength); // If we're on Ethernet, see if we're using SNAP here. if (Interface->ai_media == NdisMedium802_3 && entry->ate_addrlength != sizeof(ENetHeader)) { ENetHeader *Header; uint DataSize; ushort ShortDataSize; // We're apparently using SNAP on Ethernet. Query the // packet for the size, and set the length properly. NdisQueryPacket(Packet, NULL, NULL, NULL, &DataSize); #if BACK_FILL if (!TmpMdl) { ShortDataSize = (ushort) (DataSize + sizeof(SNAPHeader)); } else { ShortDataSize = (ushort) (DataSize - entry->ate_addrlength + sizeof(SNAPHeader)); } #else // BACK_FILL ShortDataSize = (ushort) (DataSize + sizeof(SNAPHeader)); #endif // !BACK_FILL Header = (ENetHeader *) BufAddr; Header->eh_type = net_short(ShortDataSize); // In case backfill is enabled, we need to remember that // a SNAP header was appended to the Ethernet header // so we can restore the correct offsets in the MDL. ((PacketContext*) Packet->ProtocolReserved)->pc_common.pc_flags |= PACKET_FLAG_SNAP; } else ((PacketContext*) Packet->ProtocolReserved)->pc_common.pc_flags &= ~PACKET_FLAG_SNAP; CTEFreeLock(&entry->ate_lock, lhandle); #if BACK_FILL if (TmpMdl == NULL) { NdisChainBufferAtFront(Packet, ARPBuffer); } #else NdisChainBufferAtFront(Packet, ARPBuffer); #endif NdisSend(&Status, Interface->ai_handle, Packet); if (Status != NDIS_STATUS_PENDING) { // Send finished // immediately. if (Status == NDIS_STATUS_SUCCESS) { Interface->ai_outoctets += Packet->Private.TotalLength; } else { if (Status == NDIS_STATUS_RESOURCES) Interface->ai_outdiscards++; else Interface->ai_outerrors++; } Proc = KeGetCurrentProcessorNumber(); Interface->ai_qlen[Proc].ai_qlen--; #if BACK_FILL if (TmpMdl == NULL) { NdisUnchainBufferAtFront(Packet, &ARPBuffer); FreeARPBuffer(Interface, ARPBuffer); } else { uint HdrSize; HdrSize = sizeof(ENetHeader); if (((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_flags & PACKET_FLAG_SNAP) HdrSize += Interface->ai_snapsize; TmpMdl->MappedSystemVa = (PVOID) (((ULONG_PTR) TmpMdl->MappedSystemVa) + HdrSize); TmpMdl->ByteOffset += HdrSize; TmpMdl->ByteCount -= HdrSize; } #else NdisUnchainBufferAtFront(Packet, &ARPBuffer); FreeARPBuffer(Interface, ARPBuffer); #endif } return Status; } else { // No buffer, free lock and return. CTEFreeLock(&entry->ate_lock, lhandle); Interface->ai_outdiscards++; return NDIS_STATUS_RESOURCES; } } // The IP addresses match, but the state of the ARP entry indicates // it's not valid. If the address is marked as resolving, we'll replace // the current cached packet with this one. If it's been more than // ARP_FLOOD_RATE ms. since we last sent an ARP request, we'll send // another one now. if (entry->ate_state <= ARP_RESOLVING) { PNDIS_PACKET OldPacket = entry->ate_packet; ulong Now = CTESystemUpTime(); entry->ate_packet = Packet; if ((Now - entry->ate_valid) > ARP_FLOOD_RATE) { IPAddr Dest = entry->ate_dest; entry->ate_valid = Now; entry->ate_state = ARP_RESOLVING_GLOBAL; // We've done this // at least once. CTEFreeLock(&entry->ate_lock, lhandle); SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL, NULL, TRUE); // Send a request. } else CTEFreeLock(&entry->ate_lock, lhandle); if (OldPacket) IPSendComplete(Interface->ai_context, OldPacket, NDIS_STATUS_SUCCESS); return NDIS_STATUS_PENDING; } else { ASSERT(0); CTEFreeLock(&entry->ate_lock, lhandle); Interface->ai_outerrors++; return NDIS_STATUS_INVALID_PACKET; } } else { // Adapter is down. Just return the error. CTEFreeLock(&entry->ate_lock, lhandle); return NDIS_STATUS_ADAPTER_NOT_READY; } } //* CreateARPTableEntry - Create a new entry in the ARP table. // // A function to put an entry into the ARP table. We allocate memory if we // need to. // // The first thing to do is get the lock on the ARP table, and see if the // entry already exists. If it does, we're done. Otherwise we need to // allocate memory and create a new entry. // // Entry: Interface - Interface for ARP table. // Destination - Destination address to be mapped. // Handle - Pointer to lock handle for entry. // // Returns: Pointer to newly created entry. // ARPTableEntry * CreateARPTableEntry(ARPInterface * Interface, IPAddr Destination, CTELockHandle * Handle, void *UserArp) { ARPTableEntry *NewEntry, *Entry; CTELockHandle TableHandle; int i = ARP_HASH(Destination); int Size; // First look for it, and if we don't find it return try to create one. CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle); if ((Entry = ARPLookup(Interface, Destination)) != NULL) { CTEFreeLockFromDPC(&Interface->ai_ARPTblLock); *Handle = TableHandle; // if we are using arp api entry, turn off the // userarp flag so that handle arp need not free it. if (!UserArp && Entry->ate_userarp) { Entry->ate_userarp = 0; } if (UserArp) { if (Entry->ate_resolveonly) { // chain the current request at the end of the new // before using the new request as the head. // ((ARPControlBlock *)UserArp)->next = Entry->ate_resolveonly; } // link the new request. // Entry->ate_resolveonly = (ARPControlBlock *)UserArp; } return Entry; } // Allocate memory for the entry. If we can't, fail the request. Size = sizeof(ARPTableEntry) - 1 + (Interface->ai_media == NdisMedium802_5 ? ARP_MAX_MEDIA_TR : (Interface->ai_hdrsize + Interface->ai_snapsize)); if ((NewEntry = CTEAllocMemN(Size, 'QiCT')) == (ARPTableEntry *) NULL) { CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); return (ARPTableEntry *) NULL; } RtlZeroMemory(NewEntry, Size); NewEntry->ate_dest = Destination; if (Interface->ai_media != NdisMedium802_5 || sArpAlwaysSourceRoute) { NewEntry->ate_state = ARP_RESOLVING_GLOBAL; } else { NewEntry->ate_state = ARP_RESOLVING_LOCAL; } if (UserArp) { NewEntry->ate_userarp = 1; } NewEntry->ate_resolveonly = (ARPControlBlock *)UserArp; NewEntry->ate_valid = CTESystemUpTime(); NewEntry->ate_useticks = ArpCacheLife; CTEInitLock(&NewEntry->ate_lock); // Entry does not exist. Insert the new entry into the table at the // appropriate spot. // NewEntry->ate_next = (*Interface->ai_ARPTbl)[i]; (*Interface->ai_ARPTbl)[i] = NewEntry; Interface->ai_count++; CTEGetLockAtDPC(&NewEntry->ate_lock); CTEFreeLockFromDPC(&Interface->ai_ARPTblLock); *Handle = TableHandle; return NewEntry; } //* ARPTransmit - Send a frame. // // The main ARP transmit routine, called by the upper layer. This routine // takes as input a buf desc chain, RCE, and size. We validate the cached // information in the RCE. If it is valid, we use it to send the frame. // Otherwise we do a table lookup. If we find it in the table, we'll update // the RCE and continue. Otherwise we'll queue the packet and start an ARP // resolution. // // Entry: Context - A pointer to the AI structure. // Packet - A pointer to the BufDesc chain to be sent. // Destination - IP address of destination we're trying to reach, // RCE - A pointer to an RCE which may have cached information. // // Returns: Status of the transmit - success, an error, or pending. // NDIS_STATUS __stdcall ARPTransmit(void *Context, PNDIS_PACKET * PacketArray, uint NumberOfPackets, IPAddr Destination, RouteCacheEntry * RCE, void *LinkCtxt) { ARPInterface *ai = (ARPInterface *) Context; // Set up as AI pointer. ARPContext *ac = NULL; // ARP context pointer. ARPTableEntry *entry; // Pointer to ARP tbl. entry CTELockHandle Handle; // Lock handle NDIS_STATUS Status; PNDIS_PACKET Packet = *PacketArray; // // For now, we get only one packet... // DBG_UNREFERENCED_PARAMETER(NumberOfPackets); UNREFERENCED_PARAMETER(LinkCtxt); ASSERT(NumberOfPackets == 1); DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_TX, (DTEXT("+ARPTransmit(%x, %x, %d, %x, %x, %x)\n"), Context, PacketArray, NumberOfPackets, Destination, RCE, LinkCtxt)); if (ai->ai_operstatus != INTERFACE_UP) { return NDIS_STATUS_ADAPTER_NOT_READY; } CTEGetLock(&ai->ai_ARPTblLock, &Handle); if (RCE != (RouteCacheEntry *) NULL) { // Have a valid RCE. ac = (ARPContext *) RCE->rce_context; // Get pointer to context entry = ac->ac_ate; if (entry != (ARPTableEntry *) NULL) { // Have a valid ATE. CTEGetLockAtDPC(&entry->ate_lock); // Lock this structure if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) { uint refresh,status; uchar state = entry->ate_state; refresh= entry->ate_refresh; CTEFreeLockFromDPC(&ai->ai_ARPTblLock); status = ARPSendData(ai, Packet, entry, Handle); // Send the data if (refresh) { if (sArpAlwaysSourceRoute) { // // If Always source route is on, // the state should ve resolving_global // so that SendArpRequest will send SR // header in case of 802.5. // state = ARP_RESOLVING_GLOBAL; } SendARPRequest(ai, Destination, state, NULL, TRUE); } return status; } // We have an RCE that identifies the wrong ATE. We'll free it from // this list and try and find an ATE that is valid. ARPRemoveRCE(entry, RCE); CTEFreeLockFromDPC(&entry->ate_lock); // Fall through to 'no valid entry' code. } } // Here we have no valid ATE, either because the RCE is NULL or the ATE // specified by the RCE was invalid. We'll try and find one in the table. If // we find one, we'll fill in this RCE and send the packet. Otherwise we'll // try to create one. At this point we hold the lock on the ARP table. if ((entry = ARPLookup(ai, Destination)) != (ARPTableEntry *) NULL) { // Found a matching entry. ARPLookup returns with the ATE lock held. if (RCE != (RouteCacheEntry *) NULL) { ac->ac_next = entry->ate_rce; // Fill in context for next time. entry->ate_rce = RCE; ac->ac_ate = entry; } DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX, (DTEXT("ARPTx: ATE %x - calling ARPSendData\n"), entry)); CTEFreeLockFromDPC(&ai->ai_ARPTblLock); return ARPSendData(ai, Packet, entry, Handle); } // No valid entry in the ARP table. First we'll see if we're sending to a // broadcast address or multicast address. If not, we'll try to create // an entry in the table and get an ARP resolution going. ARPLookup returns // with the table lock held when it fails, we'll free it here. CTEFreeLock(&ai->ai_ARPTblLock, Handle); if (ARPSendBCast(ai, Destination, Packet, &Status)) { return Status; } entry = CreateARPTableEntry(ai, Destination, &Handle, 0); if (entry != NULL) { if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry. uchar state = entry->ate_state; DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX, (DTEXT("ARPTx: Created ATE %x\n"), entry)); // Someone else could have raced in and created the entry between // the time we free the lock and the time we called // CreateARPTableEntry(). We check this by looking at the packet // on the entry. If there is no old packet we'll ARP. If there is, // we'll call ARPSendData to figure out what to do. if (entry->ate_packet == NULL) { entry->ate_packet = Packet; DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX, (DTEXT("ARPTx: ATE %x - calling SendARPRequest\n"), entry)); CTEFreeLock(&entry->ate_lock, Handle); SendARPRequest(ai, Destination, state, NULL, TRUE); // We don't know the state of the entry - we've freed the lock // and yielded, and it could conceivably have timed out by now, // or SendARPRequest could have failed, etc. We could take the // lock, check the status from SendARPRequest, see if it's // still the same packet, and then make a decision on the // return value, but it's easiest just to return pending. If // SendARPRequest failed, the entry will time out anyway. return NDIS_STATUS_PENDING; } else { return ARPSendData(ai, Packet, entry, Handle); } } else if (entry->ate_state == ARP_GOOD) { // Yow! A valid entry. return ARPSendData(ai, Packet, entry, Handle); } else { // An invalid entry! CTEFreeLock(&entry->ate_lock, Handle); return NDIS_STATUS_RESOURCES; } } else { // Couldn't create an entry. DEBUGMSG(DBG_ERROR && DBG_ARP, (DTEXT("ARPTx: Failed to create ATE.\n"))); return NDIS_STATUS_RESOURCES; } } //* RemoveARPTableEntry - Delete an entry from the ARP table. // // This is a simple utility function to delete an entry from the ATP table. We // assume locks are held on both the table and the entry. // // Entry: Previous - The entry immediately before the one to be deleted. // Entry - The entry to be deleted. // // Returns: Nothing. // void RemoveARPTableEntry(ARPTableEntry * Previous, ARPTableEntry * Entry) { RouteCacheEntry *RCE; // Pointer to route cache entry ARPContext *AC; RCE = Entry->ate_rce; // Loop through and invalidate all RCEs on this ATE. while (RCE != (RouteCacheEntry *) NULL) { AC = (ARPContext *) RCE->rce_context; AC->ac_ate = (ARPTableEntry *) NULL; RCE = AC->ac_next; } // Splice this guy out of the list. Previous->ate_next = Entry->ate_next; } //* ARPFlushATE - removes ARP Table entry for given dest address // // Called by IP layer when it needs to flush the link layer address from arp // cache // Entry: Interface - A pointer to the AI structure. // Destination - Destination Address whose Xlation needs to be removed // // Returns: TRUE if the entry was found and flushed, FALSE otherwise // BOOLEAN ARPFlushATE(void *Context, IPAddr Address) { ARPInterface *ai = (ARPInterface *) Context; CTELockHandle lhandle, tlhandle; ARPTable *Table; ARPTableEntry *Current, *Previous; int i = ARP_HASH(Address); PNDIS_PACKET OldPacket = NULL; CTEGetLock(&ai->ai_ARPTblLock, &tlhandle); Table = ai->ai_ARPTbl; Current = (*Table)[i]; Previous = (ARPTableEntry *) ((uchar *) & ((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next)); while (Current != (ARPTableEntry *) NULL) { CTEGetLock(&Current->ate_lock, &lhandle); if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match. if (Current->ate_resolveonly) { ARPControlBlock *ArpContB, *TmpArpContB; ArpContB = Current->ate_resolveonly; while (ArpContB) { ArpRtn rtn; rtn = (ArpRtn) ArpContB->CompletionRtn; ArpContB->status = (ULONG) STATUS_UNSUCCESSFUL; TmpArpContB = ArpContB->next; (*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL); ArpContB = TmpArpContB; } Current->ate_resolveonly = NULL; } RemoveARPTableEntry(Previous, Current); CTEFreeLock(&Current->ate_lock, lhandle); OldPacket = Current->ate_packet; CTEFreeLock(&ai->ai_ARPTblLock, tlhandle); if (OldPacket) { IPSendComplete(ai->ai_context, OldPacket, NDIS_STATUS_SUCCESS); } CTEFreeMem(Current); return TRUE; } CTEFreeLock(&Current->ate_lock, lhandle); Previous = Current; Current = Current->ate_next; } CTEFreeLock(&ai->ai_ARPTblLock, tlhandle); return FALSE; } //* ARPFlushAllATE - removes all ARP Table entries. // // Entry: Interface - A pointer to the AI structure. // // Returns: None // void ARPFlushAllATE(void *Context) { ARPInterface *ai = (ARPInterface *) Context; CTELockHandle tlhandle; ARPTable *Table; int i; ARPTableEntry *ATE; PNDIS_PACKET PList = (PNDIS_PACKET) NULL; CTEGetLock(&ai->ai_ARPTblLock, &tlhandle); Table = ai->ai_ARPTbl; if (Table != NULL) { for (i = 0; i < ARP_TABLE_SIZE; i++) { while ((*Table)[i] != NULL) { ATE = (*Table)[i]; if (ATE->ate_resolveonly) { ARPControlBlock *ArpContB, *TmpArpContB; ArpContB = ATE->ate_resolveonly; while (ArpContB) { ArpRtn rtn; rtn = (ArpRtn) ArpContB->CompletionRtn; ArpContB->status = (ULONG) STATUS_UNSUCCESSFUL; TmpArpContB = ArpContB->next; (*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL); ArpContB = TmpArpContB; } ATE->ate_resolveonly = NULL; } // Acquire ate_lock to ensure exclusive access to the ATE. CTEGetLockAtDPC(&ATE->ate_lock); RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]), ate_next), ATE); CTEFreeLockFromDPC(&ATE->ate_lock); if (ATE->ate_packet) { ((PacketContext *) ATE->ate_packet->ProtocolReserved)->pc_common.pc_link = PList; PList = ATE->ate_packet; } CTEFreeMem(ATE); } } } CTEFreeLock(&ai->ai_ARPTblLock, tlhandle); while (PList != (PNDIS_PACKET) NULL) { PNDIS_PACKET Packet = PList; PList = ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link; IPSendComplete(ai->ai_context, Packet, NDIS_STATUS_SUCCESS); } } //* ARPXferData - Transfer data on behalf on an upper later protocol. // // This routine is called by the upper layer when it needs to transfer data // from an NDIS driver. We just map his call down. // // Entry: Context - Context value we gave to IP (really a pointer to an AI). // MACContext - Context value MAC gave us on a receive. // MyOffset - Packet offset we gave to the protocol earlier. // ByteOffset - Byte offset into packet protocol wants transferred. // BytesWanted - Number of bytes to transfer. // Packet - Pointer to packet to be used for transferring. // Transferred - Pointer to where to return bytes transferred. // // Returns: NDIS_STATUS of command. // NDIS_STATUS __stdcall ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset, uint ByteOffset, uint BytesWanted, PNDIS_PACKET Packet, uint * Transferred) { ARPInterface *Interface = (ARPInterface *) Context; NDIS_STATUS Status; NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset + MyOffset, BytesWanted, Packet, Transferred); return Status; } //* ARPUpdateOperStatus - Update the OperStatus and LastChange values. // // Called whenever ai_adminstate or ai_mediastatus changes, to preserve // the invariant that ai_operstatus should only be up if the admin // status is up and media sense is present. // // Entry: Interface - Interface to update. // // Returns: Nothing. // __inline void ARPUpdateOperStatus(ARPInterface *Interface) { uchar NewOperStatus; if (Interface->ai_operstatus == INTERFACE_UNINIT) { return; } if ((Interface->ai_adminstate == IF_STATUS_DOWN) || (Interface->ai_mediastatus == FALSE)) { NewOperStatus = INTERFACE_DOWN; } else { NewOperStatus = INTERFACE_UP; } if (Interface->ai_operstatus != NewOperStatus) { Interface->ai_operstatus = NewOperStatus; Interface->ai_lastchange = GetTimeTicks(); } } //* ARPClose - Close an adapter. // // Called by IP when it wants to close an adapter, presumably due to an error condition. // We'll close the adapter, but we won't free any memory. // // Entry: Context - Context value we gave him earlier. // // Returns: Nothing. // void __stdcall ARPClose(void *Context) { ARPInterface *Interface = (ARPInterface *) Context; NDIS_STATUS Status; CTELockHandle LockHandle; NDIS_HANDLE Handle; Interface->ai_mediastatus = FALSE; ARPUpdateOperStatus(Interface); // // Mark the interface as going away so it will disappear from the // entity list. // Interface->ai_operstatus = INTERFACE_UNINIT; CTEInitBlockStruc(&Interface->ai_block); CTEGetLock(&Interface->ai_lock, &LockHandle); if (Interface->ai_handle != (NDIS_HANDLE) NULL) { Handle = Interface->ai_handle; CTEFreeLock(&Interface->ai_lock, LockHandle); NdisCloseAdapter(&Status, Handle); if (Status == NDIS_STATUS_PENDING) { Status = CTEBlock(&Interface->ai_block); } Interface->ai_handle = NULL; } else { CTEFreeLock(&Interface->ai_lock, LockHandle); } } //* ARPInvalidate - Notification that an RCE is invalid. // // Called by IP when an RCE is closed or otherwise invalidated. We look up // the ATE for the specified RCE, and then remove the RCE from the ATE list. // // Entry: Context - Context value we gave him earlier. // RCE - RCE to be invalidated // // Returns: Nothing. // void __stdcall ARPInvalidate(void *Context, RouteCacheEntry *RCE) { ARPInterface *Interface = (ARPInterface *) Context; ARPTableEntry *ATE; CTELockHandle Handle; ARPContext *AC = (ARPContext *) RCE->rce_context; CTEGetLock(&Interface->ai_ARPTblLock, &Handle); #if DBG if (!(RCE->rce_flags & RCE_CONNECTED)) { ARPTableEntry *tmpATE; ATE = ARPLookup(Interface, RCE->rce_dest); if (ATE != NULL) { tmpATE = ATE; while (ATE) { if (ATE->ate_rce == RCE) { DbgBreakPoint(); } ATE = ATE->ate_next; } CTEFreeLockFromDPC(&Interface->ai_ARPTblLock); CTEFreeLock(&tmpATE->ate_lock, Handle); return; } } #endif if ((ATE = AC->ac_ate) == (ARPTableEntry *) NULL) { CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE. return; } CTEGetLockAtDPC(&ATE->ate_lock); ARPRemoveRCE(ATE, RCE); RtlZeroMemory(RCE->rce_context, RCE_CONTEXT_SIZE); CTEFreeLockFromDPC(&Interface->ai_ARPTblLock); CTEFreeLock(&ATE->ate_lock, Handle); } //* ARPSetMCastList - Set the multicast address list for the adapter. // // Called to try and set the multicast reception list for the adapter. // We allocate a buffer big enough to hold the new address list, and format // the address list into the buffer. Then we submit the NDIS request to set // the list. If we can't set the list because the multicast address list is // full we'll put the card into all multicast mode. // // Input: Interface - Interface on which to set list. // // Returns: NDIS_STATUS of attempt. // NDIS_STATUS ARPSetMCastList(ARPInterface * Interface) { CTELockHandle Handle; uchar *MCastBuffer, *CurrentPtr; uint MCastSize; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; uint i; ARPMCastAddr *AddrPtr; IPAddr UNALIGNED *Temp; CTEGetLock(&Interface->ai_lock, &Handle); MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH; if (MCastSize != 0) MCastBuffer = CTEAllocMemN(MCastSize, 'RiCT'); else MCastBuffer = NULL; if (MCastBuffer != NULL || MCastSize == 0) { // Got the buffer. Loop through, building the list. AddrPtr = Interface->ai_mcast; CurrentPtr = MCastBuffer; for (i = 0; i < Interface->ai_mcastcnt; i++) { ASSERT(AddrPtr != NULL); if (Interface->ai_media == NdisMedium802_3) { RtlCopyMemory(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH); Temp = (IPAddr UNALIGNED *) (CurrentPtr + 2); *Temp |= AddrPtr->ama_addr; } else if ((Interface->ai_media == NdisMedium802_5) & TRFunctionalMcast) { RtlCopyMemory(CurrentPtr, TRNetMcst, ARP_802_ADDR_LENGTH - 2); MCastSize = 4; } else if (Interface->ai_media == NdisMediumFddi) { RtlCopyMemory(CurrentPtr, ((FDDIHeader *) FDDIMcst)->fh_daddr, ARP_802_ADDR_LENGTH); Temp = (IPAddr UNALIGNED *) (CurrentPtr + 2); *Temp |= AddrPtr->ama_addr; } else ASSERT(0); CurrentPtr += ARP_802_ADDR_LENGTH; AddrPtr = AddrPtr->ama_next; } CTEFreeLock(&Interface->ai_lock, Handle); // We're built the list. Now give it to the driver to handle. if (Interface->ai_media == NdisMedium802_3) { Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_802_3_MULTICAST_LIST, MCastBuffer, MCastSize, NULL, TRUE); } else if ((Interface->ai_media == NdisMedium802_5) & TRFunctionalMcast) { if (!(Interface->ai_pfilter & NDIS_PACKET_TYPE_FUNCTIONAL)) { Interface->ai_pfilter |= NDIS_PACKET_TYPE_FUNCTIONAL; Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL, TRUE); } Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_802_5_CURRENT_FUNCTIONAL, MCastBuffer, MCastSize, NULL, TRUE); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "SetMcast after OID-- TR mcast address on %x status %x\n", Interface, Status)); } else if (Interface->ai_media == NdisMediumFddi) { Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL, TRUE); } else ASSERT(0); if (MCastBuffer != NULL) { CTEFreeMem(MCastBuffer); } if (Status == NDIS_STATUS_MULTICAST_FULL) { // Multicast list is full. Try to set the filter to all multicasts. Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST; Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL, TRUE); } } else { CTEFreeLock(&Interface->ai_lock, Handle); Status = NDIS_STATUS_RESOURCES; } return Status; } //* ARPFindMCast - Find a multicast address structure on our list. // // Called as a utility to find a multicast address structure. If we find // it, we return a pointer to it and it's predecessor. Otherwise we return // NULL. We assume the caller holds the lock on the interface already. // // Input: Interface - Interface to search. // Addr - Addr to find. // Prev - Where to return previous pointer. // // Returns: Pointer if we find one, NULL otherwise. // ARPMCastAddr * ARPFindMCast(ARPInterface * Interface, IPAddr Addr, ARPMCastAddr ** Prev) { ARPMCastAddr *AddrPtr, *PrevPtr; PrevPtr = STRUCT_OF(ARPMCastAddr, &Interface->ai_mcast, ama_next); AddrPtr = PrevPtr->ama_next; while (AddrPtr != NULL) { if (IP_ADDR_EQUAL(AddrPtr->ama_addr, Addr)) break; else { PrevPtr = AddrPtr; AddrPtr = PrevPtr->ama_next; } } *Prev = PrevPtr; return AddrPtr; } //* ARPDelMCast - Delete a multicast address. // // Called when we want to delete a multicast address. We look for a matching // (masked) address. If we find one, we'll dec. the reference count and if // it goes to 0 we'll pull him from the list and reset the multicast list. // // Input: Interface - Interface on which to act. // Addr - Address to be deleted. // // Returns: TRUE if it worked, FALSE otherwise. // uint ARPDelMCast(ARPInterface * Interface, IPAddr Addr) { ARPMCastAddr *AddrPtr, *PrevPtr; CTELockHandle Handle; uint Status = TRUE; // When we support TR (RFC 1469) fully we'll need to change this. if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media == NdisMediumFddi || (Interface->ai_media == NdisMedium802_5 && TRFunctionalMcast)) { // This is an interface that supports mcast addresses. Addr &= ARP_MCAST_MASK; CTEGetLock(&Interface->ai_lock, &Handle); AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr); if (AddrPtr != NULL) { // We found one. Dec. his refcnt, and if it's 0 delete him. (AddrPtr->ama_refcnt)--; if (AddrPtr->ama_refcnt == 0) { // He's done. PrevPtr->ama_next = AddrPtr->ama_next; (Interface->ai_mcastcnt)--; CTEFreeLock(&Interface->ai_lock, Handle); CTEFreeMem(AddrPtr); ARPSetMCastList(Interface); CTEGetLock(&Interface->ai_lock, &Handle); } } else Status = FALSE; CTEFreeLock(&Interface->ai_lock, Handle); } return Status; } //* ARPAddMCast - Add a multicast address. // // Called when we want to start receiving a multicast address. We'll mask // the address and look it up in our address list. If we find it, we'll just // bump the reference count. Otherwise we'll try to create one and put him // on the list. In that case we'll need to set the multicast address list for // the adapter. // // Input: Interface - Interface to set on. // Addr - Address to set. // // Returns: TRUE if we succeed, FALSE if we fail. // uint ARPAddMCast(ARPInterface * Interface, IPAddr Addr) { ARPMCastAddr *AddrPtr, *PrevPtr; CTELockHandle Handle; uint Status = TRUE; if (Interface->ai_operstatus != INTERFACE_UP) { return FALSE; } // Currently we don't do anything with token ring, since we send // all mcasts as TR broadcasts. When we comply with RFC 1469 we'll need to // fix this. if ((Interface->ai_media == NdisMedium802_3) || (Interface->ai_media == NdisMediumFddi) || ((Interface->ai_media == NdisMedium802_5) && TRFunctionalMcast)) { Addr &= ARP_MCAST_MASK; CTEGetLock(&Interface->ai_lock, &Handle); AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr); if (AddrPtr != NULL) { // We found one, just bump refcnt. (AddrPtr->ama_refcnt)++; } else { // Didn't find one. Allocate space for one, link him in, and // try to set the list. AddrPtr = CTEAllocMemN(sizeof(ARPMCastAddr), 'SiCT'); if (AddrPtr != NULL) { // Got one. Link him in. AddrPtr->ama_addr = Addr; AddrPtr->ama_refcnt = 1; AddrPtr->ama_next = Interface->ai_mcast; Interface->ai_mcast = AddrPtr; (Interface->ai_mcastcnt)++; CTEFreeLock(&Interface->ai_lock, Handle); // Now try to set the list. if (ARPSetMCastList(Interface) != NDIS_STATUS_SUCCESS) { // Couldn't set the list. Call the delete routine to delete // the address we just tried to set. Status = ARPDelMCast(Interface, Addr); ASSERT(Status); Status = FALSE; } CTEGetLock(&Interface->ai_lock, &Handle); } else Status = FALSE; // Couldn't get memory. } // We've done out best. Free the lock and return. CTEFreeLock(&Interface->ai_lock, Handle); } return Status; } //* ARPAddAddr - Add an address to the ARP table. // // This routine is called by IP to add an address as a local address, or // or specify the broadcast address for this interface. // // Entry: Context - Context we gave IP earlier (really an ARPInterface ptr) // Type - Type of address (local, p-arp, multicast, or // broadcast). // Address - Broadcast IP address to be added. // Mask - Mask for address. // // Returns: 0 if we failed, non-zero otherwise // uint __stdcall ARPAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) { ARPInterface *Interface = (ARPInterface *) Context; CTELockHandle Handle; if (Type != LLIP_ADDR_LOCAL && Type != LLIP_ADDR_PARP) { // Not a local address, must be broadcast or multicast. if (Type == LLIP_ADDR_BCAST) { Interface->ai_bcast = Address; return TRUE; } else if (Type == LLIP_ADDR_MCAST) { return ARPAddMCast(Interface, Address); } else return FALSE; } else { // This is a local address. CTEGetLock(&Interface->ai_lock, &Handle); if (Type != LLIP_ADDR_PARP) { uint RetStatus = FALSE; uint ArpForSelf = FALSE; if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, 0)) { Interface->ai_ipaddr.aia_addr = Address; Interface->ai_ipaddr.aia_mask = Mask; Interface->ai_ipaddr.aia_age = ArpRetryCount; if (Interface->ai_operstatus == INTERFACE_UP) { // When ArpRetryCount is 0, we'll return immediately // below, so don't save completion context Interface->ai_ipaddr.aia_context = (ArpRetryCount > 0)? Context2 : NULL; ArpForSelf = TRUE; } else { Interface->ai_ipaddr.aia_context = NULL; } RetStatus = TRUE; } else { ARPIPAddr *NewAddr; NewAddr = CTEAllocMemNBoot(sizeof(ARPIPAddr), 'TiCT'); if (NewAddr != (ARPIPAddr *) NULL) { NewAddr->aia_addr = Address; NewAddr->aia_mask = Mask; NewAddr->aia_age = ArpRetryCount; NewAddr->aia_next = Interface->ai_ipaddr.aia_next; if (Interface->ai_operstatus == INTERFACE_UP) { // When ArpRetryCount is 0, we'll return immediately // below, so don't save completion context NewAddr->aia_context = (ArpRetryCount > 0)? Context2 : NULL; ArpForSelf = TRUE; } else { NewAddr->aia_context = NULL; } Interface->ai_ipaddr.aia_next = NewAddr; RetStatus = TRUE; } } if (RetStatus) { Interface->ai_ipaddrcnt++; if (Interface->ai_telladdrchng) { CTEFreeLock(&Interface->ai_lock, Handle); AddrNotifyLink(Interface); } else { CTEFreeLock(&Interface->ai_lock, Handle); } } else { CTEFreeLock(&Interface->ai_lock, Handle); } // add wakeup pattern for this address, if the address is in // conflict ip will turn around and delete the address thus // deleting the wakeup pattern. ARPWakeupPattern(Interface, Address, TRUE); // ARP for the address we've added, to see it it already exists. if (RetStatus == TRUE && ArpForSelf == TRUE) { if (ArpRetryCount) { SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL, NULL, TRUE); return IP_PENDING; } else { return TRUE; } } return RetStatus; } else if (Type == LLIP_ADDR_PARP) { ARPPArpAddr *NewPArp, *TmpPArp; // He's adding a proxy arp address. // Don't allow to add duplicate proxy arp entries TmpPArp = Interface->ai_parpaddr; while (TmpPArp) { if (IP_ADDR_EQUAL(TmpPArp->apa_addr, Address) && IP_ADDR_EQUAL(TmpPArp->apa_mask, Mask)) { CTEFreeLock(&Interface->ai_lock, Handle); return FALSE; } TmpPArp = TmpPArp->apa_next; } NewPArp = CTEAllocMemN(sizeof(ARPPArpAddr), 'UiCT'); if (NewPArp != NULL) { NewPArp->apa_addr = Address; NewPArp->apa_mask = Mask; NewPArp->apa_next = Interface->ai_parpaddr; Interface->ai_parpaddr = NewPArp; Interface->ai_parpcount++; CTEFreeLock(&Interface->ai_lock, Handle); return TRUE; } CTEFreeLock(&Interface->ai_lock, Handle); } return FALSE; } } //* ARPDeleteAddr - Delete a local or proxy address. // // Called to delete a local or proxy address. // // Entry: Context - An ARPInterface pointer. // Type - Type of address (local or p-arp). // Address - IP address to be deleted. // Mask - Mask for address. Used only for deleting proxy-ARP // entries. // // Returns: 0 if we failed, non-zero otherwise // uint __stdcall ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) { ARPInterface *Interface = (ARPInterface *) Context; CTELockHandle Handle; ARPIPAddr *DelAddr, *PrevAddr; ARPPArpAddr *DelPAddr, *PrevPAddr; SetAddrControl *SAC; AddAddrNotifyEvent *DelayedEvent; if (Type == LLIP_ADDR_LOCAL) { CTEGetLock(&Interface->ai_lock, &Handle); if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) { IPAddr IpAddress; ARPIPAddr *Addr; Addr = &Interface->ai_ipaddr; IpAddress = Addr->aia_addr; Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR; Interface->ai_ipaddrcnt--; if (Interface->ai_telladdrchng) { CTEFreeLock(&Interface->ai_lock, Handle); AddrNotifyLink(Interface); CTEGetLock(&Interface->ai_lock, &Handle); } // if the address is deleted before the add completes, complete the add here // Doing this will complete the irp and also decrements the refcount on the interface if (Addr->aia_context != NULL) { SAC = (SetAddrControl *) Addr->aia_context; Addr->aia_context = NULL; CTEFreeLock(&Interface->ai_lock, Handle); // We cannot call completion routine at timer DPC // because completion routine will need to notify // TDI clients and that could take long time. DelayedEvent = CTEAllocMemNBoot(sizeof(AddAddrNotifyEvent), 'ViCT'); if (DelayedEvent) { DelayedEvent->SAC = SAC; DelayedEvent->Address = IpAddress; DelayedEvent->Status = IP_SUCCESS; CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed); CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent); } else { ASSERT(FALSE); return FALSE; } } else { CTEFreeLock(&Interface->ai_lock, Handle); } ARPWakeupPattern(Interface, Address, FALSE); return TRUE; } else { PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next); DelAddr = PrevAddr->aia_next; while (DelAddr != NULL) if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address)) break; else { PrevAddr = DelAddr; DelAddr = DelAddr->aia_next; } if (DelAddr != NULL) { PrevAddr->aia_next = DelAddr->aia_next; if (DelAddr->aia_next && DelAddr->aia_next->aia_age == ARPADDR_MARKER) { DelAddr->aia_next->aia_context = (PVOID) PrevAddr; } Interface->ai_ipaddrcnt--; if (Interface->ai_telladdrchng) { CTEFreeLock(&Interface->ai_lock, Handle); AddrNotifyLink(Interface); } else { CTEFreeLock(&Interface->ai_lock, Handle); } if (DelAddr->aia_context != NULL) { SAC = (SetAddrControl *) DelAddr->aia_context; DelAddr->aia_context = NULL; DelayedEvent = CTEAllocMemNBoot(sizeof(AddAddrNotifyEvent), 'ViCT'); if (DelayedEvent) { DelayedEvent->SAC = SAC; DelayedEvent->Address = DelAddr->aia_addr; DelayedEvent->Status = IP_SUCCESS; CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed); CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent); } else { ASSERT(FALSE); return FALSE; } } CTEFreeMem(DelAddr); ARPWakeupPattern(Interface, Address, FALSE); } else { CTEFreeLock(&Interface->ai_lock, Handle); } return(DelAddr != NULL); } } else if (Type == LLIP_ADDR_PARP) { CTEGetLock(&Interface->ai_lock, &Handle); PrevPAddr = STRUCT_OF(ARPPArpAddr, &Interface->ai_parpaddr, apa_next); DelPAddr = PrevPAddr->apa_next; while (DelPAddr != NULL) if (IP_ADDR_EQUAL(DelPAddr->apa_addr, Address) && DelPAddr->apa_mask == Mask) break; else { PrevPAddr = DelPAddr; DelPAddr = DelPAddr->apa_next; } if (DelPAddr != NULL) { PrevPAddr->apa_next = DelPAddr->apa_next; Interface->ai_parpcount--; CTEFreeMem(DelPAddr); } CTEFreeLock(&Interface->ai_lock, Handle); return(DelPAddr != NULL); } else if (Type == LLIP_ADDR_MCAST) return ARPDelMCast(Interface, Address); else return FALSE; } //*AddrNotifyLink - Notify link layer of Network Address changes // // Called when address are added/deleted on an interface // // Entry: Interface - ARPinterface pointer // // returns: NDIS_STATUS.Also sets ai_telladdrchng if status is failure // when this happens caller can check and see if next addr notification // need to be done or not. // NDIS_STATUS AddrNotifyLink(ARPInterface * Interface) { PNETWORK_ADDRESS_LIST AddressList; NETWORK_ADDRESS UNALIGNED *Address; int i = 0, size, count; ARPIPAddr *addrlist; NDIS_STATUS status = NDIS_STATUS_FAILURE; CTELockHandle Handle; CTEGetLock(&Interface->ai_lock, &Handle); size = Interface->ai_ipaddrcnt * (sizeof(NETWORK_ADDRESS_IP) + FIELD_OFFSET(NETWORK_ADDRESS, Address)) + FIELD_OFFSET(NETWORK_ADDRESS_LIST, Address); AddressList = CTEAllocMemN(size, 'WiCT'); if (AddressList) { addrlist = &Interface->ai_ipaddr; count = Interface->ai_ipaddrcnt; AddressList->AddressType = NDIS_PROTOCOL_ID_TCP_IP; while (addrlist && count) { NETWORK_ADDRESS_IP UNALIGNED *tmpIPAddr; uchar *Address0; // // Skip if this is a Marker. // if (addrlist->aia_age != ARPADDR_MARKER) { Address0 = (uchar *) & AddressList->Address[0]; Address = (PNETWORK_ADDRESS) (Address0 + i * (FIELD_OFFSET(NETWORK_ADDRESS, Address) + sizeof(NETWORK_ADDRESS_IP))); tmpIPAddr = (PNETWORK_ADDRESS_IP) & Address->Address[0]; Address->AddressLength = sizeof(NETWORK_ADDRESS_IP); Address->AddressType = NDIS_PROTOCOL_ID_TCP_IP; RtlCopyMemory(&tmpIPAddr->in_addr, &addrlist->aia_addr, sizeof(IPAddr)); count--; i++; } addrlist = addrlist->aia_next; } CTEFreeLock(&Interface->ai_lock, Handle); AddressList->AddressCount = i; status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_NETWORK_LAYER_ADDRESSES, AddressList, size, NULL, TRUE); if (status != NDIS_STATUS_SUCCESS) { CTEGetLock(&Interface->ai_lock, &Handle); Interface->ai_telladdrchng = 0; CTEFreeLock(&Interface->ai_lock, Handle); } CTEFreeMem(AddressList); } else { CTEFreeLock(&Interface->ai_lock, Handle); status = NDIS_STATUS_RESOURCES; } return status; } #if !MILLEN //* ARPCancelPackets // // Entry: Context - Pointer to the ARPInterface // ID - Pattern that need to be passed down to ndis // // Returns: Nothing // VOID __stdcall ARPCancelPackets(void *Context, void *ID) { ARPInterface *Interface = (ARPInterface *) Context; NdisCancelSendPackets(Interface->ai_handle,ID); } #endif //* DoWakeupPattern - Adds and removes wakeup pattern. // // Entry: Context - Pointer to the ARPInterface // PtrnDesc - Pattern buffer(s) of high level protocol // protoid - the proto type used in ethernet or snap type fields. // AddPattern - TRUE if pattern is to be added, FALSE if it is to be removed. // // Returns: Nothing. // NDIS_STATUS __stdcall DoWakeupPattern(void *Context, PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc, ushort protoid, BOOLEAN AddPattern) { ARPInterface *Interface = (ARPInterface *) Context; uint PtrnLen; uint PtrnBufferLen; uint MaskLen; PNET_PM_WAKEUP_PATTERN_DESC CurPtrnDesc; uchar *NextMask, *NextPtrn; const uchar *MMask; uint MMaskLength; uchar NextMaskBit; uchar *Buffer; PNDIS_PM_PACKET_PATTERN PtrnBuffer; NDIS_STATUS Status; // // First find the total length of the pattern. // Pattern starts right at MacHeader. // // First add the media portion of the header. // PtrnLen = Interface->ai_hdrsize + Interface->ai_snapsize; // now add the high level proto pattern size. CurPtrnDesc = PtrnDesc; while (CurPtrnDesc != (PNET_PM_WAKEUP_PATTERN_DESC) NULL) { PtrnLen += CurPtrnDesc->PtrnLen; CurPtrnDesc = CurPtrnDesc->Next; } // length of the mask: every byte of pattern requires // one bit of the mask. MaskLen = GetWakeupPatternMaskLength(PtrnLen); // total length of the pattern buffer to be given to ndis. PtrnBufferLen = sizeof(NDIS_PM_PACKET_PATTERN) + PtrnLen + MaskLen; if ((Buffer = CTEAllocMemN(PtrnBufferLen, 'XiCT')) == (uchar *) NULL) { return NDIS_STATUS_RESOURCES; } RtlZeroMemory(Buffer, PtrnBufferLen); PtrnBuffer = (PNDIS_PM_PACKET_PATTERN) Buffer; PtrnBuffer->PatternSize = PtrnLen; NextMask = Buffer + sizeof(NDIS_PM_PACKET_PATTERN); NextPtrn = NextMask + MaskLen; PtrnBuffer->MaskSize = MaskLen; PtrnBuffer->PatternOffset = (ULONG) ((ULONG_PTR) NextPtrn - (ULONG_PTR) PtrnBuffer); // Figure out what type of media this is, and do the appropriate thing. switch (Interface->ai_media) { case NdisMedium802_3: if (Interface->ai_snapsize == 0) { ENetHeader UNALIGNED *Hdr = (ENetHeader UNALIGNED *) NextPtrn; Hdr->eh_type = net_short(protoid); MMask = ENetPtrnMsk; } else { MMask = ENetSNAPPtrnMsk; } break; case NdisMedium802_5: if (Interface->ai_snapsize == 0) { MMask = TRPtrnMsk; } else { MMask = TRSNAPPtrnMsk; } break; case NdisMediumFddi: if (Interface->ai_snapsize == 0) { MMask = FDDIPtrnMsk; } else { MMask = FDDISNAPPtrnMsk; } break; case NdisMediumArcnet878_2: MMask = ARCPtrnMsk; break; default: ASSERT(0); Interface->ai_outerrors++; CTEFreeMem(Buffer); return NDIS_STATUS_UNSUPPORTED_MEDIA; } NextPtrn += Interface->ai_hdrsize; // Copy in SNAP header, if any. if (Interface->ai_snapsize) { SNAPHeader UNALIGNED *SNAPPtr = (SNAPHeader UNALIGNED *) NextPtrn; RtlCopyMemory(SNAPPtr, ARPSNAP, Interface->ai_snapsize); SNAPPtr->sh_etype = net_short(protoid); NextPtrn += Interface->ai_snapsize; } // MMaskLength = (Interface->ai_snapsize + Interface->ai_hdrsize - 1) / 8 + 1; // copy the mask for media part RtlCopyMemory(NextMask, MMask, MMaskLength); NextMaskBit = (Interface->ai_hdrsize + Interface->ai_snapsize) % 8; NextMask = NextMask + (Interface->ai_hdrsize + Interface->ai_snapsize) / 8; // copy the pattern and mask of high level proto. CurPtrnDesc = PtrnDesc; while (CurPtrnDesc) { uint CopyBits = CurPtrnDesc->PtrnLen; uchar *SrcMask = CurPtrnDesc->Mask; uchar SrcMaskBit = 0; RtlCopyMemory(NextPtrn, CurPtrnDesc->Ptrn, CurPtrnDesc->PtrnLen); NextPtrn += CurPtrnDesc->PtrnLen; while (CopyBits--) { *NextMask |= ((*SrcMask & (0x1 << SrcMaskBit)) ? (0x1 << NextMaskBit) : 0); if ((NextMaskBit = ((NextMaskBit + 1) % 8)) == 0) { NextMask++; } if ((SrcMaskBit = ((SrcMaskBit + 1) % 8)) == 0) { SrcMask++; } } CurPtrnDesc = CurPtrnDesc->Next; } // now tell ndis to set or remove the pattern. Status = DoNDISRequest( Interface, NdisRequestSetInformation, AddPattern ? OID_PNP_ADD_WAKE_UP_PATTERN : OID_PNP_REMOVE_WAKE_UP_PATTERN, PtrnBuffer, PtrnBufferLen, NULL, TRUE); CTEFreeMem(Buffer); return Status; } //* ARPWakeupPattern - add or remove ARP wakeup pattern. // // Entry: Interface - Pointer to the ARPInterface // Addr - IPAddr for which we need to set ARP pattern filter. // // Returns: Nothing. // NDIS_STATUS ARPWakeupPattern(ARPInterface * Interface, IPAddr Addr, BOOLEAN AddPattern) { PNET_PM_WAKEUP_PATTERN_DESC PtrnDesc; uint PtrnLen; uint MaskLen; const uchar *PtrnMask; NDIS_STATUS Status; // // create high level proto (ARP here) pattern descriptor. // // len of pattern. PtrnLen = sizeof(ARPHeader); // adjust for Arcnet. if (Interface->ai_media == NdisMediumArcnet878_2) { PtrnLen -= ARCNET_ARPHEADER_ADJUSTMENT; PtrnMask = ARCARPPtrnMsk; } else { PtrnMask = ARPPtrnMsk; } // masklen = 1 bit per every byte of pattern. MaskLen = GetWakeupPatternMaskLength(PtrnLen); if ((PtrnDesc = CTEAllocMemN(sizeof(NET_PM_WAKEUP_PATTERN_DESC) + PtrnLen + MaskLen, 'YiCT')) != (PNET_PM_WAKEUP_PATTERN_DESC) NULL) { ARPHeader UNALIGNED *Hdr; uchar *IPAddrPtr; RtlZeroMemory(PtrnDesc, sizeof(NET_PM_WAKEUP_PATTERN_DESC) + PtrnLen + MaskLen); // set the ptrn and mask pointers in the buffer. PtrnDesc->PtrnLen = (USHORT) PtrnLen; PtrnDesc->Ptrn = (uchar *) PtrnDesc + sizeof(NET_PM_WAKEUP_PATTERN_DESC); PtrnDesc->Mask = (uchar *) PtrnDesc + sizeof(NET_PM_WAKEUP_PATTERN_DESC) + PtrnLen; // we need to wakeup on ARP request for our IPAddr. // so set the opcode and dest ip addr fields of ARP. Hdr = (ARPHeader UNALIGNED *) PtrnDesc->Ptrn; Hdr->ah_opcode = net_short(ARP_REQUEST); IPAddrPtr = Hdr->ah_shaddr + Interface->ai_addrlen + sizeof(IPAddr) + Interface->ai_addrlen; *(IPAddr UNALIGNED *) IPAddrPtr = Addr; RtlCopyMemory(PtrnDesc->Mask, PtrnMask, MaskLen); // give it to ndis. Status = DoWakeupPattern( Interface, PtrnDesc, ARP_ETYPE_ARP, AddPattern); // free the ptrn desc. CTEFreeMem(PtrnDesc); //now add wakeup pattren for directed mac address { uint PtrnBufferLen; PNDIS_PM_PACKET_PATTERN PtrnBuffer; uchar *Buffer; PtrnLen = ARP_802_ADDR_LENGTH; //eth dest address MaskLen = 1; //1 byte, needs 6 bits, 1 bit/byte PtrnBufferLen = sizeof(NDIS_PM_PACKET_PATTERN) + PtrnLen + MaskLen; Buffer = CTEAllocMem(PtrnBufferLen); if (Buffer) { RtlZeroMemory(Buffer, PtrnBufferLen); PtrnBuffer = (PNDIS_PM_PACKET_PATTERN) Buffer; PtrnBuffer->PatternSize = PtrnLen; PtrnBuffer->MaskSize = MaskLen; PtrnBuffer->PatternOffset = sizeof(NDIS_PM_PACKET_PATTERN) + 1; *(Buffer + sizeof(NDIS_PM_PACKET_PATTERN)) = 0x3F; RtlCopyMemory(Buffer + sizeof(NDIS_PM_PACKET_PATTERN) + 1, Interface->ai_addr, ARP_802_ADDR_LENGTH); Status = DoNDISRequest( Interface, NdisRequestSetInformation, AddPattern ? OID_PNP_ADD_WAKE_UP_PATTERN : OID_PNP_REMOVE_WAKE_UP_PATTERN, PtrnBuffer, PtrnBufferLen, NULL, TRUE); CTEFreeMem(Buffer); } } return Status; } return IP_NO_RESOURCES; } //** CompleteIPSetNTEAddrRequestDelayed - // // calls CompleteIPSetNTEAddrRequest on a delayed worker thread // // Entry: // Context - pointer to the control block // Exit: // None. // void CompleteIPSetNTEAddrRequestDelayed(CTEEvent * WorkerThreadEvent, PVOID Context) { AddAddrNotifyEvent *DelayedEvent; SetAddrControl *SAC; IPAddr Address; IP_STATUS Status; UNREFERENCED_PARAMETER(WorkerThreadEvent); DelayedEvent = (AddAddrNotifyEvent *) Context; SAC = DelayedEvent->SAC; // the client context block; Address = DelayedEvent->Address; // The address for which SetNTEAddr was called for. Status = DelayedEvent->Status; // Free the worker thread event. CTEFreeMem(Context); IPAddAddrComplete(Address, SAC, Status); } #if FFP_SUPPORT //* ARPReclaimRequestMem - Post processing upon request completion // // Called upon completion of NDIS requests that originate at ARP // // Input: pRequestInfo - Points to request IP sends ARP // // Returns: None // void ARPReclaimRequestMem(PVOID pRequestInfo) { // Decrement ref count, and reclaim memory if it drops to zero if (InterlockedDecrement( (PLONG) &((ReqInfoBlock *) pRequestInfo)->RequestRefs) == 0) { // TCPTRACE(("ARPReclaimRequestMem: Freeing mem at pReqInfo = %08X\n", // pRequestInfo)); CTEFreeMem(pRequestInfo); } } #endif // if FFP_SUPPORT //* ARPTimeout - ARP timeout routine. // // This is the timeout routine that is called periodically. We scan the ARP table, looking // for invalid entries that can be removed. // // Entry: Timer - Pointer to the timer that just fired. // Context - Pointer to the interface to be timed out. // // Returns: Nothing. // void ARPTimeout(CTEEvent * Timer, void *Context) { ARPInterface *Interface = (ARPInterface *) Context; // Our interface. ARPTable *Table; ARPTableEntry *Current, *Previous; int i; // Index variable. ulong Now = CTESystemUpTime(), ValidTime; CTELockHandle tblhandle; uchar Deleted; PNDIS_PACKET PList = (PNDIS_PACKET) NULL; ARPIPAddr *Addr; ARPIPAddr Marker; UNREFERENCED_PARAMETER(Timer); // Walk down the list of addresses, decrementing the age. CTEGetLock(&Interface->ai_lock, &tblhandle); if (Interface->ai_conflict && !(--Interface->ai_delay)) { ARPNotifyStruct *NotifyStruct = Interface->ai_conflict; CTEScheduleDelayedEvent(&NotifyStruct->ans_event, NotifyStruct); Interface->ai_conflict = NULL; } Addr = &Interface->ai_ipaddr; // // Marker is used to track the next addr to be processed // in the ipaddr list. Initialize it so that its aia_age is // OLD_LOCAL, and aia_addr is NULL_IP_ADDR. The places where // address list is scanned will skip ARPIPAddr with NULL_IP_ADDR // and in ARPDeleteAddr, if an addr element before the marker is // removed, Marker's aia_context will be changed to its prev element. // Marker.aia_addr = NULL_IP_ADDR; do { if (Addr->aia_age != ARPADDR_OLD_LOCAL) { IPAddr IpAddress; (Addr->aia_age)--; // // Insert Marker after this Addr // Marker's aia_context is used as blink // Marker.aia_age = ARPADDR_MARKER; Marker.aia_next = Addr->aia_next; Marker.aia_context = Addr; Addr->aia_next = &Marker; if (Addr->aia_age == ARPADDR_OLD_LOCAL) { if (Addr->aia_context != NULL) { SetAddrControl *SAC; AddAddrNotifyEvent *DelayedEvent; SAC = (SetAddrControl *) Addr->aia_context; Addr->aia_context = NULL; IpAddress = Addr->aia_addr; CTEFreeLock(&Interface->ai_lock, tblhandle); // We cannot call completion routine at timer DPC // because completion routine will need to notify // TDI clients and that could take long time. DelayedEvent = CTEAllocMemNBoot(sizeof(AddAddrNotifyEvent), 'ZiCT'); if (DelayedEvent) { DelayedEvent->SAC = SAC; DelayedEvent->Address = IpAddress; DelayedEvent->Status = IP_SUCCESS; CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed); CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent); } CTEGetLock(&Interface->ai_lock, &tblhandle); } } else { IpAddress = Addr->aia_addr; CTEFreeLock(&Interface->ai_lock, tblhandle); SendARPRequest(Interface, IpAddress, ARP_RESOLVING_GLOBAL, NULL, TRUE); CTEGetLock(&Interface->ai_lock, &tblhandle); } // // We are done scanning the list // Remove the Marker // Addr = Marker.aia_next; ((ARPIPAddr *)(Marker.aia_context))->aia_next = Marker.aia_next; } else { Addr = Addr->aia_next; } } while (Addr != NULL); CTEFreeLock(&Interface->ai_lock, tblhandle); // Loop through the ARP table for this interface, and delete stale entries. CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle); Table = Interface->ai_ARPTbl; for (i = 0; i < ARP_TABLE_SIZE; i++) { Previous = (ARPTableEntry *) ((uchar *) & ((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next)); Current = (*Table)[i]; while (Current != (ARPTableEntry *) NULL) { CTEGetLockAtDPC(&Current->ate_lock); Deleted = 0; //Delete the entry if it was used for api purpose if (Current->ate_resolveonly) { ARPControlBlock *ArpContB, *tmpArpContB; PNDIS_PACKET Packet = Current->ate_packet; ArpContB = Current->ate_resolveonly; ASSERT(Current->ate_resolveonly != NULL); while (ArpContB) { ArpRtn rtn; //Complete the pending request rtn = (ArpRtn) ArpContB->CompletionRtn; ArpContB->status = 0; tmpArpContB = ArpContB->next; (*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL); ArpContB = tmpArpContB; } Current->ate_resolveonly = NULL; if (Packet != (PNDIS_PACKET) NULL) { ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link = PList; PList = Packet; } RemoveARPTableEntry(Previous, Current); Interface->ai_count--; Deleted = 1; goto doneapi; } if (Current->ate_state == ARP_GOOD) { // // The ARP entry is valid for ARP_VALID_TIMEOUT by default. // If a cache life greater than ARP_VALID_TIMEOUT has been // configured, we'll make the entry valid for that time. // ValidTime = ArpCacheLife * ARP_TIMER_TIME; if (ValidTime < (ArpMinValidCacheLife * 1000)) { ValidTime = (ArpMinValidCacheLife * 1000); } } else { ValidTime = ARP_RESOLVE_TIMEOUT; } if (Current->ate_valid != ALWAYS_VALID && (((Now - Current->ate_valid) > ValidTime) || (Current->ate_state == ARP_GOOD && !(--(Current->ate_useticks))))) { if (Current->ate_state != ARP_RESOLVING_LOCAL) { // Really need to delete this guy. PNDIS_PACKET Packet = Current->ate_packet; if (((Now - Current->ate_valid) > ValidTime) && Current->ate_refresh) { DEBUGMSG(DBG_INFO && DBG_ARP, (DTEXT("ARPTimeout: Expiring ATE %x\n"), Current)); if (Packet != (PNDIS_PACKET) NULL) { ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link = PList; PList = Packet; } RemoveARPTableEntry(Previous, Current); Interface->ai_count--; Deleted = 1; } else { //Just try to validate this again. Current->ate_valid = Now + ARP_REFRESH_TIME; Current->ate_refresh=TRUE; } } else { IPAddr Dest = Current->ate_dest; // This entry is only resoving locally, presumably this is // token ring. We'll need to transmit a 'global' resolution // now. ASSERT(Interface->ai_media == NdisMedium802_5); Now = CTESystemUpTime(); Current->ate_valid = Now; Current->ate_state = ARP_RESOLVING_GLOBAL; CTEFreeLockFromDPC(&Current->ate_lock); CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle); // Send a global request. SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL, NULL, TRUE); CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle); // Since we've freed the locks, we need to start over from // the start of this chain. Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]), ate_next); Current = (*Table)[i]; continue; } } doneapi: // If we deleted the entry, leave the previous pointer alone, // advance the current pointer, and free the memory. Otherwise // move both pointers forward. We can free the entry lock now // because the next pointers are protected by the table lock, and // we've removed it from the list so nobody else should // find it anyway. CTEFreeLockFromDPC(&Current->ate_lock); if (Deleted) { ARPTableEntry *Temp = Current; Current = Current->ate_next; CTEFreeMem(Temp); } else { Previous = Current; Current = Current->ate_next; } } } CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle); while (PList != (PNDIS_PACKET) NULL) { PNDIS_PACKET Packet = PList; PList = ((PacketContext *) Packet->ProtocolReserved)->pc_common.pc_link; IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); } // // Dont requeue if interface is going down and we need to stop the timer // if (Interface->ai_stoptimer) { // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARP interface %lx is down - dont requeue the timer - signal the waiter\n", Interface)); Interface->ai_timerstarted = FALSE; CTESignal(&Interface->ai_timerblock, NDIS_STATUS_SUCCESS); } else { CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface); } #if FFP_SUPPORT // Flush Processing - This can be done after starting the timer CTEGetLock(&Interface->ai_lock, &tblhandle); // If FFP supported on this interface & it is time to do a flush if ((Interface->ai_ffpversion) && (++Interface->ai_ffplastflush >= FFP_ARP_FLUSH_INTERVAL)) { ReqInfoBlock *pRequestInfo; FFPFlushParams *pFlushInfo; TCPTRACE(("ARPTimeout: Sending a FFP flush to ARPInterface %08X\n", Interface)); // Allocate the request block - For General and Request Specific Parts pRequestInfo = CTEAllocMemN(sizeof(ReqInfoBlock) + sizeof(FFPFlushParams), '0ICT'); // TCPTRACE(("ARPTimeout: Allocated mem at pReqInfo = %08X\n", // pRequestInfo)); if (pRequestInfo != NULL) { // Prepare the params for the request [Part common to all requests] pRequestInfo->RequestType = OID_FFP_FLUSH; pRequestInfo->ReqCompleteCallback = ARPReclaimRequestMem; // Prepare the params for the request [Part specific to this request] pRequestInfo->RequestLength = sizeof(FFPFlushParams); // Flush all caches that FFP keeps - just a safe reset of FFP state pFlushInfo = (FFPFlushParams *) pRequestInfo->RequestInfo; pFlushInfo->NdisProtocolType = NDIS_PROTOCOL_ID_TCP_IP; // Assign the ref count to 1 => Used for just a single request pRequestInfo->RequestRefs = 1; DoNDISRequest(Interface, NdisRequestSetInformation, OID_FFP_FLUSH, pFlushInfo, sizeof(FFPFlushParams), NULL, FALSE); // Reset the number of timer ticks since the last FFP request Interface->ai_ffplastflush = 0; } else { TCPTRACE(("Error: Unable to allocate memory for NdisRequest\n")); } } #if DBG if (fakereset) { NDIS_STATUS Status; NdisReset(&Status, Interface->ai_handle); KdPrint(("fakereset: %x\n", Status)); } #endif CTEFreeLock(&Interface->ai_lock, tblhandle); #endif // if FFP_SUPPORT } //* IsLocalAddr - Return info. about local status of address. // // Called when we need info. about whether or not a particular address is // local. We return info about whether or not it is, and if it is how old // it is. // // Entry: Interface - Pointer to interface structure to be searched. // Address - Address in question. // // Returns: ARPADDR_*, for how old it is. // // uint IsLocalAddr(ARPInterface * Interface, IPAddr Address) { CTELockHandle Handle; ARPIPAddr *CurrentAddr; uint Age; // If we are asking about the null ip address, we don't want to consider // it as a true local address. // if (IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) { return ARPADDR_NOT_LOCAL; } CTEGetLock(&Interface->ai_lock, &Handle); CurrentAddr = &Interface->ai_ipaddr; Age = ARPADDR_NOT_LOCAL; do { if (CurrentAddr->aia_addr == Address) { Age = CurrentAddr->aia_age; break; } CurrentAddr = CurrentAddr->aia_next; } while (CurrentAddr != NULL); CTEFreeLock(&Interface->ai_lock, Handle); return Age; } //* ARPLocalAddr - Determine whether or not a given address if local. // // This routine is called when we receive an incoming packet and need to // determine whether or not it's local. We look up the provided address on // the specified interface. // // Entry: Interface - Pointer to interface structure to be searched. // Address - Address in question. // // Returns: TRUE if it is a local address, FALSE if it's not. // uchar ARPLocalAddr(ARPInterface * Interface, IPAddr Address) { CTELockHandle Handle; ARPPArpAddr *CurrentPArp; IPMask Mask, NetMask; IPAddr MatchAddress; // First, see if he's a local (not-proxy) address. if (IsLocalAddr(Interface, Address) != ARPADDR_NOT_LOCAL) return TRUE; CTEGetLock(&Interface->ai_lock, &Handle); // Didn't find him in out local address list. See if he exists on our // proxy ARP list. for (CurrentPArp = Interface->ai_parpaddr; CurrentPArp != NULL; CurrentPArp = CurrentPArp->apa_next) { // See if this guy matches. Mask = CurrentPArp->apa_mask; MatchAddress = Address & Mask; if (IP_ADDR_EQUAL(CurrentPArp->apa_addr, MatchAddress)) { // He matches. We need to make a few more checks to make sure // we don't reply to a broadcast address. if (Mask == HOST_MASK) { // We're matching the whole address, so it's OK. CTEFreeLock(&Interface->ai_lock, Handle); return TRUE; } // See if the non-mask part it all-zeros. Since the mask presumably // covers a subnet, this trick will prevent us from replying to // a zero host part. if (IP_ADDR_EQUAL(MatchAddress, Address)) continue; // See if the host part is all ones. if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~Mask))) continue; // If the mask we were given is not the net mask for this address, // we'll need to repeat the above checks. NetMask = IPNetMask(Address); if (NetMask != Mask) { MatchAddress = Address & NetMask; if (IP_ADDR_EQUAL(MatchAddress, Address)) continue; if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~NetMask))) continue; } // If we get to this point we've passed all the tests, so it's // local. CTEFreeLock(&Interface->ai_lock, Handle); return TRUE; } } CTEFreeLock(&Interface->ai_lock, Handle); return FALSE; } //* NotifyConflictProc - Notify the user of an address conflict. // // Called when we need to notify the user of an address conflict. The // exact mechanism is system dependent, but generally involves a popup. // // Input: Event - Event that fired. // Context - Pointer to ARPNotifyStructure. // // Returns: Nothing. // void NotifyConflictProc(CTEEvent * Event, void *Context) { #if MILLEN // // Call into VIP to VIP_NotifyConflicProc. This will schedule an Appy // event, etc. This is a little sleazy, but we do an INT 20, give the // appropriate index into service table and VIP VxD ID. // // void VIP_NotifyConflictProc(CTEEvent *Event, void *Context); // Event is unused. // _asm { push Context push Context _emit 0xcd _emit 0x20 _emit 0x15 // VIP_NotifyConflictProc (Low) _emit 0x00 // VIP_NotifyConflictProc (High) _emit 0x89 // VIP VxD ID (Low) _emit 0x04 // VIP VxD ID (High) add esp,8 } #else // MILLEN ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *) Context; PWCHAR stringList[2]; uchar IPAddrBuffer[(sizeof(IPAddr) * 4)]; uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)]; WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; uint i; uint IPAddrCharCount; UNICODE_STRING unicodeString; ANSI_STRING ansiString; PAGED_CODE(); UNREFERENCED_PARAMETER(Event); // // Convert the IP address into a string. // IPAddrCharCount = 0; for (i = 0; i < sizeof(IPAddr); i++) { uint CurrentByte; CurrentByte = NotifyStruct->ans_addr & 0xff; if (CurrentByte > 99) { IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 100) + '0'; CurrentByte %= 100; IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0'; CurrentByte %= 10; } else if (CurrentByte > 9) { IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0'; CurrentByte %= 10; } IPAddrBuffer[IPAddrCharCount++] = (uchar)CurrentByte + '0'; if (i != (sizeof(IPAddr) - 1)) IPAddrBuffer[IPAddrCharCount++] = '.'; NotifyStruct->ans_addr >>= 8; } // // Convert the hardware address into a string. // for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) { uchar CurrentHalf; CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4; HWAddrBuffer[i * 3] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' : (CurrentHalf - 10) + 'A'); CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f; HWAddrBuffer[(i * 3) + 1] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' : (CurrentHalf - 10) + 'A'); if (i != (NotifyStruct->ans_hwaddrlen - 1)) HWAddrBuffer[(i * 3) + 2] = ':'; } // // Unicode the strings. // *unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL; unicodeString.Buffer = unicodeIPAddrBuffer; unicodeString.Length = 0; unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1); ansiString.Buffer = (PCHAR) IPAddrBuffer; ansiString.Length = (USHORT) IPAddrCharCount; ansiString.MaximumLength = (USHORT) IPAddrCharCount; RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, FALSE ); stringList[0] = unicodeIPAddrBuffer; unicodeString.Buffer = unicodeHWAddrBuffer; unicodeString.Length = 0; unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3); ansiString.Buffer = (PCHAR) HWAddrBuffer; ansiString.Length = (ushort) ((NotifyStruct->ans_hwaddrlen * 3) - 1); ansiString.MaximumLength = (ushort)(NotifyStruct->ans_hwaddrlen * 3); RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, FALSE ); stringList[1] = unicodeHWAddrBuffer; // // Kick off a popup and log an event. // if (NotifyStruct->ans_shutoff) { CTELogEvent( IPDriverObject, EVENT_TCPIP_ADDRESS_CONFLICT1, 0, 2, stringList, 0, NULL ); IoRaiseInformationalHardError( STATUS_IP_ADDRESS_CONFLICT1, NULL, NULL ); } else { CTELogEvent( IPDriverObject, EVENT_TCPIP_ADDRESS_CONFLICT2, 0, 2, stringList, 0, NULL ); IoRaiseInformationalHardError( STATUS_IP_ADDRESS_CONFLICT2, NULL, NULL ); } CTEFreeMem(NotifyStruct); #endif // !MILLEN return; } //* DebugConflictProc - Prints some debugging info in case of addr conflicts // Prints the ip and hw addr of the guy causing the conflict // Context - Pointer to ARPNotifyStructure. // // Returns: Nothing. // void DebugConflictProc(void *Context) { ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *) Context; uchar IPAddrBuffer[(sizeof(IPAddr) * 4)]; uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; uint i; uint IPAddrCharCount; IPAddr ans_addr; // // Save the IP address in case we need it later, then convert into // a string. // ans_addr = NotifyStruct->ans_addr; IPAddrCharCount = 0; for (i = 0; i < sizeof(IPAddr); i++) { uint CurrentByte; CurrentByte = NotifyStruct->ans_addr & 0xff; if (CurrentByte > 99) { IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 100) + '0'; CurrentByte %= 100; IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0'; CurrentByte %= 10; } else if (CurrentByte > 9) { IPAddrBuffer[IPAddrCharCount++] = (uchar)(CurrentByte / 10) + '0'; CurrentByte %= 10; } IPAddrBuffer[IPAddrCharCount++] = (uchar) (CurrentByte) + '0'; if (i != (sizeof(IPAddr) - 1)) IPAddrBuffer[IPAddrCharCount++] = '.'; NotifyStruct->ans_addr >>= 8; } IPAddrBuffer[IPAddrCharCount] = '\0'; // // Convert the hardware address into a string. // for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) { uchar CurrentHalf; CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4; HWAddrBuffer[i * 3] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' : (CurrentHalf - 10) + 'A'); CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f; HWAddrBuffer[(i * 3) + 1] = (uchar) (CurrentHalf < 10 ? CurrentHalf + '0' : (CurrentHalf - 10) + 'A'); if (i != (NotifyStruct->ans_hwaddrlen - 1)) HWAddrBuffer[(i * 3) + 2] = ':'; } HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0'; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "TCPIP: Address Conflict: IPAddr %s HWAddr %s \n", IPAddrBuffer, HWAddrBuffer)); return; } //* HandleARPPacket - Process an incoming ARP packet. // // This is the main routine to process an incoming ARP packet. We look at // all ARP frames, and update our cache entry for the source address if one // exists. Else, if we are the target we create an entry if one doesn't // exist. Finally, we'll handle the opcode, responding if this is a request // or sending pending packets if this is a response. // // Entry: Interface - Pointer to interface structure for this adapter. // Header - Pointer to header buffer. // HeaderSize - Size of header buffer. // ARPHdr - ARP packet header. // ARPHdrSize - Size of ARP header. // ProtOffset - Offset into original data field of arp header. // Will be non-zero if we're using SNAP. // // Returns: An NDIS_STATUS value to be returned to the NDIS driver. // NDIS_STATUS HandleARPPacket(ARPInterface * Interface, void *Header, uint HeaderSize, ARPHeader UNALIGNED * ARPHdr, uint ARPHdrSize, uint ProtOffset) { ARPTableEntry *Entry; // Entry in ARP table CTELockHandle LHandle = DISPATCH_LEVEL, TableHandle; RC UNALIGNED *SourceRoute = (RC UNALIGNED *) NULL; // Pointer to Source Route info, if any. uint SourceRouteSize = 0; ulong Now = CTESystemUpTime(); uchar LocalAddr; uint LocalAddrAge; uchar *SHAddr, *DHAddr; IPAddr UNALIGNED *SPAddr, *DPAddr; ENetHeader *ENetHdr; TRHeader *TRHdr; FDDIHeader *FHdr; ARCNetHeader *AHdr; ushort MaxMTU; uint UseSNAP; SetAddrControl *SAC=NULL; ARPIPAddr *CurrentAddr; AddAddrNotifyEvent *DelayedEvent; uint NUCast; DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_RX, (DTEXT("+HandleARPPacket(%x, %x, %d, %x, %d, %d)\n"), Interface, Header, HeaderSize, ARPHdr, ARPHdrSize, ProtOffset)); // Validate the opcode // if ((ARPHdr->ah_opcode != net_short(ARP_REQUEST)) && (ARPHdr->ah_opcode != net_short(ARP_RESPONSE))) { return NDIS_STATUS_NOT_RECOGNIZED; } // We examine all ARP frames. If we find the source address in the ARP table, we'll // update the hardware address and set the state to valid. If we're the // target and he's not in the table, we'll add him. Otherwise if we're the // target and this is a response we'll send any pending packets to him. if (Interface->ai_media != NdisMediumArcnet878_2) { if (ARPHdrSize < sizeof(ARPHeader)) return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small. if (ARPHdr->ah_hw != net_short(ARP_HW_ENET) && ARPHdr->ah_hw != net_short(ARP_HW_802)) return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type if (ARPHdr->ah_hlen != ARP_802_ADDR_LENGTH) return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length. if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize == 0) UseSNAP = FALSE; else UseSNAP = (ProtOffset != 0); // Figure out SR size on TR. if (Interface->ai_media == NdisMedium802_5) { // Check for source route information. SR is present if the header // size is greater than the standard TR header size. If the SR is // only an RC field, we ignore it because it came from the same // ring which is the same as no SR. if ((HeaderSize - sizeof(TRHeader)) > sizeof(RC)) { SourceRouteSize = HeaderSize - sizeof(TRHeader); SourceRoute = (RC UNALIGNED *) ((uchar *) Header + sizeof(TRHeader)); } } SHAddr = ARPHdr->ah_shaddr; SPAddr = (IPAddr UNALIGNED *) & ARPHdr->ah_spaddr; DHAddr = ARPHdr->ah_dhaddr; DPAddr = (IPAddr UNALIGNED *) & ARPHdr->ah_dpaddr; } else { if (ARPHdrSize < (sizeof(ARPHeader) - ARCNET_ARPHEADER_ADJUSTMENT)) return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small. if (ARPHdr->ah_hw != net_short(ARP_HW_ARCNET)) return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type if (ARPHdr->ah_hlen != 1) return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length. UseSNAP = FALSE; SHAddr = ARPHdr->ah_shaddr; SPAddr = (IPAddr UNALIGNED *) (SHAddr + 1); DHAddr = (uchar *) SPAddr + sizeof(IPAddr); DPAddr = (IPAddr UNALIGNED *) (DHAddr + 1); } if (ARPHdr->ah_pro != net_short(ARP_ETYPE_IP)) return NDIS_STATUS_NOT_RECOGNIZED; // Unsupported protocol type. if (ARPHdr->ah_plen != sizeof(IPAddr)) return NDIS_STATUS_NOT_RECOGNIZED; LocalAddrAge = ARPADDR_NOT_LOCAL; // First, let's see if we have an address conflict. // LocalAddrAge = IsLocalAddr(Interface, *SPAddr); if (LocalAddrAge != ARPADDR_NOT_LOCAL) { // The source IP address is one of ours. See if the source h/w address // is ours also. if (ARPHdr->ah_hlen != Interface->ai_addrlen || CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) { uint Shutoff = FALSE; BOOLEAN PopUP = TRUE; ARPNotifyStruct *NotifyStruct; // This isn't from us; we must have an address conflict somewhere. // We always log an error about this. If what triggered this is a // response and the address in conflict is young, we'll turn off // the interface. if (LocalAddrAge != ARPADDR_OLD_LOCAL && ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) { // Send an arp request with the owner's address to reset the // caches. CTEGetLock(&Interface->ai_lock, &LHandle); // now find the address that is in conflict and get the // corresponding client context. CurrentAddr = &Interface->ai_ipaddr; do { if (CurrentAddr->aia_addr == *SPAddr) { SAC = (SetAddrControl *) CurrentAddr->aia_context; CurrentAddr->aia_context = NULL; break; } CurrentAddr = CurrentAddr->aia_next; } while (CurrentAddr != NULL); CTEFreeLock(&Interface->ai_lock, LHandle); SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL, SHAddr, FALSE); // Send a request. Shutoff = TRUE; // Display the debug information for remote boot/install. // This code should be kept. { ARPNotifyStruct *DebugNotifyStruct; DebugNotifyStruct = CTEAllocMemN(offsetof(ARPNotifyStruct, ans_hwaddr) + ARPHdr->ah_hlen, '1ICT'); if (DebugNotifyStruct != NULL) { DebugNotifyStruct->ans_addr = *SPAddr; DebugNotifyStruct->ans_shutoff = Shutoff; DebugNotifyStruct->ans_hwaddrlen = (uint) ARPHdr->ah_hlen; RtlCopyMemory(DebugNotifyStruct->ans_hwaddr, SHAddr, ARPHdr->ah_hlen); DebugConflictProc(DebugNotifyStruct); CTEFreeMem(DebugNotifyStruct); } } if ((SAC != NULL) && !SAC->StaticAddr) { // This is a dhcp adapter. // Don't display a warning dialog in this case - DHCP will // alert the user // PopUP = FALSE; } // We cannot call completion routine at this time // because completion routine calls back into arp to // reset the address and that may go down into ndis. DelayedEvent = CTEAllocMemN(sizeof(AddAddrNotifyEvent), '2ICT'); if (DelayedEvent) { DelayedEvent->SAC = SAC; DelayedEvent->Address = *SPAddr; DelayedEvent->Status = IP_DUPLICATE_ADDRESS; CTEInitEvent(&DelayedEvent->Event, CompleteIPSetNTEAddrRequestDelayed); CTEScheduleDelayedEvent(&DelayedEvent->Event, DelayedEvent); } else { ASSERT(FALSE); } if (!PopUP) { goto no_dialog; } } else { if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) && (IsLocalAddr(Interface, *DPAddr) == ARPADDR_OLD_LOCAL)) { // Send a response for gratuitous ARP. SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr, SourceRoute, SourceRouteSize, UseSNAP); Shutoff = FALSE; } else if (LocalAddrAge != ARPADDR_OLD_LOCAL) { // our address is still young. we dont need to put the // warning popup as it will be done by the code that // checks for arp response in above if portion of the code. goto no_dialog; } // Else. We have an old local address and received an ARP for // a third address. Fall through and indicate address // conflict. } // Now allocate a structure, and schedule an event to notify // the user. NotifyStruct = CTEAllocMemN(offsetof(ARPNotifyStruct, ans_hwaddr) + ARPHdr->ah_hlen, '3ICT'); if (NotifyStruct != NULL) { NotifyStruct->ans_addr = *SPAddr; NotifyStruct->ans_shutoff = Shutoff; NotifyStruct->ans_hwaddrlen = (uint) ARPHdr->ah_hlen; RtlCopyMemory(NotifyStruct->ans_hwaddr, SHAddr, ARPHdr->ah_hlen); CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc); if (Shutoff) { // Delay notification for few seconds. Interface->ai_conflict = NotifyStruct; #if MILLEN Interface->ai_delay = 5; #else Interface->ai_delay = 90; // delay 3 seconds. #endif } else CTEScheduleDelayedEvent(&NotifyStruct->ans_event, NotifyStruct); } no_dialog: ; } return NDIS_STATUS_NOT_RECOGNIZED; } if (!EnableBcastArpReply) { // Check for bogus arp entry NUCast = ((*(SHAddr) & Interface->ai_bcastmask) == Interface->ai_bcastval) ? AI_NONUCAST_INDEX : AI_UCAST_INDEX; if (NUCast == AI_NONUCAST_INDEX) { return NDIS_STATUS_NOT_RECOGNIZED; } } CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle); MaxMTU = Interface->ai_mtu; LocalAddr = ARPLocalAddr(Interface, *DPAddr); // If the sender's address is not remote (i.e. multicast, broadcast, // local, or just invalid), We don't want to create an entry for it or // bother looking it up. // if ((DEST_REMOTE == GetAddrType(*SPAddr))) { Entry = ARPLookup(Interface, *SPAddr); if (Entry == (ARPTableEntry *) NULL) { // Didn't find him, create one if it's for us. The call to ARPLookup // returned with the ARPTblLock held, so we need to free it. CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); if (LocalAddr) { // If this was an ARP request, we need to create a new // entry for the source info. If this was a reply, it was // unsolicited and we don't create an entry. // if (ARPHdr->ah_opcode != net_short(ARP_RESPONSE)) { Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle, 0); } } else { return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us. } } else { //if this is for userarp, make sure that it is out of the table //while we still have the arp table lock. if (Entry->ate_userarp) { ARPTable *Table; ARPTableEntry *PrevATE, *CurrentATE; uint Index = ARP_HASH(*SPAddr); Table = Interface->ai_ARPTbl; PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next); CurrentATE = PrevATE; while (CurrentATE != (ARPTableEntry *) NULL) { if (CurrentATE == Entry) { break; } PrevATE = CurrentATE; CurrentATE = CurrentATE->ate_next; } if (CurrentATE != NULL) { RemoveARPTableEntry(PrevATE, CurrentATE); Interface->ai_count--; } } CTEFreeLockFromDPC(&Interface->ai_ARPTblLock); LHandle = TableHandle; } } else { // Source address was invalid for an Arp table entry. CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); Entry = NULL; } // At this point, entry should be valid and we hold the lock on the entry // in LHandle or entry is NULL. if (Entry != (ARPTableEntry *) NULL) { PNDIS_PACKET Packet; // Packet to be sent. DEBUGMSG(DBG_INFO && DBG_ARP && DBG_RX, (DTEXT("HandleARPPacket: resolving addr for ATE %x\n"), Entry)); Entry->ate_refresh = FALSE; // If the entry is already static, we'll want to leave it as static. if (Entry->ate_valid != ALWAYS_VALID) { // OK, we have an entry to use, and hold the lock on it. Fill in the // required fields. switch (Interface->ai_media) { case NdisMedium802_3: // This is an Ethernet. ENetHdr = (ENetHeader *) Entry->ate_addr; RtlCopyMemory(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH); RtlCopyMemory(ENetHdr->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); ENetHdr->eh_type = net_short(ARP_ETYPE_IP); // If we're using SNAP on this entry, copy in the SNAP header. if (UseSNAP) { RtlCopyMemory(&Entry->ate_addr[sizeof(ENetHeader)], ARPSNAP, sizeof(SNAPHeader)); Entry->ate_addrlength = (uchar) (sizeof(ENetHeader) + sizeof(SNAPHeader)); *(ushort UNALIGNED *) & Entry->ate_addr[Entry->ate_addrlength - 2] = net_short(ARP_ETYPE_IP); } else Entry->ate_addrlength = sizeof(ENetHeader); Entry->ate_state = ARP_GOOD; Entry->ate_valid = Now; // Mark last time he was // valid. Entry->ate_useticks = ArpCacheLife; break; case NdisMedium802_5: // This is TR. // For token ring we have to deal with source routing. There's // a special case to handle multiple responses for an all-routes // request - if the entry is currently good and we knew it was // valid recently, we won't update the entry. if (Entry->ate_state != ARP_GOOD || (Now - Entry->ate_valid) > ARP_RESOLVE_TIMEOUT) { TRHdr = (TRHeader *) Entry->ate_addr; // We need to update a TR entry. TRHdr->tr_ac = ARP_AC; TRHdr->tr_fc = ARP_FC; RtlCopyMemory(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH); RtlCopyMemory(TRHdr->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); if (SourceRoute != (RC UNALIGNED *) NULL) { uchar MaxIFieldBits; // We have source routing information. RtlCopyMemory(&Entry->ate_addr[sizeof(TRHeader)], SourceRoute, SourceRouteSize); MaxIFieldBits = (SourceRoute->rc_dlf & RC_LF_MASK) >> LF_BIT_SHIFT; MaxIFieldBits = MIN(MaxIFieldBits, MAX_LF_BITS); MaxMTU = IFieldSize[MaxIFieldBits]; // The new MTU we've computed is the max I-field size, // which doesn't include source routing info but // does include SNAP info. Subtract off the SNAP size. MaxMTU -= sizeof(SNAPHeader); TRHdr->tr_saddr[0] |= TR_RII; (*(RC UNALIGNED *) & Entry->ate_addr[sizeof(TRHeader)]).rc_dlf ^= RC_DIR; // Make sure it's non-broadcast. (*(RC UNALIGNED *) & Entry->ate_addr[sizeof(TRHeader)]).rc_blen &= RC_LENMASK; } RtlCopyMemory(&Entry->ate_addr[sizeof(TRHeader) + SourceRouteSize], ARPSNAP, sizeof(SNAPHeader)); Entry->ate_state = ARP_GOOD; Entry->ate_valid = Now; Entry->ate_useticks = ArpCacheLife; Entry->ate_addrlength = (uchar) (sizeof(TRHeader) + SourceRouteSize + sizeof(SNAPHeader)); *(ushort *) & Entry->ate_addr[Entry->ate_addrlength - 2] = net_short(ARP_ETYPE_IP); } break; case NdisMediumFddi: FHdr = (FDDIHeader *) Entry->ate_addr; FHdr->fh_pri = ARP_FDDI_PRI; RtlCopyMemory(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH); RtlCopyMemory(FHdr->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); RtlCopyMemory(&Entry->ate_addr[sizeof(FDDIHeader)], ARPSNAP, sizeof(SNAPHeader)); Entry->ate_addrlength = (uchar) (sizeof(FDDIHeader) + sizeof(SNAPHeader)); *(ushort UNALIGNED *) & Entry->ate_addr[Entry->ate_addrlength - 2] = net_short(ARP_ETYPE_IP); Entry->ate_state = ARP_GOOD; Entry->ate_valid = Now; // Mark last time he was // valid. Entry->ate_useticks = ArpCacheLife; break; case NdisMediumArcnet878_2: AHdr = (ARCNetHeader *) Entry->ate_addr; AHdr->ah_saddr = Interface->ai_addr[0]; AHdr->ah_daddr = *SHAddr; AHdr->ah_prot = ARP_ARCPROT_IP; Entry->ate_addrlength = sizeof(ARCNetHeader); Entry->ate_state = ARP_GOOD; Entry->ate_valid = Now; // Mark last time he was // valid. break; default: ASSERT(0); break; } } if (Entry->ate_resolveonly) { ARPControlBlock *ArpContB, *TmpArpContB; ArpContB = Entry->ate_resolveonly; ASSERT(Entry->ate_resolveonly != NULL); while (ArpContB) { ArpRtn rtn; rtn = (ArpRtn) ArpContB->CompletionRtn; ArpContB->status = FillARPControlBlock(Interface, Entry, ArpContB); TmpArpContB = ArpContB->next; (*rtn) (ArpContB, STATUS_SUCCESS); ArpContB = TmpArpContB; } Entry->ate_resolveonly = NULL; if (Entry->ate_userarp) { PNDIS_PACKET OldPacket = NULL; OldPacket = Entry->ate_packet; CTEFreeLock(&Entry->ate_lock, LHandle); CTEFreeMem(Entry); if (OldPacket) { IPSendComplete(Interface->ai_context, OldPacket, NDIS_STATUS_SUCCESS); } } else { CTEFreeLock(&Entry->ate_lock, LHandle); } return NDIS_STATUS_SUCCESS; } // At this point we've updated the entry, and we still hold the lock // on it. If we have a packet that was pending to be sent, send it now. // Otherwise just free the lock. Packet = Entry->ate_packet; if (Packet != NULL) { // We have a packet to send. ASSERT(Entry->ate_state == ARP_GOOD); Entry->ate_packet = NULL; DEBUGMSG(DBG_INFO && DBG_ARP && DBG_TX, (DTEXT("ARPHandlePacket: Sending packet %x after resolving ATE %x\n"), Packet, Entry)); if (ARPSendData(Interface, Packet, Entry, LHandle) != NDIS_STATUS_PENDING) { IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); } } else { CTEFreeLock(&Entry->ate_lock, LHandle); } } // See if the MTU is less than our local one. This should only happen // in the case of token ring source routing. if (MaxMTU < Interface->ai_mtu) { LLIPAddrMTUChange LAM; LAM.lam_mtu = MaxMTU; LAM.lam_addr = *SPAddr; // It is less. Notify IP. ASSERT(Interface->ai_media == NdisMedium802_5); IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE, &LAM, sizeof(LLIPAddrMTUChange), NULL); } // At this point we've updated the entry (if we had one), and we've freed // all locks. If it's for a local address and it's a request, reply to // it. if (LocalAddr) { // It's for us. if (ARPHdr->ah_opcode == net_short(ARP_REQUEST)) { // It's a request, and we need to respond. SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr, SourceRoute, SourceRouteSize, UseSNAP); } } return NDIS_STATUS_SUCCESS; } //* InitAdapter - Initialize an adapter. // // Called when an adapter is open to finish initialization. We set // up our lookahead size and packet filter, and we're ready to go. // // Entry: // adapter - Pointer to an adapter structure for the adapter to be // initialized. // // Exit: Nothing // void InitAdapter(ARPInterface * Adapter) { NDIS_STATUS Status; CTELockHandle Handle; ARPIPAddr *Addr, *OldAddr; if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation, OID_GEN_CURRENT_LOOKAHEAD, &ARPLookahead, sizeof(ARPLookahead), NULL, TRUE)) != NDIS_STATUS_SUCCESS) { Adapter->ai_operstatus = INTERFACE_UNINIT; return; } if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint), NULL, TRUE)) == NDIS_STATUS_SUCCESS) { uint MediaStatus; Adapter->ai_adminstate = IF_STATUS_UP; Adapter->ai_mediastatus = TRUE; if ((Status = DoNDISRequest(Adapter, NdisRequestQueryInformation, OID_GEN_MEDIA_CONNECT_STATUS, &MediaStatus, sizeof(MediaStatus), NULL, TRUE)) == NDIS_STATUS_SUCCESS) { if (MediaStatus == NdisMediaStateDisconnected) { Adapter->ai_mediastatus = FALSE; } } ARPUpdateOperStatus(Adapter); // Now walk through any addresses we have and ARP for them , only when ArpRetryCount != 0. if (ArpRetryCount) { CTEGetLock(&Adapter->ai_lock, &Handle); OldAddr = NULL; Addr = &Adapter->ai_ipaddr; do { if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { IPAddr Address = Addr->aia_addr; Addr->aia_age = ArpRetryCount; CTEFreeLock(&Adapter->ai_lock, Handle); OldAddr = Addr; SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL, NULL, TRUE); CTEGetLock(&Adapter->ai_lock, &Handle); Addr = &Adapter->ai_ipaddr; while (Addr != OldAddr && Addr != NULL) { Addr = Addr->aia_next; } if (Addr != NULL) { Addr = Addr->aia_next; } } else { Addr = Addr->aia_next; } } while (Addr != NULL); CTEFreeLock(&Adapter->ai_lock, Handle); } } else { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_ERROR_LEVEL, "**InitAdapter setting FAILED\n")); Adapter->ai_operstatus = INTERFACE_UNINIT; } } //** ARPOAComplete - ARP Open adapter complete handler. // // This routine is called by the NDIS driver when an open adapter // call completes. Presumably somebody is blocked waiting for this, so // we'll wake him up now. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Status - Final status of command. // ErrorStatus - Final error status. // // Exit: Nothing. // void NDIS_API ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus) { ARPInterface *ai = (ARPInterface *) Handle; // For compiler. UNREFERENCED_PARAMETER(ErrorStatus); CTESignal(&ai->ai_block, (uint) Status); // Wake him up, and return status. } //** ARPCAComplete - ARP close adapter complete handler. // // This routine is called by the NDIS driver when a close adapter // call completes. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Status - Final status of command. // // Exit: Nothing. // void NDIS_API ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status) { ARPInterface *ai = (ARPInterface *) Handle; // For compiler. CTESignal(&ai->ai_block, (uint) Status); // Wake him up, and return status. } //** ARPSendComplete - ARP send complete handler. // // This routine is called by the NDIS driver when a send completes. // This is a pretty time critical operation, we need to get through here // quickly. We'll strip our buffer off and put it back, and call the upper // later send complete handler. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Packet - A pointer to the packet that was sent. // Status - Final status of command. // // Exit: Nothing. // void NDIS_API ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status) { ARPInterface *Interface = (ARPInterface *) Handle; PacketContext *PC = (PacketContext *) Packet->ProtocolReserved; PNDIS_BUFFER Buffer; uint DataLength; ulong Proc; Proc = KeGetCurrentProcessorNumber(); Interface->ai_qlen[Proc].ai_qlen--; if (Status == NDIS_STATUS_SUCCESS) { DataLength = Packet->Private.TotalLength; if (!(Packet->Private.ValidCounts)) { NdisQueryPacket(Packet, NULL, NULL, NULL,&DataLength); } Interface->ai_outoctets += DataLength; } else { if (Status == NDIS_STATUS_RESOURCES) Interface->ai_outdiscards++; else Interface->ai_outerrors++; } #if BACK_FILL // Get first buffer on packet. if (Interface->ai_media == NdisMedium802_3) { PMDL TmpMdl = NULL; uint HdrSize; NdisQueryPacket(Packet, NULL, NULL, &TmpMdl, NULL); if (TmpMdl->MdlFlags & MDL_NETWORK_HEADER) { HdrSize = sizeof(ENetHeader); if (((PacketContext*) Packet->ProtocolReserved)->pc_common.pc_flags & PACKET_FLAG_SNAP) HdrSize += Interface->ai_snapsize; TmpMdl->MappedSystemVa = (PVOID) (((ULONG_PTR) TmpMdl->MappedSystemVa) + HdrSize); TmpMdl->ByteOffset += HdrSize; TmpMdl->ByteCount -= HdrSize; } else { NdisUnchainBufferAtFront(Packet, &Buffer); FreeARPBuffer(Interface, Buffer); // Free it up. } } else { NdisUnchainBufferAtFront(Packet, &Buffer); FreeARPBuffer(Interface, Buffer); // Free it up. } #else // Get first buffer on packet. NdisUnchainBufferAtFront(Packet, &Buffer); ASSERT(Buffer); FreeARPBuffer(Interface, Buffer); // Free it up. #endif if (PC->pc_common.pc_owner != PACKET_OWNER_LINK) { // We don't own this one. IPSendComplete(Interface->ai_context, Packet, Status); return; } // This packet belongs to us, so free it. NdisFreePacket(Packet); } //** ARPTDComplete - ARP transfer data complete handler. // // This routine is called by the NDIS driver when a transfer data // call completes. Since we never transfer data ourselves, this must be // from the upper layer. We'll just call his routine and let him deal // with it. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Packet - A pointer to the packet used for the TD. // Status - Final status of command. // BytesCopied - Count of bytes copied. // // Exit: Nothing. // void NDIS_API ARPTDComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status, uint BytesCopied) { ARPInterface *ai = (ARPInterface *) Handle; IPTDComplete(ai->ai_context, Packet, Status, BytesCopied); } //** ARPResetComplete - ARP reset complete handler. // // This routine is called by the NDIS driver when a reset completes. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Status - Final status of command. // // Exit: Nothing. // void NDIS_API ARPResetComplete(NDIS_HANDLE Handle, NDIS_STATUS Status) { ARPInterface *ai = (ARPInterface *) Handle; UNREFERENCED_PARAMETER(Status); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ArpResetComplete on %x\n", ai->ai_context)); IPReset(ai->ai_context); } //** ARPRequestComplete - ARP request complete handler. // // This routine is called by the NDIS driver when a general request // completes. If ARP blocks on a request, we'll just give a wake up // to whoever's blocked on this request. Else if it is a non-blocking // request, we extract the request complete callback fn in the request // call it, and then deallocate the request block (that is on the heap) // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Request - A pointer to the request that completed. // Status - Final status of command. // // Exit: Nothing. // void NDIS_API ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST pRequest, NDIS_STATUS Status) { RequestBlock *rb = STRUCT_OF(RequestBlock, pRequest, Request); DBG_UNREFERENCED_PARAMETER(Handle); DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST, (DTEXT("+ARPRequestComplete(%x, %x, %x) RequestBlock %x\n"), Handle, pRequest, Status, rb)); if (rb->Blocking) { // Request through BLOCKING DoNDISRequest // Signal the blocked guy here CTESignal(&rb->Block, (uint) Status); if (InterlockedDecrement( (PLONG) &rb->RefCount) == 0) { CTEFreeMem(rb); } } else { ReqInfoBlock *rib; RCCALL reqcallback; // Request through NON-BLOCKING DoNDISRequest // Extract the callback fn pointer & params if (pRequest->RequestType == NdisRequestSetInformation) rib = STRUCT_OF(ReqInfoBlock, pRequest->DATA.SET_INFORMATION.InformationBuffer, RequestInfo); else rib = STRUCT_OF(ReqInfoBlock, pRequest->DATA.QUERY_INFORMATION.InformationBuffer, RequestInfo); reqcallback = rib->ReqCompleteCallback; if (reqcallback) reqcallback(rib); // Free ARP memory associated with request KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPRequestComplete: Freeing mem at pRequest = %08X\n", rb)); CTEFreeMem(rb); } DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_REQUEST, (DTEXT("-ARPRequestComplete [%x]\n"), Status)); } //** ARPRcv - ARP receive data handler. // // This routine is called when data arrives from the NDIS driver. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Context - NDIS context to be used for TD. // Header - Pointer to header // HeaderSize - Size of header // Data - Pointer to buffer of received data // Size - Byte count of data in buffer. // TotalSize - Byte count of total packet size. // // Exit: Status indicating whether or not we took the packet. // NDIS_STATUS NDIS_API ARPRcv(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize, void *Data, uint Size, uint TotalSize) { ARPInterface *Interface = Handle; NDIS_STATUS status; PINT OrigPacket = NULL; //get the original packet (if any) //this is required to make task offload work //note: We shall hack the pClientCount Field //to point to the packet as a short term solution //to avoid changing all atm - ip interface changes if (Interface->ai_OffloadFlags || Interface->ai_IPSecOffloadFlags) { OrigPacket = (PINT) NdisGetReceivedPacket(Interface->ai_handle, Context); } //Call the new interface with null mdl and context pointers status = ARPRcvIndicationNew(Handle, Context, Header, HeaderSize, Data, Size, TotalSize, NULL, OrigPacket); return status; } //** ARPRcvPacket - ARP receive data handler. // // This routine is called when data arrives from the NDIS driver. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Packet - Contains the incoming frame // // Returns number of upper layer folks latching on to this frame // // INT ARPRcvPacket(NDIS_HANDLE Handle, PNDIS_PACKET Packet) { UINT HeaderBufferSize = NDIS_GET_PACKET_HEADER_SIZE(Packet); UINT firstbufferLength, bufferLength, LookAheadBufferSize; PNDIS_BUFFER pFirstBuffer; PUCHAR headerBuffer; NTSTATUS ntStatus; INT ClientCnt = 0; // // Query the number of buffers, the first MDL's descriptor and the packet length // NdisGetFirstBufferFromPacket(Packet, // packet &pFirstBuffer, // first buffer descriptor &headerBuffer, // ptr to the start of packet &firstbufferLength, // length of the header+lookahead &bufferLength); // length of the bytes in the buffers // // ReceiveContext is the packet itself // LookAheadBufferSize = firstbufferLength - HeaderBufferSize; ntStatus = ARPRcvIndicationNew(Handle, Packet, headerBuffer, HeaderBufferSize, headerBuffer + HeaderBufferSize, // LookaheadBuffer LookAheadBufferSize, // LookaheadBufferSize bufferLength - HeaderBufferSize, // PacketSize - since // the whole packet is // indicated pFirstBuffer, // pMdl &ClientCnt // tdi client count ); return ClientCnt; } //** ARPRcvIndicationNew - ARP receive data handler. // // This routine is called when data arrives from the NDIS driver. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // Context - NDIS context to be used for TD. // Header - Pointer to header // HeaderSize - Size of header // Data - Pointer to buffer of received data // Size - Byte count of data in buffer. // TotalSize - Byte count of total packet size. // pMdl - NDIS_BUFFER of incoming frame // pClientCnt address to return the clinet counts // // Exit: Status indicating whether or not we took the packet. // NDIS_STATUS NDIS_API ARPRcvIndicationNew(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize, void *Data, uint Size, uint TotalSize, PNDIS_BUFFER pNdisBuffer, PINT pClientCnt) { ARPInterface *Interface = Handle; // Interface for this driver. ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *) Header; SNAPHeader UNALIGNED *SNAPHdr; ushort type; // Protocol type uint ProtOffset; // Offset in Data to non-media info. uint NUCast; // TRUE if the frame is not a unicast frame. if ((Interface->ai_operstatus == INTERFACE_UP) && HeaderSize >= (uint) Interface->ai_hdrsize) { // Per RFC 1213 and its successors, the inoctets count includes // the MAC header bytes. Interface->ai_inoctets += HeaderSize + TotalSize; NUCast = ((*((uchar UNALIGNED *) EHdr + Interface->ai_bcastoff) & Interface->ai_bcastmask) == Interface->ai_bcastval) ? AI_NONUCAST_INDEX : AI_UCAST_INDEX; if ((Interface->ai_promiscuous) && (!NUCast)) { // AI_UCAST_INDEX = 0 switch (Interface->ai_media) { case NdisMedium802_3:{ // Enet if (Interface->ai_addrlen != ARP_802_ADDR_LENGTH || CTEMemCmp(EHdr->eh_daddr, Interface->ai_addr, ARP_802_ADDR_LENGTH) != 0) { NUCast = AI_PROMIS_INDEX; } break; } case NdisMedium802_5:{ // token ring TRHeader UNALIGNED *THdr = (TRHeader UNALIGNED *) Header; if (Interface->ai_addrlen != ARP_802_ADDR_LENGTH || CTEMemCmp(THdr->tr_daddr, Interface->ai_addr, ARP_802_ADDR_LENGTH) != 0) { NUCast = AI_PROMIS_INDEX; } break; } case NdisMediumFddi:{ // FDDI FDDIHeader UNALIGNED *FHdr = (FDDIHeader UNALIGNED *) Header; if (Interface->ai_addrlen != ARP_802_ADDR_LENGTH || CTEMemCmp(FHdr->fh_daddr, Interface->ai_addr, ARP_802_ADDR_LENGTH) != 0) { NUCast = AI_PROMIS_INDEX; } break; } case NdisMediumArcnet878_2:{ // ArcNet DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_RX, (DTEXT("-ARPRcvIndicationNew [NOT_RECOGNIZED]\n"))); return NDIS_STATUS_NOT_RECOGNIZED; break; } default: ASSERT(0); Interface->ai_outerrors++; DEBUGMSG(DBG_TRACE && DBG_ARP && DBG_RX, (DTEXT("-ARPRcvIndicationNew [UNSUPPORTED_MEDIA]\n"))); return NDIS_STATUS_UNSUPPORTED_MEDIA; } } if ((Interface->ai_media == NdisMedium802_3) && (type = net_short(EHdr->eh_type)) >= MIN_ETYPE) { ProtOffset = 0; } else if (Interface->ai_media != NdisMediumArcnet878_2) { SNAPHdr = (SNAPHeader UNALIGNED *) Data; if (Size >= sizeof(SNAPHeader) && SNAPHdr->sh_dsap == SNAP_SAP && SNAPHdr->sh_ssap == SNAP_SAP && SNAPHdr->sh_ctl == SNAP_UI) { type = net_short(SNAPHdr->sh_etype); ProtOffset = sizeof(SNAPHeader); } else { //handle XID/TEST here. Interface->ai_uknprotos++; return NDIS_STATUS_NOT_RECOGNIZED; } } else { ARCNetHeader UNALIGNED *AH = (ARCNetHeader UNALIGNED *) Header; ProtOffset = 0; if (AH->ah_prot == ARP_ARCPROT_IP) type = ARP_ETYPE_IP; else if (AH->ah_prot == ARP_ARCPROT_ARP) type = ARP_ETYPE_ARP; else type = 0; } if (type == ARP_ETYPE_IP) { (Interface->ai_inpcount[NUCast])++; ASSERT (KeGetCurrentIrql() <= DISPATCH_LEVEL); IPRcvPacket(Interface->ai_context, (uchar *) Data + ProtOffset, Size - ProtOffset, TotalSize - ProtOffset, Context, ProtOffset, NUCast, HeaderSize, pNdisBuffer, (PUINT) pClientCnt, NULL); return NDIS_STATUS_SUCCESS; } else { if (type == ARP_ETYPE_ARP) { (Interface->ai_inpcount[NUCast])++; return HandleARPPacket(Interface, Header, HeaderSize, (ARPHeader *) ((uchar *) Data + ProtOffset), Size - ProtOffset, ProtOffset); } else { Interface->ai_uknprotos++; return NDIS_STATUS_NOT_RECOGNIZED; } } } else { // Interface is marked as down. return NDIS_STATUS_NOT_RECOGNIZED; } } //** ARPRcvComplete - ARP receive complete handler. // // This routine is called by the NDIS driver after some number of // receives. In some sense, it indicates 'idle time'. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // // Exit: Nothing. // void NDIS_API ARPRcvComplete(NDIS_HANDLE Handle) { UNREFERENCED_PARAMETER(Handle); IPRcvComplete(); } //** ARPStatus - ARP status handler. // // Called by the NDIS driver when some sort of status change occurs. // We take action depending on the type of status. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // GStatus - General type of status that caused the call. // Status - Pointer to a buffer of status specific information. // StatusSize - Size of the status buffer. // // Exit: Nothing. // void NDIS_API ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint StatusSize) { ARPInterface *ai = (ARPInterface *) Handle; // // ndis calls this sometimes even before ip interface is created. // if ((ai->ai_context) && (ai->ai_operstatus != INTERFACE_INIT)) { IPStatus(ai->ai_context, GStatus, Status, StatusSize, NULL); switch (GStatus) { //reflect media connect/disconnect status in //operstatus for query purpose case NDIS_STATUS_MEDIA_CONNECT: ai->ai_mediastatus = TRUE; ARPUpdateOperStatus(ai); break; case NDIS_STATUS_MEDIA_DISCONNECT: ai->ai_mediastatus = FALSE; ARPUpdateOperStatus(ai); break; default: break; } } } //** ARPStatusComplete - ARP status complete handler. // // A routine called by the NDIS driver so that we can do postprocessing // after a status event. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // // Exit: Nothing. // void NDIS_API ARPStatusComplete(NDIS_HANDLE Handle) { UNREFERENCED_PARAMETER(Handle); } //** ARPPnPEvent - ARP PnPEvent handler. // // Called by the NDIS driver when PnP or PM events occurs. // // Entry: // Handle - The binding handle we specified (really a pointer to an AI). // NetPnPEvent - This is a pointer to a NET_PNP_EVENT that describes // the PnP indication. // // Exit: // Just call into IP and return status. // NDIS_STATUS ARPPnPEvent(NDIS_HANDLE Handle, PNET_PNP_EVENT NetPnPEvent) { ARPInterface *ai = (ARPInterface *) Handle; // // ndis can calls this sometimes even before ip interface is created. // if (ai && !ai->ai_context) { return STATUS_SUCCESS; } else { return IPPnPEvent(ai ? ai->ai_context : NULL, NetPnPEvent); } } //** ARPSetNdisRequest - ARP Ndisrequest handler. // // Called by the upper driver to set the packet filter for the interface. // // Entry: // Context - Context value we gave to IP (really a pointer to an AI). // OID - Object ID to set/unset // On - Set_if, clear_if or clear_card // // Exit: // returns status. // NDIS_STATUS __stdcall ARPSetNdisRequest(void *Context, NDIS_OID OID, uint On) { int Status; ARPInterface *Interface = (ARPInterface *) Context; if (On == SET_IF) { Interface->ai_pfilter |= OID; if (OID == NDIS_PACKET_TYPE_PROMISCUOUS) { Interface->ai_promiscuous = 1; } Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL, TRUE); } else { // turn off Interface->ai_pfilter &= ~(OID); if (OID == NDIS_PACKET_TYPE_PROMISCUOUS) { Interface->ai_promiscuous = 0; } Status = DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL, TRUE); } return Status; } //** ARPPnPComplete - ARP PnP complete handler. // // Called by the upper driver to do the post processing of pnp event. // // Entry: // Context - Context value we gave to IP (really a pointer to an AI). // Status - Status code of the pnp operation. // NetPnPEvent - This is a pointer to a NET_PNP_EVENT that describes // the PnP indication. // // Exit: // returns nothing. // void __stdcall ARPPnPComplete(void *Context, NDIS_STATUS Status, PNET_PNP_EVENT NetPnPEvent) { ARPInterface *Interface = (ARPInterface *) Context; NdisCompletePnPEvent(Status, (Interface ? Interface->ai_handle : NULL), NetPnPEvent); } extern void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2); extern void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext); extern void NDIS_API ARPUnloadProtocol(void); extern void ArpUnload(PDRIVER_OBJECT); //* ARPReadNext - Read the next entry in the ARP table. // // Called by the GetInfo code to read the next ATE in the table. We assume // the context passed in is valid, and the caller has the ARP TableLock. // // Input: Context - Pointer to a IPNMEContext. // Interface - Pointer to interface for table to read on. // Buffer - Pointer to an IPNetToMediaEntry structure. // // Returns: TRUE if more data is available to be read, FALSE is not. // uint ARPReadNext(void *Context, ARPInterface * Interface, void *Buffer) { IPNMEContext *NMContext = (IPNMEContext *) Context; IPNetToMediaEntry *IPNMEntry = (IPNetToMediaEntry *) Buffer; CTELockHandle Handle; ARPTableEntry *CurrentATE; uint i; ARPTable *Table = Interface->ai_ARPTbl; uint AddrOffset; CurrentATE = NMContext->inc_entry; // Fill in the buffer. CTEGetLock(&CurrentATE->ate_lock, &Handle); IPNMEntry->inme_index = Interface->ai_index; IPNMEntry->inme_physaddrlen = Interface->ai_addrlen; switch (Interface->ai_media) { case NdisMedium802_3: AddrOffset = 0; break; case NdisMedium802_5: AddrOffset = offsetof(struct TRHeader, tr_daddr); break; case NdisMediumFddi: AddrOffset = offsetof(struct FDDIHeader, fh_daddr); break; case NdisMediumArcnet878_2: AddrOffset = offsetof(struct ARCNetHeader, ah_daddr); break; default: AddrOffset = 0; break; } RtlCopyMemory(IPNMEntry->inme_physaddr, &CurrentATE->ate_addr[AddrOffset], Interface->ai_addrlen); IPNMEntry->inme_addr = CurrentATE->ate_dest; if (CurrentATE->ate_state == ARP_GOOD) IPNMEntry->inme_type = (CurrentATE->ate_valid == ALWAYS_VALID ? INME_TYPE_STATIC : INME_TYPE_DYNAMIC); else IPNMEntry->inme_type = INME_TYPE_INVALID; CTEFreeLock(&CurrentATE->ate_lock, Handle); // We've filled it in. Now update the context. if (CurrentATE->ate_next != NULL) { NMContext->inc_entry = CurrentATE->ate_next; return TRUE; } else { // The next ATE is NULL. Loop through the ARP Table looking for a new // one. i = NMContext->inc_index + 1; while (i < ARP_TABLE_SIZE) { if ((*Table)[i] != NULL) { NMContext->inc_entry = (*Table)[i]; NMContext->inc_index = i; return TRUE; break; } else i++; } NMContext->inc_index = 0; NMContext->inc_entry = NULL; return FALSE; } } //* ARPValidateContext - Validate the context for reading an ARP table. // // Called to start reading an ARP table sequentially. We take in // a context, and if the values are 0 we return information about the // first route in the table. Otherwise we make sure that the context value // is valid, and if it is we return TRUE. // We assume the caller holds the ARPInterface lock. // // Input: Context - Pointer to a RouteEntryContext. // Interface - Pointer to an interface // Valid - Where to return information about context being // valid. // // Returns: TRUE if more data to be read in table, FALSE if not. *Valid set // to TRUE if input context is valid // uint ARPValidateContext(void *Context, ARPInterface * Interface, uint * Valid) { IPNMEContext *NMContext = (IPNMEContext *) Context; uint i; ARPTableEntry *TargetATE; ARPTableEntry *CurrentATE; ARPTable *Table = Interface->ai_ARPTbl; i = NMContext->inc_index; TargetATE = NMContext->inc_entry; // If the context values are 0 and NULL, we're starting from the beginning. if (i == 0 && TargetATE == NULL) { *Valid = TRUE; do { if ((CurrentATE = (*Table)[i]) != NULL) { break; } i++; } while (i < ARP_TABLE_SIZE); if (CurrentATE != NULL) { NMContext->inc_index = i; NMContext->inc_entry = CurrentATE; return TRUE; } else return FALSE; } else { // We've been given a context. We just need to make sure that it's // valid. if (i < ARP_TABLE_SIZE) { CurrentATE = (*Table)[i]; while (CurrentATE != NULL) { if (CurrentATE == TargetATE) { *Valid = TRUE; return TRUE; break; } else { CurrentATE = CurrentATE->ate_next; } } } // If we get here, we didn't find the matching ATE. *Valid = FALSE; return FALSE; } } #define IFE_FIXED_SIZE offsetof(struct IFEntry, if_descr) //* ARPQueryInfo - ARP query information handler. // // Called to query information about the ARP table or statistics about the // actual interface. // // Input: IFContext - Interface context (pointer to an ARPInterface). // ID - TDIObjectID for object. // Buffer - Buffer to put data into. // Size - Pointer to size of buffer. On return, filled with // bytes copied. // Context - Pointer to context block. // // Returns: Status of attempt to query information. // int __stdcall ARPQueryInfo(void *IFContext, TDIObjectID * ID, PNDIS_BUFFER Buffer, uint * Size, void *Context) { ARPInterface *AI = (ARPInterface *) IFContext; uint Offset = 0; uint BufferSize = *Size; CTELockHandle Handle; uint ContextValid, DataLeft; uint BytesCopied = 0; uchar InfoBuff[sizeof(IFEntry)]; uint Entity; uint Instance; BOOLEAN fStatus; long QueueLength = 0; uint i; DEBUGMSG(DBG_TRACE && DBG_QUERYINFO, (DTEXT("+ARPQueryInfo(%x, %x, %x, %x, %x)\n"), IFContext, ID, Buffer, Size, Context)); Entity = ID->toi_entity.tei_entity; Instance = ID->toi_entity.tei_instance; // TCPTRACE(("ARPQueryInfo: AI %lx, Instance %lx, ai_atinst %lx, ai_ifinst %lx\n", // AI, Instance, AI->ai_atinst, AI->ai_ifinst )); // First, make sure it's possibly an ID we can handle. if ((Entity != AT_ENTITY || Instance != AI->ai_atinst) && (Entity != IF_ENTITY || Instance != AI->ai_ifinst)) { return TDI_INVALID_REQUEST; } *Size = 0; // In case of an error. if (ID->toi_type != INFO_TYPE_PROVIDER) return TDI_INVALID_PARAMETER; if (ID->toi_class == INFO_CLASS_GENERIC) { if (ID->toi_id == ENTITY_TYPE_ID) { // He's trying to see what type we are. if (BufferSize >= sizeof(uint)) { *(uint *) & InfoBuff[0] = (Entity == AT_ENTITY) ? AT_ARP : IF_MIB; fStatus = CopyToNdisSafe(Buffer, NULL, InfoBuff, sizeof(uint), &Offset); if (fStatus == FALSE) { return TDI_NO_RESOURCES; } *Size = sizeof(uint); return TDI_SUCCESS; } else return TDI_BUFFER_TOO_SMALL; } return TDI_INVALID_PARAMETER; } // Might be able to handle this. if (Entity == AT_ENTITY) { // It's an address translation object. It could be a MIB object or // an implementation specific object (the generic objects were handled // above). if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { ARPPArpAddr *PArpAddr; // It's an implementation specific ID. The only ones we handle // are the PARP_COUNT_ID and the PARP_ENTRY ID. if (ID->toi_id == AT_ARP_PARP_COUNT_ID) { // He wants to know the count. Just return that to him. if (BufferSize >= sizeof(uint)) { CTEGetLock(&AI->ai_lock, &Handle); fStatus = CopyToNdisSafe(Buffer, NULL, (uchar *) & AI->ai_parpcount, sizeof(uint), &Offset); CTEFreeLock(&AI->ai_lock, Handle); if (fStatus == FALSE) { return TDI_NO_RESOURCES; } *Size = sizeof(uint); return TDI_SUCCESS; } else return TDI_BUFFER_TOO_SMALL; } if (ID->toi_id != AT_ARP_PARP_ENTRY_ID) return TDI_INVALID_PARAMETER; // It's for Proxy ARP entries. The context should be either NULL // or a pointer to the next one to be read. CTEGetLock(&AI->ai_lock, &Handle); PArpAddr = *(ARPPArpAddr **) Context; if (PArpAddr != NULL) { ARPPArpAddr *CurrentPARP; // Loop through the P-ARP addresses on the interface, and // see if we can find this one. CurrentPARP = AI->ai_parpaddr; while (CurrentPARP != NULL) { if (CurrentPARP == PArpAddr) break; else CurrentPARP = CurrentPARP->apa_next; } // If we found a match, PARPAddr points to where to begin // reading. Otherwise, fail the request. if (CurrentPARP == NULL) { // Didn't find a match, so fail the request. CTEFreeLock(&AI->ai_lock, Handle); return TDI_INVALID_PARAMETER; } } else PArpAddr = AI->ai_parpaddr; // PARPAddr points to the next entry to put in the buffer, if // there is one. while (PArpAddr != NULL) { if ((int)(BufferSize - BytesCopied) >= (int)sizeof(ProxyArpEntry)) { ProxyArpEntry *TempPArp; TempPArp = (ProxyArpEntry *) InfoBuff; TempPArp->pae_status = PAE_STATUS_VALID; TempPArp->pae_addr = PArpAddr->apa_addr; TempPArp->pae_mask = PArpAddr->apa_mask; BytesCopied += sizeof(ProxyArpEntry); fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *) TempPArp, sizeof(ProxyArpEntry), &Offset); if (fStatus == FALSE) { CTEFreeLock(&AI->ai_lock, Handle); return TDI_NO_RESOURCES; } PArpAddr = PArpAddr->apa_next; } else break; } // We're done copying. Free the lock and return the correct // status. CTEFreeLock(&AI->ai_lock, Handle); *Size = BytesCopied; **(ARPPArpAddr ***) & Context = PArpAddr; return(PArpAddr == NULL) ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW; } if (ID->toi_id == AT_MIB_ADDRXLAT_INFO_ID) { AddrXlatInfo *AXI; // It's for the count. Just return the number of entries in the // table. if (BufferSize >= sizeof(AddrXlatInfo)) { *Size = sizeof(AddrXlatInfo); AXI = (AddrXlatInfo *) InfoBuff; AXI->axi_count = AI->ai_count; AXI->axi_index = AI->ai_index; fStatus = CopyToNdisSafe(Buffer, NULL, (uchar *) AXI, sizeof(AddrXlatInfo), &Offset); if (fStatus == FALSE) { return TDI_NO_RESOURCES; } *Size = sizeof(AddrXlatInfo); return TDI_SUCCESS; } else return TDI_BUFFER_TOO_SMALL; } if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID) { // He's trying to read the table. // Make sure we have a valid context. CTEGetLock(&AI->ai_ARPTblLock, &Handle); DataLeft = ARPValidateContext(Context, AI, &ContextValid); // If the context is valid, we'll continue trying to read. if (!ContextValid) { CTEFreeLock(&AI->ai_ARPTblLock, Handle); return TDI_INVALID_PARAMETER; } while (DataLeft) { // The invariant here is that there is data in the table to // read. We may or may not have room for it. So DataLeft // is TRUE, and BufferSize - BytesCopied is the room left // in the buffer. if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPNetToMediaEntry)) { DataLeft = ARPReadNext(Context, AI, InfoBuff); BytesCopied += sizeof(IPNetToMediaEntry); fStatus = CopyToNdisSafe(Buffer, &Buffer, InfoBuff, sizeof(IPNetToMediaEntry), &Offset); if (fStatus == FALSE) { CTEFreeLock(&AI->ai_ARPTblLock, Handle); return(TDI_NO_RESOURCES); } } else break; } *Size = BytesCopied; CTEFreeLock(&AI->ai_ARPTblLock, Handle); return(!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); } return TDI_INVALID_PARAMETER; } if (ID->toi_class != INFO_CLASS_PROTOCOL) return TDI_INVALID_PARAMETER; // He must be asking for interface level information. See if we support // what he's asking for. if (ID->toi_id == IF_MIB_STATS_ID) { IFEntry *IFE = (IFEntry *) InfoBuff; uint speed; // He's asking for statistics. Make sure his buffer is at least big // enough to hold the fixed part. if (BufferSize < IFE_FIXED_SIZE) { return TDI_BUFFER_TOO_SMALL; } // He's got enough to hold the fixed part. Build the IFEntry structure, // and copy it to his buffer. IFE->if_index = AI->ai_index; switch (AI->ai_media) { case NdisMedium802_3: IFE->if_type = IF_TYPE_ETHERNET_CSMACD; break; case NdisMedium802_5: IFE->if_type = IF_TYPE_ISO88025_TOKENRING; break; case NdisMediumFddi: IFE->if_type = IF_TYPE_FDDI; break; case NdisMediumArcnet878_2: default: IFE->if_type = IF_TYPE_OTHER; break; } IFE->if_mtu = AI->ai_mtu; // Some adapters support dynamic speed settings and causes this // query to return a different speed from the Networks Connection // folder. Therefore, we will requery the speed of the // interface. Should we update the ai_speed? Anf if so, do we update // if_speed as well? IFE->if_speed = AI->ai_speed; if (AI->ai_mediastatus == TRUE) { if (DoNDISRequest( AI, NdisRequestQueryInformation, OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL, TRUE) == NDIS_STATUS_SUCCESS) { // Update to real value we want to return. speed *= 100L; IFE->if_speed = speed; } else { // Should we fail, or just update with known speed. IFE->if_speed = AI->ai_speed; } } IFE->if_physaddrlen = AI->ai_addrlen; RtlCopyMemory(IFE->if_physaddr, AI->ai_addr, AI->ai_addrlen); IFE->if_adminstatus = (uint) AI->ai_adminstate; if (AI->ai_operstatus == INTERFACE_UP) { IFE->if_operstatus = IF_OPER_STATUS_OPERATIONAL; } else { // DOWN,INIT, and UNINIT all count as non-operational IFE->if_operstatus = IF_OPER_STATUS_NON_OPERATIONAL; } IFE->if_lastchange = AI->ai_lastchange; IFE->if_inoctets = AI->ai_inoctets; IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX] + AI->ai_inpcount[AI_PROMIS_INDEX]; IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX]; IFE->if_indiscards = AI->ai_indiscards; IFE->if_inerrors = AI->ai_inerrors; IFE->if_inunknownprotos = AI->ai_uknprotos; IFE->if_outoctets = AI->ai_outoctets; IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX]; IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX]; IFE->if_outdiscards = AI->ai_outdiscards; IFE->if_outerrors = AI->ai_outerrors; for (i=0; i < (uint)KeNumberProcessors; i++) { QueueLength += AI->ai_qlen[i].ai_qlen; } IFE->if_outqlen = max(0, QueueLength); IFE->if_descrlen = AI->ai_desclen; #if FFP_SUPPORT // If FFP enabled on this interface, adjust IF stats for FFP'd packets if (AI->ai_ffpversion) { FFPAdapterStats IFStatsInfo = { NDIS_PROTOCOL_ID_TCP_IP, 0, 0, 0, 0, 0, 0, 0, 0 }; // Update ARP SNMP vars to account for FFP'd packets if (DoNDISRequest(AI, NdisRequestQueryInformation, OID_FFP_ADAPTER_STATS, &IFStatsInfo, sizeof(FFPAdapterStats), NULL, TRUE) == NDIS_STATUS_SUCCESS) { // Compensate 'inoctets' for packets not seen due to FFP IFE->if_inoctets += IFStatsInfo.InOctetsForwarded; IFE->if_inoctets += IFStatsInfo.InOctetsDiscarded; // Compensate 'inucastpkts' for packets not seen due to FFP // Assume all FFP fwded/dropped pkts came in as Eth Unicasts // A check to see if it is a ucast or an mcast would slow FFP IFE->if_inucastpkts += IFStatsInfo.InPacketsForwarded; IFE->if_inucastpkts += IFStatsInfo.InPacketsDiscarded; // Compensate 'outoctets' for packets not seen due to FFP IFE->if_outoctets += IFStatsInfo.OutOctetsForwarded; // Compensate 'outucastpkts' for packets not seen due to FFP // Assume all FFP fwded are sent as Ethernet Unicasts // A check to see if it is a ucast or an mcast would slow FFP IFE->if_outucastpkts += IFStatsInfo.OutPacketsForwarded; } } #endif // if FFP_SUPPORT fStatus = CopyToNdisSafe(Buffer, &Buffer, (uchar *) IFE, IFE_FIXED_SIZE, &Offset); if (fStatus == FALSE) { return TDI_NO_RESOURCES; } // See if he has room for the descriptor string. if (BufferSize >= (IFE_FIXED_SIZE + AI->ai_desclen)) { // He has room. Copy it. if (AI->ai_desclen != 0) { fStatus = CopyToNdisSafe( Buffer, NULL, (PUCHAR) AI->ai_desc, AI->ai_desclen, &Offset); } if (fStatus == FALSE) { return TDI_NO_RESOURCES; } *Size = IFE_FIXED_SIZE + AI->ai_desclen; return TDI_SUCCESS; } else { // Not enough room to copy the desc. string. *Size = IFE_FIXED_SIZE; return TDI_BUFFER_OVERFLOW; } } else if (ID->toi_id == IF_FRIENDLY_NAME_ID) { int Status; PNDIS_BUFFER NextBuffer; NDIS_STRING NdisString; // This is a query for the adapter's friendly name. // We'll convert this to an OID_GEN_FRIENDLY_NAME query for NDIS, // and transfer the resulting UNICODE_STRING to the caller's buffer // as a nul-terminated Unicode string. if (NdisQueryAdapterInstanceName(&NdisString, AI->ai_handle) == NDIS_STATUS_SUCCESS) { // Verify that the buffer is large enough for the string we just // retrieved and, if so, attempt to copy the string to the // caller's buffer. If that succeeds, nul-terminate the resulting // string. if (BufferSize >= (NdisString.Length + 1) * sizeof(WCHAR)) { fStatus = CopyToNdisSafe(Buffer, &NextBuffer, (uchar *)NdisString.Buffer, NdisString.Length, &Offset); if (fStatus) { WCHAR Nul = L'\0'; fStatus = CopyToNdisSafe(Buffer, &NextBuffer, (uchar *)&Nul, sizeof(Nul), &Offset); if (fStatus) { *Size = NdisString.Length + sizeof(Nul); Status = TDI_SUCCESS; } else Status = TDI_NO_RESOURCES; } else Status = TDI_NO_RESOURCES; } else Status = TDI_BUFFER_OVERFLOW; NdisFreeString(NdisString); return Status; } else return TDI_NO_RESOURCES; } else if (ID->toi_id == IF_QUERY_SET_OFFLOAD_ID) { IFOffloadCapability IFOC; if (BufferSize < sizeof(IFOC)) { return TDI_BUFFER_TOO_SMALL; } if (QueryAndSetOffload(AI)) { IFOC.ifoc_OffloadFlags = AI->ai_OffloadFlags; IFOC.ifoc_IPSecOffloadFlags = AI->ai_IPSecOffloadFlags; if (CopyToNdisSafe(Buffer, NULL, (uchar*)&IFOC, sizeof(IFOC), &Offset)) { return TDI_SUCCESS; } } return TDI_NO_RESOURCES; } return TDI_INVALID_PARAMETER; } //* ARPSetInfo - ARP set information handler. // // The ARP set information handler. We support setting of an I/F admin // status, and setting/deleting of ARP table entries. // // Input: Context - Pointer to I/F to set on. // ID - The object ID // Buffer - Pointer to buffer containing value to set. // Size - Size in bytes of Buffer. // // Returns: Status of attempt to set information. // int __stdcall ARPSetInfo(void *Context, TDIObjectID * ID, void *Buffer, uint Size) { ARPInterface *Interface = (ARPInterface *) Context; CTELockHandle Handle, EntryHandle; int Status; IFEntry UNALIGNED *IFE = (IFEntry UNALIGNED *) Buffer; IPNetToMediaEntry UNALIGNED *IPNME; ARPTableEntry *PrevATE, *CurrentATE; ARPTable *Table; ENetHeader *Header; uint Entity, Instance; PNDIS_PACKET Packet; Entity = ID->toi_entity.tei_entity; Instance = ID->toi_entity.tei_instance; // First, make sure it's possibly an ID we can handle. if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) && (Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) { return TDI_INVALID_REQUEST; } if (ID->toi_type != INFO_TYPE_PROVIDER) { return TDI_INVALID_PARAMETER; } // Might be able to handle this. if (Entity == IF_ENTITY) { // It's for the I/F level, see if it's for the statistics. if (ID->toi_class != INFO_CLASS_PROTOCOL) return TDI_INVALID_PARAMETER; if (ID->toi_id == IF_MIB_STATS_ID) { // It's for the stats. Make sure it's a valid size. if (Size >= IFE_FIXED_SIZE) { // It's a valid size. See what he wants to do. CTEGetLock(&Interface->ai_lock, &Handle); switch (IFE->if_adminstatus) { case IF_STATUS_UP: // He's marking it up. If the operational state is // alse up, mark the whole interface as up. Interface->ai_adminstate = IF_STATUS_UP; ARPUpdateOperStatus(Interface); Status = TDI_SUCCESS; break; case IF_STATUS_DOWN: // He's taking it down. Mark both the admin state and // the interface state down. Interface->ai_adminstate = IF_STATUS_DOWN; ARPUpdateOperStatus(Interface); Status = TDI_SUCCESS; break; case IF_STATUS_TESTING: // He's trying to cause up to do testing, which we // don't support. Just return success. Status = TDI_SUCCESS; break; default: Status = TDI_INVALID_PARAMETER; break; } CTEFreeLock(&Interface->ai_lock, Handle); return Status; } else return TDI_INVALID_PARAMETER; } else { return TDI_INVALID_PARAMETER; } } // Not for the interface level. See if it's an implementation or protocol // class. if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { ProxyArpEntry UNALIGNED *PArpEntry; ARPIPAddr *Addr; IPAddr AddAddr; IPMask Mask; // It's for the implementation. It should be the proxy-ARP ID. if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry)) return TDI_INVALID_PARAMETER; PArpEntry = (ProxyArpEntry UNALIGNED *) Buffer; AddAddr = PArpEntry->pae_addr; Mask = PArpEntry->pae_mask; // See if he's trying to add or delete a proxy arp entry. if (PArpEntry->pae_status == PAE_STATUS_VALID) { // We're trying to add an entry. We won't allow an entry // to be added that we believe to be invalid or conflicting // with our local addresses. if (!VALID_MASK(Mask)) return TDI_INVALID_PARAMETER; if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) || IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) || IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) || IP_LOOPBACK(AddAddr) || CLASSD_ADDR(AddAddr) || CLASSE_ADDR(AddAddr)) return TDI_INVALID_PARAMETER; // Walk through the list of addresses on the interface, and see // if they would match the AddAddr. If so, fail the request. CTEGetLock(&Interface->ai_lock, &Handle); if (IsBCastOnIF(Interface, AddAddr & Mask)) { CTEFreeLock(&Interface->ai_lock, Handle); return TDI_INVALID_PARAMETER; } Addr = &Interface->ai_ipaddr; do { if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr)) break; } Addr = Addr->aia_next; } while (Addr != NULL); CTEFreeLock(&Interface->ai_lock, Handle); if (Addr != NULL) return TDI_INVALID_PARAMETER; // At this point, we believe we're ok. Try to add the address. if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL)) return TDI_SUCCESS; else return TDI_NO_RESOURCES; } else { if (PArpEntry->pae_status == PAE_STATUS_INVALID) { // He's trying to delete a proxy ARP address. if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask)) return TDI_SUCCESS; } return TDI_INVALID_PARAMETER; } } if (ID->toi_class != INFO_CLASS_PROTOCOL) { return TDI_INVALID_PARAMETER; } if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID && Size >= sizeof(IPNetToMediaEntry)) { // He does want to set an ARP table entry. See if he's trying to // create or delete one. IPNME = (IPNetToMediaEntry UNALIGNED *) Buffer; if (IPNME->inme_type == INME_TYPE_INVALID) { uint Index = ARP_HASH(IPNME->inme_addr); // We're trying to delete an entry. See if we can find it, // and then delete it. CTEGetLock(&Interface->ai_ARPTblLock, &Handle); Table = Interface->ai_ARPTbl; PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next); CurrentATE = (*Table)[Index]; while (CurrentATE != (ARPTableEntry *) NULL) { if (CurrentATE->ate_dest == IPNME->inme_addr) { // Found him. Break out of the loop. break; } else { PrevATE = CurrentATE; CurrentATE = CurrentATE->ate_next; } } if (CurrentATE != NULL) { CTEGetLock(&CurrentATE->ate_lock, &EntryHandle); if (CurrentATE->ate_resolveonly) { ARPControlBlock *ArpContB, *TmpArpContB; ArpContB = CurrentATE->ate_resolveonly; while (ArpContB) { ArpRtn rtn; rtn = (ArpRtn) ArpContB->CompletionRtn; ArpContB->status = (IP_STATUS) STATUS_UNSUCCESSFUL; TmpArpContB = ArpContB->next; (*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL); ArpContB = TmpArpContB; } CurrentATE->ate_resolveonly = NULL; } RemoveARPTableEntry(PrevATE, CurrentATE); Interface->ai_count--; CTEFreeLockFromDPC(&CurrentATE->ate_lock); CTEFreeLock(&Interface->ai_ARPTblLock, Handle); if (CurrentATE->ate_packet != NULL) { IPSendComplete(Interface->ai_context, CurrentATE->ate_packet, NDIS_STATUS_SUCCESS); } CTEFreeMem(CurrentATE); return TDI_SUCCESS; } else Status = TDI_INVALID_PARAMETER; CTEFreeLock(&Interface->ai_ARPTblLock, Handle); return Status; } // We're not trying to delete. See if we're trying to create. if (IPNME->inme_type != INME_TYPE_DYNAMIC && IPNME->inme_type != INME_TYPE_STATIC) { // Not creating, return an error. return TDI_INVALID_PARAMETER; } // Make sure he's trying to create a valid address. if (IPNME->inme_physaddrlen != Interface->ai_addrlen) return TDI_INVALID_PARAMETER; // We're trying to create an entry. Call CreateARPTableEntry to create // one, and fill it in. CurrentATE = CreateARPTableEntry(Interface, IPNME->inme_addr, &Handle, 0); if (CurrentATE == NULL) { return TDI_NO_RESOURCES; } // We've created or found an entry. Fill it in. Header = (ENetHeader *) CurrentATE->ate_addr; switch (Interface->ai_media) { case NdisMedium802_5: { TRHeader *Temp = (TRHeader *) Header; // Fill in the TR specific parts, and set the length to the // size of a TR header. Temp->tr_ac = ARP_AC; Temp->tr_fc = ARP_FC; RtlCopyMemory(&Temp->tr_saddr[ARP_802_ADDR_LENGTH], ARPSNAP, sizeof(SNAPHeader)); Header = (ENetHeader *) & Temp->tr_daddr; CurrentATE->ate_addrlength = sizeof(TRHeader) + sizeof(SNAPHeader); } break; case NdisMedium802_3: CurrentATE->ate_addrlength = sizeof(ENetHeader); break; case NdisMediumFddi: { FDDIHeader *Temp = (FDDIHeader *) Header; Temp->fh_pri = ARP_FDDI_PRI; RtlCopyMemory(&Temp->fh_saddr[ARP_802_ADDR_LENGTH], ARPSNAP, sizeof(SNAPHeader)); Header = (ENetHeader *) & Temp->fh_daddr; CurrentATE->ate_addrlength = sizeof(FDDIHeader) + sizeof(SNAPHeader); } break; case NdisMediumArcnet878_2: { ARCNetHeader *Temp = (ARCNetHeader *) Header; Temp->ah_saddr = Interface->ai_addr[0]; Temp->ah_daddr = IPNME->inme_physaddr[0]; Temp->ah_prot = ARP_ARCPROT_IP; CurrentATE->ate_addrlength = sizeof(ARCNetHeader); } break; default: ASSERT(0); break; } // Copy in the source and destination addresses. if (Interface->ai_media != NdisMediumArcnet878_2) { RtlCopyMemory(Header->eh_daddr, IPNME->inme_physaddr, ARP_802_ADDR_LENGTH); RtlCopyMemory(Header->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); // Now fill in the Ethertype. *(ushort *) & CurrentATE->ate_addr[CurrentATE->ate_addrlength - 2] = net_short(ARP_ETYPE_IP); } // If he's creating a static entry, mark it as always valid. Otherwise // mark him as valid now. if (IPNME->inme_type == INME_TYPE_STATIC) CurrentATE->ate_valid = ALWAYS_VALID; else CurrentATE->ate_valid = CTESystemUpTime(); CurrentATE->ate_state = ARP_GOOD; Packet = CurrentATE->ate_packet; CurrentATE->ate_packet = NULL; CTEFreeLock(&CurrentATE->ate_lock, Handle); if (Packet) { IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); } return TDI_SUCCESS; } return TDI_INVALID_PARAMETER; } #pragma BEGIN_INIT //** ARPInit - Initialize the ARP module. // // This functions intializes all of the ARP module, including allocating // the ARP table and any other necessary data structures. // // Entry: nothing. // // Exit: Returns 0 if we fail to init., !0 if we succeed. // int ARPInit() { NDIS_STATUS Status; // Status for NDIS calls. NDIS_PROTOCOL_CHARACTERISTICS Characteristics; DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("+ARPInit()\n"))); RtlZeroMemory(&Characteristics, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); Characteristics.MajorNdisVersion = NDIS_MAJOR_VERSION; Characteristics.MinorNdisVersion = NDIS_MINOR_VERSION; Characteristics.OpenAdapterCompleteHandler = ARPOAComplete; Characteristics.CloseAdapterCompleteHandler = ARPCAComplete; Characteristics.SendCompleteHandler = ARPSendComplete; Characteristics.TransferDataCompleteHandler = ARPTDComplete; Characteristics.ResetCompleteHandler = ARPResetComplete; Characteristics.RequestCompleteHandler = ARPRequestComplete; Characteristics.ReceiveHandler = ARPRcv, Characteristics.ReceiveCompleteHandler = ARPRcvComplete; Characteristics.StatusHandler = ARPStatus; Characteristics.StatusCompleteHandler = ARPStatusComplete; // // Re-direct to IP since IP now binds to NDIS. // Characteristics.BindAdapterHandler = IPBindAdapter; // ARPBindAdapter; Characteristics.UnbindAdapterHandler = ARPUnbindAdapter; Characteristics.PnPEventHandler = ARPPnPEvent; #if MILLEN Characteristics.UnloadHandler = ARPUnloadProtocol; #endif // MILLEN RtlInitUnicodeString(&(Characteristics.Name), ARPName); Characteristics.ReceivePacketHandler = ARPRcvPacket; DEBUGMSG(DBG_INFO && DBG_INIT, (DTEXT("ARPInit: Calling NdisRegisterProtocol %d:%d %ws\n"), NDIS_MAJOR_VERSION, NDIS_MINOR_VERSION, ARPName)); NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *) & Characteristics, sizeof(Characteristics)); DEBUGMSG(DBG_TRACE && DBG_INIT, (DTEXT("-ARPInit [%x]\n"), Status)); if (Status == NDIS_STATUS_SUCCESS) { return(1); } else { return(0); } } //* FreeARPInterface - Free an ARP interface // // Called in the event of some sort of initialization failure. We free all // the memory associated with an ARP interface. // // Entry: Interface - Pointer to interface structure to be freed. // // Returns: Nothing. // void FreeARPInterface(ARPInterface *Interface) { NDIS_STATUS Status; ARPTable *Table; // ARP table. uint i; // Index variable. ARPTableEntry *ATE; CTELockHandle LockHandle; NDIS_HANDLE Handle; if (Interface->ai_timerstarted && !CTEStopTimer(&Interface->ai_timer)) { // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Could not stop ai_timer - waiting for event\n")); (VOID) CTEBlock(&Interface->ai_timerblock); KeClearEvent(&Interface->ai_timerblock.cbs_event); } // If we're bound to the adapter, close it now. CTEInitBlockStruc(&Interface->ai_block); CTEGetLock(&Interface->ai_lock, &LockHandle); if (Interface->ai_handle != (NDIS_HANDLE) NULL) { Handle = Interface->ai_handle; Interface->ai_handle = NULL; CTEFreeLock(&Interface->ai_lock, LockHandle); NdisCloseAdapter(&Status, Handle); if (Status == NDIS_STATUS_PENDING) Status = CTEBlock(&Interface->ai_block); } else { CTEFreeLock(&Interface->ai_lock, LockHandle); } // First free any outstanding ARP table entries. Table = Interface->ai_ARPTbl; if (Table != NULL) { for (i = 0; i < ARP_TABLE_SIZE; i++) { while ((*Table)[i] != NULL) { ATE = (*Table)[i]; if (ATE->ate_resolveonly) { ARPControlBlock *ArpContB, *TmpArpContB; ArpContB = ATE->ate_resolveonly; while (ArpContB) { ArpRtn rtn; rtn = (ArpRtn) ArpContB->CompletionRtn; ArpContB->status = (IP_STATUS) STATUS_UNSUCCESSFUL; TmpArpContB = ArpContB->next; (*rtn) (ArpContB, (IP_STATUS) STATUS_UNSUCCESSFUL); ArpContB = TmpArpContB; } ATE->ate_resolveonly = NULL; } RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]), ate_next), ATE); if (ATE->ate_packet) { IPSendComplete(Interface->ai_context, ATE->ate_packet, NDIS_STATUS_SUCCESS); } CTEFreeMem(ATE); } } CTEFreeMem(Table); } Interface->ai_ARPTbl = NULL; if (Interface->ai_ppool != (NDIS_HANDLE) NULL) NdisFreePacketPool(Interface->ai_ppool); if (Interface->ai_qlen) { CTEFreeMem(Interface->ai_qlen); } if (Interface->ai_devicename.Buffer != NULL) { CTEFreeMem(Interface->ai_devicename.Buffer); } if (Interface->ai_desc) { CTEFreeMem(Interface->ai_desc); } // Free the interface itself. CTEFreeMem(Interface); } //** ARPOpen - Open an adapter for reception. // // This routine is called when the upper layer is done initializing and wishes to // begin receiveing packets. The adapter is actually 'open', we just call InitAdapter // to set the packet filter and lookahead size. // // Input: Context - Interface pointer we gave to IP earlier. // // Returns: Nothing // void __stdcall ARPOpen(void *Context) { ARPInterface *Interface = (ARPInterface *) Context; InitAdapter(Interface); // Set the packet filter - we'll begin receiving. } //* ARPGetEList - Get the entity list. // // Called at init time to get an entity list. We fill our stuff in, and // then call the interfaces below us to allow them to do the same. // // Input: EntityList - Pointer to entity list to be filled in. // Count - Pointer to number of entries in the list. // // Returns Status of attempt to get the info. // int __stdcall ARPGetEList(void *Context, TDIEntityID * EList, uint * Count) { ARPInterface *Interface = (ARPInterface *) Context; uint MyATBase; uint MyIFBase; uint i; TDIEntityID *ATEntity, *IFEntity; TDIEntityID *EntityList; // Walk down the list, looking for existing AT or IF entities, and // adjust our base instance accordingly. // if we are already on the list then do nothing. // if we are going away, mark our entry invalid. EntityList = EList; MyATBase = 0; MyIFBase = 0; ATEntity = NULL; IFEntity = NULL; for (i = 0; i < *Count; i++, EntityList++) { if (EntityList->tei_entity == AT_ENTITY) { // if we are already on the list remember our entity item // o/w find an instance # for us. if (EntityList->tei_instance == Interface->ai_atinst && EntityList->tei_instance != INVALID_ENTITY_INSTANCE) { ATEntity = EntityList; // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - Found our interface %lx at_atinst %lx\n",Interface, Interface->ai_atinst)); } else { MyATBase = MAX(MyATBase, EntityList->tei_instance + 1); } } else { if (EntityList->tei_entity == IF_ENTITY) // if we are already on the list remember our entity item // o/w find an instance # for us. if (EntityList->tei_instance == Interface->ai_ifinst && EntityList->tei_instance != INVALID_ENTITY_INSTANCE) { IFEntity = EntityList; // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - Found our interface %lx ai_ifinst %lx\n",Interface, Interface->ai_ifinst)); } else { MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1); } } if (ATEntity && IFEntity) { break; } } if (ATEntity) { // we are already on the list. // are we going away? if (Interface->ai_operstatus == INTERFACE_UNINIT) { // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - our interface %lx atinst %lx going away \n",Interface, Interface->ai_atinst)); ATEntity->tei_instance = (ULONG) INVALID_ENTITY_INSTANCE; } } else { // we are not on the list. // insert ourself iff we are not going away. if (Interface->ai_operstatus != INTERFACE_UNINIT) { // make sure we have the room for it. if (*Count >= MAX_TDI_ENTITIES) { return FALSE; } Interface->ai_atinst = MyATBase; ATEntity = &EList[*Count]; ATEntity->tei_entity = AT_ENTITY; ATEntity->tei_instance = MyATBase; (*Count)++; // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - adding interface %lx atinst %lx \n",Interface, Interface->ai_atinst)); } } if (IFEntity) { // we are already on the list. // are we going away? if (Interface->ai_operstatus == INTERFACE_UNINIT) { // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - our interface %lx ifinst %lx going away \n",Interface, Interface->ai_ifinst)); IFEntity->tei_instance = (ULONG) INVALID_ENTITY_INSTANCE; } } else { // we are not on the list. // insert ourself iff we are not going away. if (Interface->ai_operstatus != INTERFACE_UNINIT) { // make sure we have the room for it. if (*Count >= MAX_TDI_ENTITIES) { return FALSE; } Interface->ai_ifinst = MyIFBase; IFEntity = &EList[*Count]; IFEntity->tei_entity = IF_ENTITY; IFEntity->tei_instance = MyIFBase; (*Count)++; // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetElist - adding interface %lx ifinst %lx \n",Interface, Interface->ai_ifinst)); } } // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ARPGetEList: arp interface %lx, ai_atinst %lx, ai_ifinst %lx, total %lx\n", // Interface, Interface->ai_atinst, Interface->ai_ifinst, *Count)); return TRUE; } extern uint UseEtherSNAP(PNDIS_STRING Name); extern void GetAlwaysSourceRoute(uint * pArpAlwaysSourceRoute, uint * pIPAlwaysSourceRoute); extern uint GetArpCacheLife(void); extern uint GetArpRetryCount(void); //** InitTaskOffloadHeader - Initializes the task offload header wrt version // and encapsulation, etc. // // All task offload header structure members are initialized. // // Input: // ai - ARPInterface for which we are initializing // the task offload header. // TaskOffloadHeader - Pointer to task offload header to initialize. // Returns: // None. // VOID InitTaskOffloadHeader(ARPInterface *ai, PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader) { TaskOffloadHeader->Version = NDIS_TASK_OFFLOAD_VERSION; TaskOffloadHeader->Size = sizeof(NDIS_TASK_OFFLOAD_HEADER); TaskOffloadHeader->EncapsulationFormat.Flags.FixedHeaderSize = 1; TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize = ai->ai_hdrsize; TaskOffloadHeader->OffsetFirstTask = 0; if (ai->ai_media == NdisMedium802_3) { if (ai->ai_snapsize) { TaskOffloadHeader->EncapsulationFormat.Encapsulation = LLC_SNAP_ROUTED_Encapsulation; TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize += ai->ai_snapsize; } else { TaskOffloadHeader->EncapsulationFormat.Encapsulation = IEEE_802_3_Encapsulation; } } else if (ai->ai_media == NdisMedium802_5) { TaskOffloadHeader->EncapsulationFormat.Encapsulation = IEEE_802_5_Encapsulation; } else { TaskOffloadHeader->EncapsulationFormat.Encapsulation = UNSPECIFIED_Encapsulation; } return; } //**SetOffload - Set offload capabilities // // // All task offload header structure members are initialized. // // Input: // ai - ARPInterface for which we are initializing // the task offload header. // TaskOffloadHeader - Pointer to task offload header to initialize. // Bufsize - length of task offload buffer allocated by teh caller // // Returns: // TRUE - successfully set the offload capability // FALSE - failure case // BOOLEAN SetOffload(ARPInterface *ai,PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader,uint BufSize) { PNDIS_TASK_OFFLOAD tmpoffload; PNDIS_TASK_OFFLOAD TaskOffload, NextTaskOffLoad, LastTaskOffload; NDIS_TASK_IPSEC ipsecCaps; uint TotalLength; NDIS_STATUS Status; uint PrevOffLoad=ai->ai_OffloadFlags; uint PrevIPSecOffLoad=ai->ai_IPSecOffloadFlags; //Parse the buffer for Checksum and tcplargesend offload capabilities KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Something to Offload. offload buffer size %x\n", BufSize)); ASSERT(TaskOffloadHeader->OffsetFirstTask == sizeof(NDIS_TASK_OFFLOAD_HEADER)); TaskOffload = tmpoffload = (NDIS_TASK_OFFLOAD *) ((uchar *) TaskOffloadHeader + TaskOffloadHeader->OffsetFirstTask); if (BufSize >= (TaskOffloadHeader->OffsetFirstTask + sizeof(NDIS_TASK_OFFLOAD))) { while (tmpoffload) { if (tmpoffload->Task == TcpIpChecksumNdisTask) { //Okay we this adapter supports checksum offload //check if tcp and/or ip chksums bits are present PNDIS_TASK_TCP_IP_CHECKSUM ChecksumInfo; ChecksumInfo = (PNDIS_TASK_TCP_IP_CHECKSUM) & tmpoffload->TaskBuffer[0]; //if (ChecksumInfo->V4Transmit.V4Checksum) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"V4 Checksum offload\n")); if (ChecksumInfo->V4Transmit.TcpChecksum) { ai->ai_OffloadFlags |= TCP_XMT_CHECKSUM_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Tcp Checksum offload\n")); } if (ChecksumInfo->V4Transmit.IpChecksum) { ai->ai_OffloadFlags |= IP_XMT_CHECKSUM_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP xmt Checksum offload\n")); } if (ChecksumInfo->V4Receive.TcpChecksum) { ai->ai_OffloadFlags |= TCP_RCV_CHECKSUM_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Tcp Rcv Checksum offload\n")); } if (ChecksumInfo->V4Receive.IpChecksum) { ai->ai_OffloadFlags |= IP_RCV_CHECKSUM_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP rcv Checksum offload\n")); } if (ChecksumInfo->V4Transmit.IpOptionsSupported) { ai->ai_OffloadFlags |= IP_CHECKSUM_OPT_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP Checksum xmt options offload\n")); } if (ChecksumInfo->V4Transmit.TcpOptionsSupported) { ai->ai_OffloadFlags |= TCP_CHECKSUM_OPT_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," TCP Checksum xmt options offload\n")); } } else if ((tmpoffload->Task == TcpLargeSendNdisTask) && (ai->ai_snapsize == 0)) { PNDIS_TASK_TCP_LARGE_SEND TcpLargeSend, in_LargeSend = (PNDIS_TASK_TCP_LARGE_SEND) & tmpoffload->TaskBuffer[0]; TcpLargeSend = &ai->ai_TcpLargeSend; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," Tcp large send!! \n")); TcpLargeSend->MaxOffLoadSize = in_LargeSend->MaxOffLoadSize; TcpLargeSend->MinSegmentCount = in_LargeSend->MinSegmentCount; // // If MaxOffLoadSize is zero, reject this request. // if (TcpLargeSend->MaxOffLoadSize) { ai->ai_OffloadFlags |= TCP_LARGE_SEND_OFFLOAD; // no tcp or ip options when doing large send // Need to reevaluate this as we turn on Time stamp option. if (in_LargeSend->TcpOptions) { ai->ai_OffloadFlags |= TCP_LARGE_SEND_TCPOPT_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," TCP largesend options offload\n")); } if (in_LargeSend->IpOptions) { ai->ai_OffloadFlags |= TCP_LARGE_SEND_IPOPT_OFFLOAD; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL," IP largesend options offload\n")); } } } else if (tmpoffload->Task == IpSecNdisTask) { PNDIS_TASK_IPSEC pIPSecCaps = (PNDIS_TASK_IPSEC) & tmpoffload->TaskBuffer[0]; // // Save off the capabilities for setting them later. // ipsecCaps = *pIPSecCaps; // // CryptoOnly is assumed if we have IpSecNdisTask // ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_CRYPTO_ONLY; // // Do Support first // if (pIPSecCaps->Supported.AH_ESP_COMBINED) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_ESP; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_ESP\n")); } if (pIPSecCaps->Supported.TRANSPORT_TUNNEL_COMBINED) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_TUNNEL; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"TPT_TUNNEL\n")); } if (pIPSecCaps->Supported.V4_OPTIONS) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_V4_OPTIONS; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"V4_OPTIONS\n")); } if (pIPSecCaps->Supported.RESERVED) { if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_ENCAPTYPE_IKE) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_IKE; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_IKE\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TUN_UDPESP_ENCAPTYPE_IKE) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_IKE) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_IKE) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_ENCAPTYPE_OTHER) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_OTHER; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_OTHER\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TUN_UDPESP_ENCAPTYPE_OTHER) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_OTHER) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER\n")); } if (pIPSecCaps->Supported.RESERVED & IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_OTHER) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER\n")); } } // // Do V4AH next // if (pIPSecCaps->V4AH.MD5) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_MD5; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"MD5\n")); } if (pIPSecCaps->V4AH.SHA_1) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_SHA_1; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"SHA\n")); } if (pIPSecCaps->V4AH.Transport) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_TPT; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_TRANSPORT\n")); } if (pIPSecCaps->V4AH.Tunnel) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_TUNNEL; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_TUNNEL\n")); } if (pIPSecCaps->V4AH.Send) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_XMT; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_XMT\n")); } if (pIPSecCaps->V4AH.Receive) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_AH_RCV; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"AH_RCV\n")); } // // Do V4ESP next // if (pIPSecCaps->V4ESP.DES) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_DES; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_DES\n")); } if (pIPSecCaps->V4ESP.RESERVED) { pIPSecCaps->V4ESP.RESERVED = 0; //ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_DES_40; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_DES_40\n")); } if (pIPSecCaps->V4ESP.TRIPLE_DES) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_3_DES; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_3_DES\n")); } if (pIPSecCaps->V4ESP.NULL_ESP) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_NONE; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_NONE\n")); } if (pIPSecCaps->V4ESP.Transport) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_TPT; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_TRANSPORT\n")); } if (pIPSecCaps->V4ESP.Tunnel) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_TUNNEL; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_TUNNEL\n")); } if (pIPSecCaps->V4ESP.Send) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_XMT; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_XMT\n")); } if (pIPSecCaps->V4ESP.Receive) { ai->ai_IPSecOffloadFlags |= IPSEC_OFFLOAD_ESP_RCV; KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"ESP_RCV\n")); } } // Point to the next offload structure if (tmpoffload->OffsetNextTask) { tmpoffload = (PNDIS_TASK_OFFLOAD) ((PUCHAR) tmpoffload + tmpoffload->OffsetNextTask); } else { tmpoffload = NULL; } } //while } else { //if BufSize is not okay KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"response of task offload does not have sufficient space even for 1 offload task!!\n")); return FALSE; } // Enable the capabilities by setting them. if (PrevOffLoad) { ai->ai_OffloadFlags &= PrevOffLoad; } if (PrevIPSecOffLoad) { ai->ai_IPSecOffloadFlags &= PrevIPSecOffLoad; } TaskOffload->Task = 0; TaskOffload->OffsetNextTask = 0; NextTaskOffLoad = LastTaskOffload = TaskOffload; TotalLength = sizeof(NDIS_TASK_OFFLOAD_HEADER); if ((ai->ai_OffloadFlags & TCP_XMT_CHECKSUM_OFFLOAD) || (ai->ai_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) || (ai->ai_OffloadFlags & TCP_RCV_CHECKSUM_OFFLOAD) || (ai->ai_OffloadFlags & IP_RCV_CHECKSUM_OFFLOAD)) { PNDIS_TASK_TCP_IP_CHECKSUM ChksumBuf = (PNDIS_TASK_TCP_IP_CHECKSUM) & NextTaskOffLoad->TaskBuffer[0]; NextTaskOffLoad->Task = TcpIpChecksumNdisTask; NextTaskOffLoad->TaskBufferLength = sizeof(NDIS_TASK_TCP_IP_CHECKSUM); NextTaskOffLoad->OffsetNextTask = FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + NextTaskOffLoad->TaskBufferLength; TotalLength += NextTaskOffLoad->OffsetNextTask; RtlZeroMemory(ChksumBuf, sizeof(NDIS_TASK_TCP_IP_CHECKSUM)); if (ai->ai_OffloadFlags & TCP_XMT_CHECKSUM_OFFLOAD) { ChksumBuf->V4Transmit.TcpChecksum = 1; } if (ai->ai_OffloadFlags & IP_XMT_CHECKSUM_OFFLOAD) { ChksumBuf->V4Transmit.IpChecksum = 1; } if (ai->ai_OffloadFlags & TCP_RCV_CHECKSUM_OFFLOAD) { ChksumBuf->V4Receive.TcpChecksum = 1; } if (ai->ai_OffloadFlags & IP_RCV_CHECKSUM_OFFLOAD) { ChksumBuf->V4Receive.IpChecksum = 1; } // // Enable Options capability if present. // if (ai->ai_OffloadFlags & IP_CHECKSUM_OPT_OFFLOAD) { ChksumBuf->V4Transmit.IpOptionsSupported = 1; } if (ai->ai_OffloadFlags & TCP_CHECKSUM_OPT_OFFLOAD) { ChksumBuf->V4Transmit.TcpOptionsSupported = 1; } LastTaskOffload = NextTaskOffLoad; NextTaskOffLoad = (PNDIS_TASK_OFFLOAD) ((PUCHAR) NextTaskOffLoad + NextTaskOffLoad->OffsetNextTask); } if (ai->ai_OffloadFlags & TCP_LARGE_SEND_OFFLOAD) { PNDIS_TASK_TCP_LARGE_SEND TcpLargeSend, out_LargeSend = (PNDIS_TASK_TCP_LARGE_SEND) & NextTaskOffLoad->TaskBuffer[0]; NextTaskOffLoad->Task = TcpLargeSendNdisTask; NextTaskOffLoad->TaskBufferLength = sizeof(NDIS_TASK_TCP_LARGE_SEND); NextTaskOffLoad->OffsetNextTask = FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + NextTaskOffLoad->TaskBufferLength; TotalLength += NextTaskOffLoad->OffsetNextTask; //(uchar)TaskOffload + sizeof(NDIS_TASK_OFFLOAD) + NextTaskOffload->TaskBufferLength; TcpLargeSend = &ai->ai_TcpLargeSend; RtlZeroMemory(out_LargeSend, sizeof(NDIS_TASK_TCP_LARGE_SEND)); out_LargeSend->MaxOffLoadSize = TcpLargeSend->MaxOffLoadSize; out_LargeSend->MinSegmentCount = TcpLargeSend->MinSegmentCount; if (ai->ai_OffloadFlags & TCP_LARGE_SEND_TCPOPT_OFFLOAD) { out_LargeSend->TcpOptions = 1; } if (ai->ai_OffloadFlags & TCP_LARGE_SEND_IPOPT_OFFLOAD) { out_LargeSend->IpOptions = 1; } LastTaskOffload = NextTaskOffLoad; NextTaskOffLoad = (PNDIS_TASK_OFFLOAD) ((PUCHAR) NextTaskOffLoad + NextTaskOffLoad->OffsetNextTask); } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_CRYPTO_ONLY) { PNDIS_TASK_IPSEC pIPSecCaps = (PNDIS_TASK_IPSEC) & NextTaskOffLoad->TaskBuffer[0]; // // plunk down the advertised capabilities // RtlZeroMemory(pIPSecCaps, sizeof(NDIS_TASK_IPSEC)); NextTaskOffLoad->Task = IpSecNdisTask; NextTaskOffLoad->TaskBufferLength = sizeof(NDIS_TASK_IPSEC); NextTaskOffLoad->OffsetNextTask = (FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) + NextTaskOffLoad->TaskBufferLength); TotalLength += NextTaskOffLoad->OffsetNextTask; if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_ESP) { pIPSecCaps->Supported.AH_ESP_COMBINED = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_TUNNEL) { pIPSecCaps->Supported.TRANSPORT_TUNNEL_COMBINED = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_V4_OPTIONS) { pIPSecCaps->Supported.V4_OPTIONS = 1; } pIPSecCaps->Supported.RESERVED = 0; if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_IKE) { pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_ENCAPTYPE_IKE; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE) { pIPSecCaps->Supported.RESERVED |= IPSEC_TUN_UDPESP_ENCAPTYPE_IKE; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE) { pIPSecCaps->Supported.RESERVED |= IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_IKE; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE) { pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_IKE; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OTHER) { pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_ENCAPTYPE_OTHER; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER) { pIPSecCaps->Supported.RESERVED |= IPSEC_TUN_UDPESP_ENCAPTYPE_OTHER; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER) { pIPSecCaps->Supported.RESERVED |= IPSEC_TPTOVERTUN_UDPESP_ENCAPTYPE_OTHER; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER) { pIPSecCaps->Supported.RESERVED |= IPSEC_TPT_UDPESP_OVER_PURE_TUN_ENCAPTYPE_OTHER; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_MD5) { pIPSecCaps->V4AH.MD5 = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_SHA_1) { pIPSecCaps->V4AH.SHA_1 = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TPT) { pIPSecCaps->V4AH.Transport = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TUNNEL) { pIPSecCaps->V4AH.Tunnel = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_XMT) { pIPSecCaps->V4AH.Send = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_RCV) { pIPSecCaps->V4AH.Receive = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_DES) { pIPSecCaps->V4ESP.DES = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_3_DES) { pIPSecCaps->V4ESP.TRIPLE_DES = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_NONE) { pIPSecCaps->V4ESP.NULL_ESP = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TPT) { pIPSecCaps->V4ESP.Transport = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TUNNEL) { pIPSecCaps->V4ESP.Tunnel = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_XMT) { pIPSecCaps->V4ESP.Send = 1; } if (ai->ai_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_RCV) { pIPSecCaps->V4ESP.Receive = 1; } LastTaskOffload = NextTaskOffLoad; NextTaskOffLoad = (PNDIS_TASK_OFFLOAD) ((PUCHAR) NextTaskOffLoad + NextTaskOffLoad->OffsetNextTask); } LastTaskOffload->OffsetNextTask = 0; // Okay, lets set this now. Status = DoNDISRequest(ai, NdisRequestSetInformation, OID_TCP_TASK_OFFLOAD, TaskOffloadHeader, TotalLength, NULL, TRUE); if (Status != NDIS_STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "Failed to enable indicated offload capabilities!!\n")); ai->ai_OffloadFlags = 0; ai->ai_IPSecOffloadFlags = 0; } return TRUE; } //**QueryOffload - Query offload capabilities // // Input: // ai - ARPInterface for which we are initializing // the task offload header. // Returns: // TRUE/FALSE - Success/Failure to query/set // BOOLEAN QueryAndSetOffload(ARPInterface *ai) { PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader; NDIS_STATUS Status; BOOLEAN stat; uint Needed = 0; uchar *buffer; // Query and set checksum capability TaskOffloadHeader = CTEAllocMemNBoot(sizeof(NDIS_TASK_OFFLOAD_HEADER), '8ICT'); Status = STATUS_BUFFER_OVERFLOW; if (TaskOffloadHeader) { InitTaskOffloadHeader(ai, TaskOffloadHeader); Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_TCP_TASK_OFFLOAD, TaskOffloadHeader, sizeof(NDIS_TASK_OFFLOAD_HEADER), &Needed, TRUE); // Need to initialize Needed to the real size of the buffer. The NDIS // call may not init on success. if (Status == NDIS_STATUS_SUCCESS) { Needed = sizeof(NDIS_TASK_OFFLOAD_HEADER); } else if ((Status == NDIS_STATUS_INVALID_LENGTH) || (Status == NDIS_STATUS_BUFFER_TOO_SHORT)) { // We know the size we need. Allocate a buffer. ASSERT(Needed >= sizeof(NDIS_TASK_OFFLOAD_HEADER)); buffer = CTEAllocMemNBoot(Needed, '9ICT'); KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL, "Calling OID_TCP_TASK_OFFLOAD with %d bytes\n", Needed)); if (buffer != NULL) { CTEFreeMem(TaskOffloadHeader); TaskOffloadHeader = (PNDIS_TASK_OFFLOAD_HEADER) buffer; InitTaskOffloadHeader(ai, TaskOffloadHeader); Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_TCP_TASK_OFFLOAD, buffer, Needed, NULL, TRUE); } } } if ((Status != NDIS_STATUS_SUCCESS) || (TaskOffloadHeader && TaskOffloadHeader->OffsetFirstTask == 0)) { //Make sure that the flag is null. ai->ai_OffloadFlags = 0; ai->ai_IPSecOffloadFlags = 0; if (TaskOffloadHeader) { CTEFreeMem(TaskOffloadHeader); } return FALSE; } if (TaskOffloadHeader) { stat = SetOffload(ai, TaskOffloadHeader, Needed); CTEFreeMem(TaskOffloadHeader); return stat; } return FALSE; } //** ARPRegister - Register a protocol with the ARP module. // // We register a protocol for ARP processing. We also open the // NDIS adapter here. // // Note that much of the information passed in here is unused, as // ARP currently only works with IP. // // Entry: // Adapter - Name of the adapter to bind to. // IPContext - Value to be passed to IP on upcalls. // int ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface) { ARPInterface *ai; // Pointer to interface struct. for this interface. NDIS_STATUS Status, OpenStatus; // Status values. uint i = 0; // Medium index. NDIS_MEDIUM MediaArray[MAX_MEDIA]; uint mss; uint speed; uint MacOpts; uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize; uint OID; uint PF; TRANSPORT_HEADER_OFFSET IPHdrOffset; CTELockHandle LockHandle; UINT MediaType; NDIS_STRING NdisString; DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("+ARPRegister(%x, %x, %x)\n"), Adapter, Flags, Interface)); if ((ai = CTEAllocMemNBoot(sizeof(ARPInterface), '4ICT')) == (ARPInterface *) NULL) return FALSE; // Couldn't allocate memory for this one. *Interface = ai; RtlZeroMemory(ai, sizeof(ARPInterface)); CTEInitTimer(&ai->ai_timer); ai->ai_timerstarted = FALSE; ai->ai_stoptimer = FALSE; MediaArray[MEDIA_DIX] = NdisMedium802_3; MediaArray[MEDIA_TR] = NdisMedium802_5; MediaArray[MEDIA_FDDI] = NdisMediumFddi; MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2; // Initialize this adapter interface structure. ai->ai_operstatus = INTERFACE_INIT; ai->ai_adminstate = IF_STATUS_UNKNOWN; ai->ai_mediastatus = FALSE; ai->ai_lastchange = GetTimeTicks(); ai->ai_bcast = IP_LOCAL_BCST; ai->ai_atinst = ai->ai_ifinst = (UINT)INVALID_ENTITY_INSTANCE; ai->ai_telladdrchng = 1; //Initially let us do try to do network layer address stuff // Initialize the locks. CTEInitLock(&ai->ai_lock); CTEInitLock(&ai->ai_ARPTblLock); GetAlwaysSourceRoute(&sArpAlwaysSourceRoute, &sIPAlwaysSourceRoute); ArpCacheLife = GetArpCacheLife(); if (!ArpCacheLife) { ArpCacheLife = 1; } ArpCacheLife = (ArpCacheLife * 1000L) / ARP_TIMER_TIME; ArpRetryCount = GetArpRetryCount(); if (!ArpMinValidCacheLife) { ArpMinValidCacheLife = 1; } // Allocate the buffer and packet pools. NdisAllocatePacketPoolEx(&Status, &ai->ai_ppool, ARP_DEFAULT_PACKETS, ARP_DEFAULT_PACKETS * 1000, sizeof(struct PCCommon)); if (Status != NDIS_STATUS_SUCCESS) { FreeARPInterface(ai); return FALSE; } // Allocate the ARP table ai->ai_ARPTbl = (ARPTable *) CTEAllocMemNBoot(ARP_TABLE_SIZE * sizeof(ARPTableEntry*), '5ICT'); if (ai->ai_ARPTbl == (ARPTable *) NULL) { FreeARPInterface(ai); return FALSE; } // // NULL out the pointers // RtlZeroMemory(ai->ai_ARPTbl, ARP_TABLE_SIZE * sizeof(ARPTableEntry *)); // Allocate the Counters Structure with best effort in avoiding false sharing ai->ai_qlen = CTEAllocMem(KeNumberProcessors * sizeof(PP_AI_COUNTERS)); if (ai->ai_qlen == (PPP_AI_COUNTERS) NULL) { FreeARPInterface(ai); return FALSE; } RtlZeroMemory(ai->ai_qlen, KeNumberProcessors * sizeof(PP_AI_COUNTERS)); CTEInitBlockStruc(&ai->ai_block); DEBUGMSG(DBG_INFO && DBG_PNP, (DTEXT("ARPRegister calling NdisOpenAdapter\n"))); // Open the NDIS adapter. NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray, MAX_MEDIA, ARPHandle, ai, Adapter, 0, NULL); // Block for open to complete. if (Status == NDIS_STATUS_PENDING) Status = (NDIS_STATUS) CTEBlock(&ai->ai_block); ai->ai_media = MediaArray[i]; // Fill in media type. // Open adapter completed. If it succeeded, we'll finish our intialization. // If it failed, bail out now. if (Status != NDIS_STATUS_SUCCESS) { ai->ai_handle = NULL; FreeARPInterface(ai); return FALSE; } #if FFP_SUPPORT // Store NIC driver handle NdisGetDriverHandle(ai->ai_handle, &ai->ai_driver); #endif // Read the local address. switch (ai->ai_media) { case NdisMedium802_3: addrlen = ARP_802_ADDR_LENGTH; bcastmask = ENET_BCAST_MASK; bcastval = ENET_BCAST_VAL; bcastoff = ENET_BCAST_OFF; OID = OID_802_3_CURRENT_ADDRESS; hdrsize = sizeof(ENetHeader); if (!UseEtherSNAP(Adapter)) { snapsize = 0; } else { snapsize = sizeof(SNAPHeader); } PF = NDIS_PACKET_TYPE_BROADCAST | \ NDIS_PACKET_TYPE_DIRECTED | \ NDIS_PACKET_TYPE_MULTICAST; ai->ai_mediatype = IF_TYPE_IS088023_CSMACD; break; case NdisMedium802_5: addrlen = ARP_802_ADDR_LENGTH; bcastmask = TR_BCAST_MASK; bcastval = TR_BCAST_VAL; bcastoff = TR_BCAST_OFF; OID = OID_802_5_CURRENT_ADDRESS; hdrsize = sizeof(TRHeader); snapsize = sizeof(SNAPHeader); PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; ai->ai_mediatype = IF_TYPE_ISO88025_TOKENRING; break; case NdisMediumFddi: addrlen = ARP_802_ADDR_LENGTH; bcastmask = FDDI_BCAST_MASK; bcastval = FDDI_BCAST_VAL; bcastoff = FDDI_BCAST_OFF; OID = OID_FDDI_LONG_CURRENT_ADDR; hdrsize = sizeof(FDDIHeader); snapsize = sizeof(SNAPHeader); PF = NDIS_PACKET_TYPE_BROADCAST | \ NDIS_PACKET_TYPE_DIRECTED | \ NDIS_PACKET_TYPE_MULTICAST; ai->ai_mediatype = IF_TYPE_FDDI; break; case NdisMediumArcnet878_2: addrlen = 1; bcastmask = ARC_BCAST_MASK; bcastval = ARC_BCAST_VAL; bcastoff = ARC_BCAST_OFF; OID = OID_ARCNET_CURRENT_ADDRESS; hdrsize = sizeof(ARCNetHeader); snapsize = 0; PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; ai->ai_mediatype = IF_TYPE_ARCNET; break; default: ASSERT(0); FreeARPInterface(ai); return FALSE; } ai->ai_bcastmask = bcastmask; ai->ai_bcastval = bcastval; ai->ai_bcastoff = bcastoff; ai->ai_addrlen = addrlen; ai->ai_hdrsize = hdrsize; ai->ai_snapsize = snapsize; ai->ai_pfilter = PF; Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID, ai->ai_addr, addrlen, NULL, TRUE); if (Status != NDIS_STATUS_SUCCESS) { FreeARPInterface(ai); return FALSE; } // Read the maximum frame size. if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL, TRUE)) != NDIS_STATUS_SUCCESS) { FreeARPInterface(ai); return FALSE; } // If this is token ring, figure out the RC len stuff now. mss -= (uint) ai->ai_snapsize; if (ai->ai_media == NdisMedium802_5) { mss -= (sizeof(RC) + (ARP_MAX_RD * sizeof(ushort))); } else { if (ai->ai_media == NdisMediumFddi) { mss = MIN(mss, ARP_FDDI_MSS); } } ai->ai_mtu = (ushort) mss; // Read the speed for local purposes. if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL, TRUE)) == NDIS_STATUS_SUCCESS) { ai->ai_speed = speed * 100L; } // Read and save the options. Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS, &MacOpts, sizeof(MacOpts), NULL, TRUE); if (Status != NDIS_STATUS_SUCCESS) { *Flags = 0; } else { *Flags = (MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0; } if (CTEMemCmp(ai->ai_addr, PPP_HW_ADDR, PPP_HW_ADDR_LEN) == 0) { *Flags = *Flags | LIP_P2P_FLAG; } // // Query the media capability to determine if it is a uni-directional adapter. // Status = DoNDISRequest( ai, NdisRequestQueryInformation, OID_GEN_MEDIA_CAPABILITIES, &MediaType, sizeof(MediaType), NULL, TRUE); // Blocking. if (Status == NDIS_STATUS_SUCCESS) { // Bit field of Rx and Tx. If only Rx, set uni flag. if (MediaType == NDIS_MEDIA_CAP_RECEIVE) { DEBUGMSG(DBG_WARN, (DTEXT("ARPRegister: ai %x: MEDIA_CAP_RX -> UniAdapter!!\n"), ai)); *Flags |= LIP_UNI_FLAG; InterlockedIncrement( (PLONG) &cUniAdapters); } } // Read and store the vendor description string. Status = NdisQueryAdapterInstanceName(&NdisString, ai->ai_handle); if (Status == NDIS_STATUS_SUCCESS) { ANSI_STRING AnsiString; // Convert the string to ANSI, and use the new ANSI string's buffer // to store the description in the ARP interface. // N.B. The conversion results in a nul-terminated string. Status = RtlUnicodeStringToAnsiString(&AnsiString, &NdisString, TRUE); if (Status == STATUS_SUCCESS) { ai->ai_desc = AnsiString.Buffer; ai->ai_desclen = (uint)strlen(AnsiString.Buffer) + 1; } NdisFreeString(NdisString); } if (!ArpEnetHeaderPool || !ArpAuxHeaderPool) { PVOID SectionHandle; // Allocate our small and big buffer pools. Take the interface list // lock simply to protect creating of the buffer pools if we haven't // already done so. We could have used our own lock, but the interface // list lock is global, and not already used in this path. // // This routine is in pageable memory. Since getting the lock // requires writable access to LockHandle at DISPATCH, we need to // lock this code in. // SectionHandle = MmLockPagableCodeSection(ARPRegister); CTEGetLock(&ArpInterfaceListLock.Lock, &LockHandle); if (!ArpEnetHeaderPool) { ArpEnetHeaderPool = MdpCreatePool(BUFSIZE_ENET_HEADER_POOL, 'ehCT'); } if (!ArpAuxHeaderPool) { ArpAuxHeaderPool = MdpCreatePool(BUFSIZE_AUX_HEADER_POOL, 'ahCT'); } CTEFreeLock(&ArpInterfaceListLock.Lock, LockHandle); MmUnlockPagableImageSection(SectionHandle); if (!ArpAuxHeaderPool || !ArpEnetHeaderPool) { FreeARPInterface(ai); return FALSE; } } ai->ai_promiscuous = 0; #if FFP_SUPPORT { FFPVersionParams Version = { NDIS_PROTOCOL_ID_TCP_IP, 0 }; // Initialize all FFP Handling Variables ai->ai_ffpversion = 0; ai->ai_ffplastflush = 0; // Query FFP Handling capabilities Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_FFP_SUPPORT, &Version, sizeof(FFPVersionParams), NULL, TRUE); // Non-Zero Value indicates FFP support if (Version.FFPVersion) { // Set the FFP startup parameters FFPSupportParams Info; Info.NdisProtocolType = NDIS_PROTOCOL_ID_TCP_IP; Info.FastForwardingCacheSize = FFPRegFastForwardingCacheSize; Info.FFPControlFlags = FFPRegControlFlags; // But store away the version first ai->ai_ffpversion = Version.FFPVersion; DoNDISRequest(ai, NdisRequestSetInformation, OID_FFP_SUPPORT, &Info, sizeof(FFPSupportParams), NULL, TRUE); TCPTRACE(("Setting FFP capabilities: Cache Size = %lu, Flags = %08x\n", Info.FastForwardingCacheSize, Info.FFPControlFlags)); } } #endif // if FFP_SUPPORT ai->ai_OffloadFlags = 0; ai->ai_IPSecOffloadFlags = 0; if (DisableTaskOffload) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Taskoffload disabled\n")); } else { if(!QueryAndSetOffload(ai)){ DEBUGMSG(DBG_ERROR, (DTEXT("ARP: Query and set offload failed.\n"))); } } // query the wakeup capabilities. Status = DoNDISRequest( ai, NdisRequestQueryInformation, OID_PNP_CAPABILITIES, &ai->ai_wakeupcap, sizeof(NDIS_PNP_CAPABILITIES), NULL, TRUE); if (Status == NDIS_STATUS_SUCCESS) { uint wakeup = NDIS_PNP_WAKE_UP_PATTERN_MATCH; // enable wakeup capabilities. Status = DoNDISRequest( ai, NdisRequestSetInformation, OID_PNP_ENABLE_WAKE_UP, &wakeup, sizeof(wakeup), NULL, TRUE); if (Status != NDIS_STATUS_SUCCESS) { ai->ai_wakeupcap.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified; } } // Store the device name, we need to pass this to our TDI clients when // we do the PNP notification. if ((ai->ai_devicename.Buffer = CTEAllocMemNBoot(Adapter->MaximumLength, 'aICT')) == NULL) { FreeARPInterface(ai); return FALSE; } RtlCopyMemory(ai->ai_devicename.Buffer, Adapter->Buffer, Adapter->MaximumLength); ai->ai_devicename.Length = Adapter->Length; ai->ai_devicename.MaximumLength = Adapter->MaximumLength; ai->ai_timerstarted = TRUE; IPHdrOffset.HeaderOffset = ai->ai_snapsize + ai->ai_hdrsize; IPHdrOffset.ProtocolType = NDIS_PROTOCOL_ID_TCP_IP; Status = DoNDISRequest(ai, NdisRequestSetInformation, OID_GEN_TRANSPORT_HEADER_OFFSET, &IPHdrOffset, sizeof(TRANSPORT_HEADER_OFFSET), NULL, TRUE); // Everything's set up, so get the ARP timer running. CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai); return TRUE; } #pragma END_INIT //* ARPDynRegister - Dynamically register IP. // // Called by IP when he's about done binding to register with us. Since we // call him directly, we don't save his info here. We do keep his context // and index number. // // Input: See ARPRegister // // Returns: Nothing. // int __stdcall ARPDynRegister( IN PNDIS_STRING Adapter, IN void *IPContext, IN struct _IP_HANDLERS *IpHandlers, OUT struct LLIPBindInfo *Info, IN uint NumIFBound) { ARPInterface *Interface = (ARPInterface *) Info->lip_context; UNREFERENCED_PARAMETER(Adapter); UNREFERENCED_PARAMETER(IpHandlers); Interface->ai_context = IPContext; Interface->ai_index = NumIFBound; // TCPTRACE(("Arp Interface %lx ai_context %lx ai_index %lx\n",Interface, Interface->ai_context, Interface->ai_index)); return TRUE; } //* ARPBindAdapter - Bind and initialize an adapter. // // Called in a PNP environment to initialize and bind an adapter. We open // the adapter and get it running, and then we call up to IP to tell him // about it. IP will initialize, and if all goes well call us back to start // receiving. // // Input: RetStatus - Where to return the status of this call. // BindContext - Handle to use for calling BindAdapterComplete. // AdapterName - Pointer to name of adapter. // SS1 - System specific 1 parameter. // SS2 - System specific 2 parameter. // // Returns: Nothing. // void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2) { uint Flags; // MAC binding flags. ARPInterface *Interface; // Newly created interface. IP_STATUS Status; // State of IPAddInterface call. LLIPBindInfo BindInfo; // Binding information for IP. NDIS_HANDLE Handle; NDIS_STRING IPConfigName; DBG_UNREFERENCED_PARAMETER(BindContext); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("+ARPBindAdapter(%x, %x, %x, %x, %x)\n"), RetStatus, BindContext, AdapterName, SS1, SS2)); if (!OpenIFConfig(SS1, &Handle)) { *RetStatus = NDIS_STATUS_FAILURE; DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: Open failure\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus)); return; } #if !MILLEN if ((*RetStatus = GetIPConfigValue(Handle, &IPConfigName)) != NDIS_STATUS_SUCCESS) { CloseIFConfig(Handle); DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: GetIPConfigValue failure\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus)); return; } #endif // !MILLEN CloseIFConfig(Handle); // First, open the adapter and get the info. if (!ARPRegister(AdapterName, &Flags, &Interface)) { #if !MILLEN CTEFreeMem(IPConfigName.Buffer); #endif // !MILLEN *RetStatus = NDIS_STATUS_FAILURE; DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: ARPRegister failure\n"))); DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus)); return; } // OK, we're opened the adapter. Call IP to tell him about it. BindInfo.lip_context = Interface; BindInfo.lip_transmit = ARPTransmit; BindInfo.lip_transfer = ARPXferData; BindInfo.lip_close = ARPClose; BindInfo.lip_addaddr = ARPAddAddr; BindInfo.lip_deladdr = ARPDeleteAddr; BindInfo.lip_invalidate = ARPInvalidate; BindInfo.lip_open = ARPOpen; BindInfo.lip_qinfo = ARPQueryInfo; BindInfo.lip_setinfo = ARPSetInfo; BindInfo.lip_getelist = ARPGetEList; BindInfo.lip_dondisreq = DoNDISRequest; BindInfo.lip_mss = Interface->ai_mtu; BindInfo.lip_speed = Interface->ai_speed; BindInfo.lip_flags = Flags; BindInfo.lip_addrlen = Interface->ai_addrlen; BindInfo.lip_addr = Interface->ai_addr; BindInfo.lip_dowakeupptrn = DoWakeupPattern; BindInfo.lip_pnpcomplete = ARPPnPComplete; BindInfo.lip_setndisrequest = ARPSetNdisRequest; BindInfo.lip_arpresolveip = ARPResolveIP; BindInfo.lip_arpflushate = ARPFlushATE; BindInfo.lip_arpflushallate = ARPFlushAllATE; #if !MILLEN BindInfo.lip_cancelpackets = ARPCancelPackets; #endif #if FFP_SUPPORT // NDIS Driver Handle, FFP Version are passed up // [ Non zero version implies FFP Support exists ] BindInfo.lip_ffpversion = Interface->ai_ffpversion; BindInfo.lip_ffpdriver = (ULONG_PTR) Interface->ai_driver; #endif //Interface capability is passed on to IP via BindInfo BindInfo.lip_OffloadFlags = Interface->ai_OffloadFlags; BindInfo.lip_IPSecOffloadFlags = Interface->ai_IPSecOffloadFlags; BindInfo.lip_MaxOffLoadSize = (uint) Interface->ai_TcpLargeSend.MaxOffLoadSize; BindInfo.lip_MaxSegments = (uint) Interface->ai_TcpLargeSend.MinSegmentCount; BindInfo.lip_closelink = NULL; BindInfo.lip_pnpcap = Interface->ai_wakeupcap.Flags; DEBUGMSG(DBG_INFO && DBG_PNP, (DTEXT("ARPBindAdapter calling IPAddInterface.\n"))); Status = IPAddInterface(AdapterName, NULL, #if MILLEN (PNDIS_STRING) SS1, #else // MILLEN (PNDIS_STRING) & IPConfigName, #endif // !MILLEN SS2, Interface, ARPDynRegister, &BindInfo, 0, Interface->ai_mediatype, IF_ACCESS_BROADCAST, IF_CONNECTION_DEDICATED); #if !MILLEN CTEFreeMem(IPConfigName.Buffer); #endif // !MILLEN if (Status != IP_SUCCESS) { // Need to close the binding. FreeARPInterface will do that, as well // as freeing resources. DEBUGMSG(DBG_ERROR && DBG_PNP, (DTEXT("ARPBindAdapter: IPAddInterface failure %x\n"), Status)); FreeARPInterface(Interface); *RetStatus = NDIS_STATUS_FAILURE; } else { // // Insert into ARP IF list // ExInterlockedInsertTailList(&ArpInterfaceList, &Interface->ai_linkage, &ArpInterfaceListLock.Lock); *RetStatus = NDIS_STATUS_SUCCESS; } DEBUGMSG(DBG_TRACE && DBG_PNP, (DTEXT("-ARPBindAdapter [%x]\n"), *RetStatus)); } //* ARPUnbindAdapter - Unbind from an adapter. // // Called when we need to unbind from an adapter. We'll call up to IP to tell // him. When he's done, we'll free our memory and return. // // Input: RetStatus - Where to return status from call. // ProtBindContext - The context we gave NDIS earlier - really a // pointer to an ARPInterface structure. // UnbindContext - Context for completeing this request. // // Returns: Nothing. // void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext) { ARPInterface *Interface = (ARPInterface *) ProtBindContext; NDIS_STATUS Status; // Status of close call. CTELockHandle LockHandle; // Shut him up, so we don't get any more frames. Interface->ai_pfilter = 0; if (Interface->ai_handle != NULL) { DoNDISRequest(Interface, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), NULL, TRUE); } CTEInitBlockStrucEx(&Interface->ai_timerblock); Interface->ai_stoptimer = TRUE; // Mark him as down. Interface->ai_adminstate = IF_STATUS_DOWN; ARPUpdateOperStatus(Interface); // Mark the interface as going away so it will disappear from the // entity list. Interface->ai_operstatus = INTERFACE_UNINIT; #if FFP_SUPPORT // Stop FFP on this interface Interface->ai_ffpversion = 0; #endif KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Flushing all ates %x\n", Interface)); ARPFlushAllATE(Interface); // Now tell IP he's gone. We need to make sure that we don't tell him twice. // To do this we set the context to NULL after we tell him the first time, // and we check to make sure it's non-NULL before notifying him. if (Interface->ai_context != NULL) { IPDelInterface(Interface->ai_context, TRUE); Interface->ai_context = NULL; } // Finally, close him. We do this here so we can return a valid status. CTEGetLock(&Interface->ai_lock, &LockHandle); if (Interface->ai_handle != NULL) { NDIS_HANDLE Handle = Interface->ai_handle; CTEFreeLock(&Interface->ai_lock, LockHandle); CTEInitBlockStruc(&Interface->ai_block); NdisCloseAdapter(&Status, Handle); // Block for close to complete. if (Status == NDIS_STATUS_PENDING) { Status = (NDIS_STATUS) CTEBlock(&Interface->ai_block); } Interface->ai_handle = NULL; } else { CTEFreeLock(&Interface->ai_lock, LockHandle); Status = NDIS_STATUS_SUCCESS; } //Check if are called from ARPUnload if ((ARPInterface *) UnbindContext != Interface) { CTELockHandle Handle; //No. Acquire lock and remove entry. CTEGetLock(&ArpInterfaceListLock.Lock, &Handle); RemoveEntryList(&Interface->ai_linkage); CTEFreeLock(&ArpInterfaceListLock.Lock, Handle); } *RetStatus = Status; if (Status == NDIS_STATUS_SUCCESS) { FreeARPInterface(Interface); } } extern ulong VIPTerminate; //* ARPUnloadProtocol - Unload. // // Called when we need to unload. All we do is call up to IP, and return. // // Input: Nothing. // // Returns: Nothing. // void NDIS_API ARPUnloadProtocol(void) { #if MILLEN DEBUGMSG(1, (DTEXT("ARPUnloadProtocol called! What to do???\n"))); #endif // MILLEN } VOID ArpUnload(IN PDRIVER_OBJECT DriverObject) /*++ Routine Description: This routine unloads the TCPIP stack. It unbinds from any NDIS drivers that are open and frees all resources associated with the transport. The I/O system will not call us until nobody above has IPX open. NOTE: Also, since other ARP modules depend on IP, they are unloaded before out unload handler is called. We concern ourselves with the LAN arp only at this point Arguments: DriverObject - Pointer to driver object created by the system. Return Value: None. When the function returns, the driver is unloaded. --*/ { PLIST_ENTRY pEntry; CTELockHandle LockHandle; NDIS_STATUS status; ARPInterface *Interface; // // Walk the list of opened ARP interfaces, issuing // PnP deletes on each in turn. // CTEGetLock(&ArpInterfaceListLock.Lock, &LockHandle); while(!IsListEmpty(&ArpInterfaceList)) { pEntry = ArpInterfaceList.Flink; Interface = STRUCT_OF(ARPInterface, pEntry, ai_linkage); RemoveEntryList(&Interface->ai_linkage); CTEFreeLock(&ArpInterfaceListLock.Lock, LockHandle); // KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_INFO_LEVEL,"Issuing unbind on %lx\n", Interface)); ARPUnbindAdapter(&status, Interface, Interface); CTEGetLock(&ArpInterfaceListLock.Lock, &LockHandle); } CTEFreeLock(&ArpInterfaceListLock.Lock, LockHandle); MdpDestroyPool(ArpEnetHeaderPool); MdpDestroyPool(ArpAuxHeaderPool); // // Deal with any residual events/timers // Only one timer sits at this layer: ai_timer, which is stopped // on the unbind above. // // // call into IP so it can cleanup. // IPUnload(DriverObject); }