From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/tdi/tcpip/ip/arp.c | 4839 +++++++++++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/arp.h | 21 + private/ntos/tdi/tcpip/ip/arpdef.h | 340 +++ private/ntos/tdi/tcpip/ip/dirs | 22 + private/ntos/tdi/tcpip/ip/icmp.c | 1698 ++++++++++++ private/ntos/tdi/tcpip/ip/icmp.h | 70 + private/ntos/tdi/tcpip/ip/igmp.c | 799 ++++++ private/ntos/tdi/tcpip/ip/igmp.h | 42 + private/ntos/tdi/tcpip/ip/info.c | 609 +++++ private/ntos/tdi/tcpip/ip/info.h | 22 + private/ntos/tdi/tcpip/ip/init.c | 3509 ++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/ipdef.h | 371 +++ private/ntos/tdi/tcpip/ip/ipinit.h | 155 ++ private/ntos/tdi/tcpip/ip/iploop.c | 665 +++++ private/ntos/tdi/tcpip/ip/iprcv.c | 1145 ++++++++ private/ntos/tdi/tcpip/ip/iproute.c | 4703 ++++++++++++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/iproute.h | 107 + private/ntos/tdi/tcpip/ip/iprtdef.h | 135 + private/ntos/tdi/tcpip/ip/ipstatus.c | 205 ++ private/ntos/tdi/tcpip/ip/ipxmit.c | 1949 +++++++++++++ private/ntos/tdi/tcpip/ip/ipxmit.h | 34 + private/ntos/tdi/tcpip/ip/mp/makefile | 6 + private/ntos/tdi/tcpip/ip/mp/sources | 27 + private/ntos/tdi/tcpip/ip/ntip.c | 3361 +++++++++++++++++++++++ private/ntos/tdi/tcpip/ip/ntirp.c | 1458 ++++++++++ private/ntos/tdi/tcpip/ip/ntreg.c | 672 +++++ private/ntos/tdi/tcpip/ip/sources.inc | 59 + private/ntos/tdi/tcpip/ip/up/makefile | 6 + private/ntos/tdi/tcpip/ip/up/sources | 27 + 29 files changed, 27056 insertions(+) create mode 100644 private/ntos/tdi/tcpip/ip/arp.c create mode 100644 private/ntos/tdi/tcpip/ip/arp.h create mode 100644 private/ntos/tdi/tcpip/ip/arpdef.h create mode 100644 private/ntos/tdi/tcpip/ip/dirs create mode 100644 private/ntos/tdi/tcpip/ip/icmp.c create mode 100644 private/ntos/tdi/tcpip/ip/icmp.h create mode 100644 private/ntos/tdi/tcpip/ip/igmp.c create mode 100644 private/ntos/tdi/tcpip/ip/igmp.h create mode 100644 private/ntos/tdi/tcpip/ip/info.c create mode 100644 private/ntos/tdi/tcpip/ip/info.h create mode 100644 private/ntos/tdi/tcpip/ip/init.c create mode 100644 private/ntos/tdi/tcpip/ip/ipdef.h create mode 100644 private/ntos/tdi/tcpip/ip/ipinit.h create mode 100644 private/ntos/tdi/tcpip/ip/iploop.c create mode 100644 private/ntos/tdi/tcpip/ip/iprcv.c create mode 100644 private/ntos/tdi/tcpip/ip/iproute.c create mode 100644 private/ntos/tdi/tcpip/ip/iproute.h create mode 100644 private/ntos/tdi/tcpip/ip/iprtdef.h create mode 100644 private/ntos/tdi/tcpip/ip/ipstatus.c create mode 100644 private/ntos/tdi/tcpip/ip/ipxmit.c create mode 100644 private/ntos/tdi/tcpip/ip/ipxmit.h create mode 100644 private/ntos/tdi/tcpip/ip/mp/makefile create mode 100644 private/ntos/tdi/tcpip/ip/mp/sources create mode 100644 private/ntos/tdi/tcpip/ip/ntip.c create mode 100644 private/ntos/tdi/tcpip/ip/ntirp.c create mode 100644 private/ntos/tdi/tcpip/ip/ntreg.c create mode 100644 private/ntos/tdi/tcpip/ip/sources.inc create mode 100644 private/ntos/tdi/tcpip/ip/up/makefile create mode 100644 private/ntos/tdi/tcpip/ip/up/sources (limited to 'private/ntos/tdi/tcpip/ip') diff --git a/private/ntos/tdi/tcpip/ip/arp.c b/private/ntos/tdi/tcpip/ip/arp.c new file mode 100644 index 000000000..3c8253566 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arp.c @@ -0,0 +1,4839 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** arp.c - ARP VxD routines. +// +// This file containes all of the ARP related routines, including +// table lookup, registration, etc. +// +// ARP is architected to support multiple protocols, but for now +// it in only implemented to take one protocol (IP). This is done +// for simplicity and ease of implementation. In the future we may +// split ARP out into a seperate driver. + +#include "oscfg.h" +#ifdef VXD +#include +#endif +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "ipdef.h" +#include "llipif.h" +#include "arp.h" +#include "arpdef.h" +#include "tdiinfo.h" +#include "ipinfo.h" +#include "llinfo.h" +#include "tdistat.h" +#include "iproute.h" +#include "iprtdef.h" +#include "arpinfo.h" +#include "ipinit.h" + +#ifndef CHICAGO +#ifndef _PNP_POWER +#define NDIS_MAJOR_VERSION 0x03 +#define NDIS_MINOR_VERSION 0 +#else +#define NDIS_MAJOR_VERSION 0x04 +#define NDIS_MINOR_VERSION 0 +#endif +#endif + +#ifndef NDIS_API +#define NDIS_API +#endif + + +static ulong ARPLookahead = LOOKAHEAD_SIZE; + +static uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06"; +static uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70"; +static uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00"; +static uchar ARCBcst[] = "\x00\x00\xd5"; + +static uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00"; +static uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00"; +static uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06"; + +#ifdef NT +static WCHAR ARPName[] = TCP_NAME; +#else // NT +static uchar ARPName[] = TCP_NAME; +#endif // NT + +NDIS_HANDLE ARPHandle; // Our NDIS protocol handle. + +uint ArpCacheLife; +uint sArpAlwaysSourceRoute; // True if we always send ARP requests + // with source route info on token ring. +uint sIPAlwaysSourceRoute; +extern uchar TrRii; + +extern PDRIVER_OBJECT IPDriverObject; + +extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint); +extern void IPTDComplete(void *, PNDIS_PACKET, NDIS_STATUS, uint); +extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS); +extern void IPStatus(void *, NDIS_STATUS, void *, uint); +extern void IPRcvComplete(void); +extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset); + +extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS); +extern void IPULUnloadNotify(void); + +#ifdef _PNP_POWER +extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNP, + void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo); +extern void IPDelInterface(void *Context); + +extern void NotifyOfUnload(void); + + +extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle); +extern int IsLLInterfaceValueNull (NDIS_HANDLE Handle) ; +extern void CloseIFConfig(NDIS_HANDLE Handle); + +#endif + + +// Tables for bitswapping. + +uchar SwapTableLo[] = { + 0, // 0 + 0x08, // 1 + 0x04, // 2 + 0x0c, // 3 + 0x02, // 4 + 0x0a, // 5, + 0x06, // 6, + 0x0e, // 7, + 0x01, // 8, + 0x09, // 9, + 0x05, // 10, + 0x0d, // 11, + 0x03, // 12, + 0x0b, // 13, + 0x07, // 14, + 0x0f // 15 +}; + +uchar SwapTableHi[] = { + 0, // 0 + 0x80, // 1 + 0x40, // 2 + 0xc0, // 3 + 0x20, // 4 + 0xa0, // 5, + 0x60, // 6, + 0xe0, // 7, + 0x10, // 8, + 0x90, // 9, + 0x50, // 10, + 0xd0, // 11, + 0x30, // 12, + 0xb0, // 13, + 0x70, // 14, + 0xf0 // 15 +}; + +// Table of source route maximum I-field lengths for token ring. +ushort IFieldSize[] = { + 516, + 1500, + 2052, + 4472, + 8191 +}; + +#define LF_BIT_SHIFT 4 +#define MAX_LF_BITS 4 + +#ifdef NT +#ifdef ALLOC_PRAGMA +// +// Disposable init code. +// +void FreeARPInterface(ARPInterface *Interface); +void ARPOpen(void *Context); + +#pragma alloc_text(INIT, ARPInit) +#ifndef _PNP_POWER +#pragma alloc_text(INIT, FreeARPInterface) +#pragma alloc_text(INIT, ARPOpen) +#pragma alloc_text(INIT, ARPRegister) +#else +#pragma alloc_text(PAGE, ARPOpen) +#pragma alloc_text(PAGE, ARPRegister) + +#endif + +// +// Paged code +// +void NotifyConflictProc(CTEEvent *Event, void *Context); + +#pragma alloc_text(PAGE, NotifyConflictProc) + +#endif // ALLOC_PRAGMA +#endif // NT + +#ifdef VXD +extern void EnableInts(void); +#endif + +//* DoNDISRequest - Submit a request to an NDIS driver. +// +// This is a utility routine to submit a general request to an NDIS +// driver. The caller specifes the request code (OID), a buffer and +// a length. This routine allocates a request structure, +// fills it in, and submits the request. +// +// Entry: +// Adapter - A pointer to the ARPInterface adapter structure. +// Request - Type of request to be done (Set or Query) +// OID - Value to be set/queried. +// Info - A pointer to the buffer to be passed. +// Length - Length of data in the buffer. +// Needed - On return, filled in with bytes needed in buffer. +// +// Exit: +// +NDIS_STATUS +DoNDISRequest(ARPInterface *Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID, + void *Info, uint Length, uint *Needed) +{ + NDIS_REQUEST Request; // Request structure we'll use. + NDIS_STATUS Status; + + // Now fill it in. + Request.RequestType = RT; + if (RT == NdisRequestSetInformation) { + Request.DATA.SET_INFORMATION.Oid = OID; + Request.DATA.SET_INFORMATION.InformationBuffer = Info; + Request.DATA.SET_INFORMATION.InformationBufferLength = Length; + } else { + Request.DATA.QUERY_INFORMATION.Oid = OID; + Request.DATA.QUERY_INFORMATION.InformationBuffer = Info; + Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length; + } + + // Initialize the block structure. + CTEInitBlockStruc(&Adapter->ai_block); +#ifdef VXD + EnableInts(); +#endif + + // Submit the request. + NdisRequest(&Status, Adapter->ai_handle, &Request); + + // Wait for it to finish + if (Status == NDIS_STATUS_PENDING) + Status = (NDIS_STATUS)CTEBlock(&Adapter->ai_block); + + if (Needed != NULL) + *Needed = Request.DATA.QUERY_INFORMATION.BytesNeeded; + + return Status; +} +//* FreeARPBuffer - Free a header and buffer descriptor pair. +// +// Called when we're done with a buffer. We'll free the buffer and the +// buffer descriptor pack to the interface. +// +// Entry: Interface - Interface buffer/bd came frome. +// Buffer - NDIS_BUFFER to be freed. +// +// Returns: Nothing. +// +void +FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer) +{ + CTELockHandle lhandle; + uchar **Header; // header buffer to be freed. + uint Size; + + Size = NdisBufferLength(Buffer); + + if (Size <= Interface->ai_sbsize) { +#ifdef VXD + // A small buffer, put him on the list. + NDIS_BUFFER_LINKAGE(Buffer) = Interface->ai_sblist; + Interface->ai_sblist = Buffer; +#else + ExInterlockedPushEntrySList( + &Interface->ai_sblist, + STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next), + &Interface->ai_lock + ); + +#endif + + return; + } else { + // A big buffer. Get the buffer pointer, link it on, and free the + // NDIS buffer. + Header = (uchar **)NdisBufferVirtualAddress(Buffer); + + CTEGetLock(&Interface->ai_lock, &lhandle); + *Header = Interface->ai_bblist; + Interface->ai_bblist = (uchar *)Header; + CTEFreeLock(&Interface->ai_lock, lhandle); + + NdisFreeBuffer(Buffer); + } +} + +//* GrowARPHeaders - Grow the ARP header buffer list. +// +// Called when we need to grow the ARP header buffer list. Called with the +// interface lock held. +// +// Input: Interface - Interface on which to grow. +// +// Returns: Pointer to newly allocated buffer, or NULL. +// +PNDIS_BUFFER +GrowARPHeaders(ARPInterface *Interface) +{ + ARPBufferTracker *NewTracker; + PNDIS_BUFFER Buffer, ReturnBuffer; + uchar *Header; + uint i; + NDIS_STATUS Status; + CTELockHandle Handle; + + CTEGetLock(&Interface->ai_lock, &Handle); + + // Make sure we're allowed to allocate. + if (Interface->ai_curhdrs >= Interface->ai_maxhdrs) + goto failure; + + NewTracker = CTEAllocMem(sizeof(ARPBufferTracker)); + if (NewTracker == NULL) + goto failure; // We're out of memory. + + NdisAllocateBufferPool(&Status, &NewTracker->abt_handle, + ARP_HDRBUF_GROW_SIZE); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(NewTracker); + goto failure; + } + + Header = CTEAllocMem((uint)Interface->ai_sbsize * ARP_HDRBUF_GROW_SIZE); + if (Header == NULL) { + NdisFreeBufferPool(NewTracker->abt_handle); + CTEFreeMem(NewTracker); + goto failure; + } + + // Got the resources we need, allocate the buffers. + NewTracker->abt_buffer = Header; + NewTracker->abt_next = Interface->ai_buflist; + Interface->ai_buflist = NewTracker; + ReturnBuffer = NULL; + Interface->ai_curhdrs += ARP_HDRBUF_GROW_SIZE; + CTEFreeLock(&Interface->ai_lock, Handle); + + for (i = 0; i < ARP_HDRBUF_GROW_SIZE; i++) { + NdisAllocateBuffer(&Status, &Buffer, NewTracker->abt_handle, + Header + (i * Interface->ai_sbsize), Interface->ai_sbsize); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); + break; + } + if (i != 0) { + FreeARPBuffer(Interface, Buffer); + } else + ReturnBuffer = Buffer; + } + + // Update for what we didn't allocate, if any. + CTEInterlockedAddUlong(&Interface->ai_curhdrs, i - ARP_HDRBUF_GROW_SIZE, + &Interface->ai_lock); + + return ReturnBuffer; + +failure: + CTEFreeLock(&Interface->ai_lock, Handle); + return NULL; +} + +//* GetARPBuffer - Get a buffer and descriptor +// +// Returns a pointer to an NDIS_BUFFER and a pointer to a buffer +// of the specified size. +// +// Entry: Interface - Pointer to ARPInterface structure to allocate buffer from. +// BufPtr - Pointer to where to return buf address. +// Size - Size in bytes of buffer needed. +// +// Returns: Pointer to NDIS_BUFFER if successfull, NULL if not +// +PNDIS_BUFFER +GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar size) +{ + CTELockHandle lhandle; // Lock handle + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; // NDIS buffer allocated. + + if (size <= Interface->ai_sbsize) { +#ifdef VXD + Buffer = Interface->ai_sblist; + if (Buffer != NULL) { + Interface->ai_sblist = NDIS_BUFFER_LINKAGE(Buffer); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + NdisBufferLength(Buffer) = size; + *BufPtr = NdisBufferVirtualAddress(Buffer); + return Buffer; +#else + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList( + &Interface->ai_sblist, + &Interface->ai_lock + ); + if (BufferLink != NULL) { + Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + NdisBufferLength(Buffer) = size; + *BufPtr = NdisBufferVirtualAddress(Buffer); + return Buffer; +#endif + + } else { + Buffer = GrowARPHeaders(Interface); + if (Buffer != NULL) { + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + NdisBufferLength(Buffer) = size; + *BufPtr = NdisBufferVirtualAddress(Buffer); + } + return Buffer; + } + } else { + // Need a 'big' buffer. + CTEGetLock(&Interface->ai_lock, &lhandle); + if ((*BufPtr = Interface->ai_bblist) != (uchar *)NULL) { + Interface->ai_bblist = *(uchar **)*BufPtr; + CTEFreeLock(&Interface->ai_lock, lhandle); // Got a buffer. + NdisAllocateBuffer(&Status, &Buffer, Interface->ai_bpool, *BufPtr, + size); + if (Status == NDIS_STATUS_SUCCESS) + return Buffer; + else { // Couldn't get NDIS buffer, free our buffer. + CTEGetLock(&Interface->ai_lock, &lhandle); + *(uchar **)&**BufPtr = Interface->ai_bblist; + Interface->ai_bblist = *BufPtr; + CTEFreeLock(&Interface->ai_lock, lhandle); + return (PNDIS_BUFFER)NULL; + } + } + + // Couldn't get a header buffer, free lock and return NULL. + CTEFreeLock(&Interface->ai_lock, lhandle); + return (PNDIS_BUFFER)NULL; + } +} + + +//* BitSwap - Bit swap two strings. +// +// A routine to bitswap two strings. +// +// Input: Dest - Destination of swap. +// Src - Src string to be swapped. +// Length - Length in bytes to swap. +// +// Returns: Nothing. +// +void +BitSwap(uchar *Dest, uchar *Src, uint Length) +{ + uint i; + uchar Temp, TempSrc; + + for (i = 0; i < Length; i++, Dest++, Src++) { + TempSrc = *Src; + Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f]; + *Dest = Temp; + } + +} + + +//* SendARPPacket - Build a header, and send a packet. +// +// A utility routine to build and ARP header and send a packet. We assume +// the media specific header has been built. +// +// Entry: Interface - Interface for NDIS drive. +// Packet - Pointer to packet to be sent +// Header - Pointer to header to fill in. +// Opcode - Opcode for packet. +// Address - Source HW address. +// SrcAddr - Address to use as our source h/w address. +// Destination - Destination IP address. +// Src - Source IP address. +// HWType - Hardware type. +// CheckIF - TRUE iff we are to check the I/F status before +// sending. +// +// Returns: NDIS_STATUS of send. +// +NDIS_STATUS +SendARPPacket(ARPInterface *Interface, PNDIS_PACKET Packet, ARPHeader *Header, ushort Opcode, + uchar *Address, uchar *SrcAddr, IPAddr Destination, IPAddr Src, + ushort HWType, uint CheckIF) +{ + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; + uint PacketDone; + uchar *AddrPtr; + + Header->ah_hw = HWType; + Header->ah_pro = net_short(ARP_ETYPE_IP); + Header->ah_hlen = Interface->ai_addrlen; + Header->ah_plen = sizeof(IPAddr); + Header->ah_opcode = Opcode; + AddrPtr = Header->ah_shaddr; + + if (SrcAddr == NULL) + SrcAddr = Interface->ai_addr; + + CTEMemCopy(AddrPtr, SrcAddr, Interface->ai_addrlen); + + AddrPtr += Interface->ai_addrlen; + *(IPAddr UNALIGNED *)AddrPtr = Src; + AddrPtr += sizeof(IPAddr); + + if (Address != (uchar *)NULL) + CTEMemCopy(AddrPtr, Address, Interface->ai_addrlen); + else + CTEMemSet(AddrPtr, 0, Interface->ai_addrlen); + + AddrPtr += Interface->ai_addrlen; + *(IPAddr UNALIGNED *)AddrPtr = Destination; + + PacketDone = FALSE; + + if (!CheckIF || Interface->ai_state == INTERFACE_UP) { + + Interface->ai_qlen++; + NdisSend(&Status, Interface->ai_handle, Packet); + + if (Status != NDIS_STATUS_PENDING) { + PacketDone = TRUE; + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + if (Status == NDIS_STATUS_SUCCESS) + Interface->ai_outoctets += Packet->Private.TotalLength; + else { + if (Status == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + } + } else { + PacketDone = TRUE; + Status = NDIS_STATUS_ADAPTER_NOT_READY; + } + + if (PacketDone) { + NdisUnchainBufferAtFront(Packet, &Buffer); + FreeARPBuffer(Interface, Buffer); + NdisFreePacket(Packet); + } + return Status; +} + +//* SendARPRequest - Send an ARP packet +// +// Called when we need to ARP an IP address, or respond to a request. We'll send out +// the packet, and the receiving routines will process the response. +// +// Entry: Interface - Interface to send the request on. +// Destination - The IP address to be ARPed. +// Type - Either RESOLVING_GLOBAL or RESOLVING_LOCAL +// SrcAddr - NULL if we're sending from ourselves, the value +// to use otherwise. +// CheckIF - Flag passed through to SendARPPacket(). +// +// Returns: Status of attempt to send ARP request. +// +NDIS_STATUS +SendARPRequest(ARPInterface *Interface, IPAddr Destination, uchar Type, + uchar *SrcAddr, uint CheckIF) +{ + uchar *MHeader; // Pointer to media header. + PNDIS_BUFFER Buffer; // NDIS buffer descriptor. + uchar MHeaderSize; // Size of media header. + uchar *MAddr; // Pointer to media address structure. + uint SAddrOffset; // Offset into media address of source address. + uchar SRFlag = 0; // Source routing flag. + uchar SNAPLength = 0; + uchar *SNAPAddr; // Address of SNAP header. + PNDIS_PACKET Packet; // Packet for sending. + NDIS_STATUS Status; + ushort HWType; + IPAddr Src; + CTELockHandle Handle; + ARPIPAddr *Addr; + + // First, get a source address we can use. + CTEGetLock(&Interface->ai_lock, &Handle); + Addr = &Interface->ai_ipaddr; + Src = NULL_IP_ADDR; + do { + if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { + // + // This is a valid address. See if it is the same as the + // target address - i.e. arp'ing for ourselves. If it is, + // we want to use that as our source address. + // + if (IP_ADDR_EQUAL(Addr->aia_addr, Destination)) { + Src = Addr->aia_addr; + break; + } + + // See if the target is on this subnet. + if (IP_ADDR_EQUAL( + Addr->aia_addr & Addr->aia_mask, + Destination & Addr->aia_mask + )) + { + // + // See if we've already found a suitable candidate on the + // same subnet. If we haven't, we'll use this one. + // + if (!IP_ADDR_EQUAL( + Addr->aia_addr & Addr->aia_mask, + Src & Addr->aia_mask + )) + { + Src = Addr->aia_addr; + } + } + else { + // He's not on our subnet. If we haven't already found a valid + // address save this one in case we don't find a match for the + // subnet. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { + Src = Addr->aia_addr; + } + } + } + + Addr = Addr->aia_next; + + } while (Addr != NULL); + + CTEFreeLock(&Interface->ai_lock, Handle); + + // If we didn't find a source address, give up. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) + return NDIS_STATUS_SUCCESS; + + NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool); + if (Status != NDIS_STATUS_SUCCESS) { + Interface->ai_outdiscards++; + return Status; + } + + ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK; + (Interface->ai_outpcount[AI_NONUCAST_INDEX])++; + + // Figure out what type of media this is, and do the appropriate thing. + switch (Interface->ai_media) { + case NdisMedium802_3: + MHeaderSize = ARP_MAX_MEDIA_ENET; + MAddr = ENetBcst; + if (Interface->ai_snapsize == 0) { + SNAPAddr = (uchar *)NULL; + HWType = net_short(ARP_HW_ENET); + } else { + SNAPLength = sizeof(SNAPHeader); + SNAPAddr = ARPSNAP; + HWType = net_short(ARP_HW_802); + } + + SAddrOffset = offsetof(struct ENetHeader, eh_saddr); + break; + case NdisMedium802_5: + // Token ring. We have logic for dealing with the second transmit + // of an arp request. + MAddr = TRBcst; + SAddrOffset = offsetof(struct TRHeader, tr_saddr); + SNAPLength = sizeof(SNAPHeader); + SNAPAddr = ARPSNAP; + MHeaderSize = sizeof(TRHeader); + HWType = net_short(ARP_HW_802); + if (Type == ARP_RESOLVING_GLOBAL) { + MHeaderSize += sizeof(RC); + SRFlag = TR_RII; + } + break; + case NdisMediumFddi: + MHeaderSize = sizeof(FDDIHeader); + MAddr = FDDIBcst; + SNAPAddr = ARPSNAP; + SNAPLength = sizeof(SNAPHeader); + SAddrOffset = offsetof(struct FDDIHeader, fh_saddr); + HWType = net_short(ARP_HW_ENET); + break; + case NdisMediumArcnet878_2: + MHeaderSize = ARP_MAX_MEDIA_ARC; + MAddr = ARCBcst; + SNAPAddr = (uchar *)NULL; + SAddrOffset = offsetof(struct ARCNetHeader, ah_saddr); + HWType = net_short(ARP_HW_ARCNET); + break; + default: + DEBUGCHK; + Interface->ai_outerrors++; + return NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + + + if ((Buffer = GetARPBuffer(Interface, &MHeader, + (uchar)(sizeof(ARPHeader) + MHeaderSize + SNAPLength))) == (PNDIS_BUFFER)NULL) { + NdisFreePacket(Packet); + Interface->ai_outdiscards++; + return NDIS_STATUS_RESOURCES; + } + + if (Interface->ai_media == NdisMediumArcnet878_2) + NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT; + + // Copy broadcast address into packet. + CTEMemCopy(MHeader, MAddr, MHeaderSize); + // Fill in source address. + if (SrcAddr == NULL) { + SrcAddr = Interface->ai_addr; + } + + if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize != 0) { + ENetHeader *Hdr = (ENetHeader *)MHeader; + + // Using SNAP on ethernet. Adjust the etype to a length. + Hdr->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader)); + } + + CTEMemCopy(&MHeader[SAddrOffset], SrcAddr, Interface->ai_addrlen); + if ((Interface->ai_media == NdisMedium802_5) && (Type == ARP_RESOLVING_GLOBAL)) { + // Turn on source routing. + MHeader[SAddrOffset] |= SRFlag; + MHeader[SAddrOffset + Interface->ai_addrlen] |= TrRii; + } + // Copy in SNAP header, if any. + CTEMemCopy(&MHeader[MHeaderSize], SNAPAddr, SNAPLength); + + // Media header is filled in. Now do ARP packet itself. + NdisChainBufferAtFront(Packet, Buffer); + return SendARPPacket(Interface, Packet,(ARPHeader *)&MHeader[MHeaderSize + SNAPLength], + net_short(ARP_REQUEST), (uchar *)NULL, SrcAddr, Destination, Src, + HWType, CheckIF); +} + +//* SendARPReply - Reply to an ARP request. +// +// Called by our receive packet handler when we need to reply. We build a packet +// and buffer and call SendARPPacket to send it. +// +// Entry: Interface - Pointer to interface to reply on. +// Destination - IPAddress to reply to. +// Src - Source address to reply from. +// HWAddress - Hardware address to reply to. +// SourceRoute - Source Routing information, if any. +// SourceRouteSize - Size in bytes of soure routing. +// UseSNAP - Whether or not to use SNAP for this reply. +// +// Returns: Nothing. +// +void +SendARPReply(ARPInterface *Interface, IPAddr Destination, IPAddr Src, uchar *HWAddress, + RC UNALIGNED *SourceRoute, uint SourceRouteSize, uint UseSNAP) +{ + PNDIS_PACKET Packet; // Buffer and packet to be used. + PNDIS_BUFFER Buffer; + uchar *Header; // Pointer to media header. + NDIS_STATUS Status; + uchar Size = 0; // Size of media header buffer. + ushort HWType; + ENetHeader *EH; + FDDIHeader *FH; + ARCNetHeader *AH; + TRHeader *TRH; + + // Allocate a packet for this. + NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool); + if (Status != NDIS_STATUS_SUCCESS) { + Interface->ai_outdiscards++; + return; + } + + ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK; + (Interface->ai_outpcount[AI_UCAST_INDEX])++; + + Size = Interface->ai_hdrsize; + + if (UseSNAP) + Size += Interface->ai_snapsize; + + if (Interface->ai_media == NdisMedium802_5) + Size += SourceRouteSize; + + if ((Buffer = GetARPBuffer(Interface, &Header, (uchar)(Size + sizeof(ARPHeader)))) == + (PNDIS_BUFFER)NULL) { + Interface->ai_outdiscards++; + NdisFreePacket(Packet); + return; + } + + // Decide how to build the header based on the media type. + switch (Interface->ai_media) { + case NdisMedium802_3: + EH = (ENetHeader *)Header; + CTEMemCopy(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH); + CTEMemCopy(EH->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); + if (!UseSNAP) { + EH->eh_type = net_short(ARP_ETYPE_ARP); + HWType = net_short(ARP_HW_ENET); + } else { + // Using SNAP on ethernet. + EH->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader)); + HWType = net_short(ARP_HW_802); + CTEMemCopy(Header + sizeof(ENetHeader), ARPSNAP, + sizeof(SNAPHeader)); + } + break; + case NdisMedium802_5: + TRH = (TRHeader *)Header; + TRH->tr_ac = ARP_AC; + TRH->tr_fc = ARP_FC; + CTEMemCopy(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH); + CTEMemCopy(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); + if (SourceRouteSize) {// If we have source route info, deal with + // it. + CTEMemCopy(Header + sizeof(TRHeader), SourceRoute, + SourceRouteSize); + // Convert to directed response. + ((RC *)&Header[sizeof(TRHeader)])->rc_blen &= RC_LENMASK; + + ((RC *)&Header[sizeof(TRHeader)])->rc_dlf ^= RC_DIR; + TRH->tr_saddr[0] |= TR_RII; + } + CTEMemCopy(Header + sizeof(TRHeader) + SourceRouteSize, ARPSNAP, + sizeof(SNAPHeader)); + HWType = net_short(ARP_HW_802); + break; + case NdisMediumFddi: + FH = (FDDIHeader *)Header; + FH->fh_pri = ARP_FDDI_PRI; + CTEMemCopy(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH); + CTEMemCopy(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH); + CTEMemCopy(Header + sizeof(FDDIHeader), ARPSNAP, sizeof(SNAPHeader)); + HWType = net_short(ARP_HW_ENET); + break; + case NdisMediumArcnet878_2: + AH = (ARCNetHeader *)Header; + AH->ah_saddr = Interface->ai_addr[0]; + AH->ah_daddr = *HWAddress; + AH->ah_prot = ARP_ARCPROT_ARP; + NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT; + HWType = net_short(ARP_HW_ARCNET); + break; + default: + DEBUGCHK; + Interface->ai_outerrors++; + FreeARPBuffer(Interface, Buffer); + NdisFreePacket(Packet); + return; + } + + NdisChainBufferAtFront(Packet, Buffer); + SendARPPacket(Interface, Packet,(ARPHeader *)(Header + Size), net_short(ARP_RESPONSE), + HWAddress, NULL, Destination, Src, HWType, TRUE); +} + + +//* ARPRemoveRCE - Remove an RCE from the ATE list. +// +// This funtion removes a specified RCE from a given ATE. It assumes the ate_lock +// is held by the caller. +// +// Entry: ATE - ATE from which RCE is to be removed. +// RCE - RCE to be removed. +// +// Returns: Nothing +// +void +ARPRemoveRCE(ARPTableEntry *ATE, RouteCacheEntry *RCE) +{ + ARPContext *CurrentAC; // Current ARP Context being checked. +#ifdef DEBUG + uint Found = FALSE; +#endif + + CurrentAC = (ARPContext *)(((char *)&ATE->ate_rce) - + offsetof(struct ARPContext, ac_next)); + + while (CurrentAC->ac_next != (RouteCacheEntry *)NULL) + if (CurrentAC->ac_next == RCE) { + ARPContext *DummyAC = (ARPContext *)RCE->rce_context; + CurrentAC->ac_next = DummyAC->ac_next; + DummyAC->ac_ate = (ARPTableEntry *)NULL; +#ifdef DEBUG + Found = TRUE; +#endif + break; + } + else + CurrentAC = (ARPContext *)CurrentAC->ac_next->rce_context; + + CTEAssert(Found); +} +//* ARPLookup - Look up an entry in the ARP table. +// +// Called to look up an entry in an interface's ARP table. If we find it, we'll +// lock the entry and return a pointer to it, otherwise we return NULL. We +// assume that the caller has the ARP table locked when we are called. +// +// The ARP table entry is structured as a hash table of pointers to +// ARPTableEntrys.After hashing on the IP address, a linear search is done to +// lookup the entry. +// +// If we find the entry, we lock it for the caller. If we don't find +// the entry, we leave the ARP table locked so that the caller may atomically +// insert a new entry without worrying about a duplicate being inserted between +// the time the table was checked and the time the caller went to insert the +// entry. +// +// Entry: Interface - The interface to be searched upon. +// Address - The IP address we're looking up. +// Handle - Pointer to lock handle to be used to lock entry. +// +// Returns: Pointer to ARPTableEntry if found, or NULL if not. +// +ARPTableEntry * +ARPLookup(ARPInterface *Interface, IPAddr Address, CTELockHandle *Handle) +{ + int i = ARP_HASH(Address); // Index into hash table. + ARPTableEntry *Current; // Current ARP Table entry being + // examined. + + Current = (*Interface->ai_ARPTbl)[i]; + + while (Current != (ARPTableEntry *)NULL) { + CTEGetLock(&Current->ate_lock, Handle); + if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match. + return Current; + } + CTEFreeLock(&Current->ate_lock, *Handle); + Current = Current->ate_next; + } + // If we got here, we didn't find the entry. Leave the table locked and + // return the handle. + return (ARPTableEntry *)NULL; +} + +//* IsBCastOnIF- See it an address is a broadcast address on an interface. +// +// Called to see if a particular address is a broadcast address on an +// interface. We'll check the global, net, and subnet broadcasts. We assume +// the caller holds the lock on the interface. +// +// Entry: Interface - Interface to check. +// Addr - Address to check. +// +// Returns: TRUE if it it a broadcast, FALSE otherwise. +// +uint +IsBCastOnIF(ARPInterface *Interface, IPAddr Addr) +{ + IPAddr BCast; + IPMask Mask; + ARPIPAddr *ARPAddr; + IPAddr LocalAddr; + + // First get the interface broadcast address. + BCast = Interface->ai_bcast; + + // First check for global broadcast. + if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr)) + return TRUE; + + // Now walk the local addresses, and check for net/subnet bcast on each + // one. + ARPAddr = &Interface->ai_ipaddr; + do { + // See if this one is valid. + LocalAddr = ARPAddr->aia_addr; + if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) { + // He's valid. + Mask = ARPAddr->aia_mask; + + // First check for subnet bcast. + if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr)) + return TRUE; + + // Now check all nets broadcast. + Mask = IPNetMask(LocalAddr); + if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr)) + return TRUE; + } + + ARPAddr = ARPAddr->aia_next; + + } while (ARPAddr != NULL); + + // If we're here, it's not a broadcast. + return FALSE; + +} + + +//* ARPSendBCast - See if this is a bcast or mcast frame, and send it. +// +// Called when we have a packet to send and we want to see if it's a broadcast +// or multicast frame on this interface. We'll search the local addresses and +// see if we can determine if it is. If it is, we'll send it here. Otherwise +// we return FALSE, and the caller will try to resolve the address. +// +// Entry: Interface - A pointer to an AI structure. +// Dest - Destination of datagram. +// Packet - Packet to be sent. +// Status - Place to return status of send attempt. +// +// Returns: TRUE if is was a bcast or mcast send, FALSE otherwise. +// +uint +ARPSendBCast(ARPInterface *Interface, IPAddr Dest, PNDIS_PACKET Packet, + PNDIS_STATUS Status) +{ + uint IsBCast; + CTELockHandle Handle; + PNDIS_BUFFER ARPBuffer; // ARP Header buffer. + uchar *BufAddr; // Address of NDIS buffer + NDIS_STATUS MyStatus; + ENetHeader *Hdr; + FDDIHeader *FHdr; + TRHeader *TRHdr; + SNAPHeader UNALIGNED *SNAPPtr; + RC UNALIGNED *RCPtr; + ARCNetHeader *AHdr; + uint DataLength; + + // Get the lock, and see if it's a broadcast. + CTEGetLock(&Interface->ai_lock, &Handle); + IsBCast = IsBCastOnIF(Interface, Dest); + CTEFreeLock(&Interface->ai_lock, Handle); + + if (IsBCast) { + if (Interface->ai_state == INTERFACE_UP) { + uchar Size; + + Size = Interface->ai_hdrsize + Interface->ai_snapsize; + + if (Interface->ai_media == NdisMedium802_5) + Size += sizeof(RC); + + ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size); + if (ARPBuffer != NULL) { + uint UNALIGNED *Temp; + + // Got the buffer we need. + switch (Interface->ai_media) { + + case NdisMedium802_3: + + Hdr = (ENetHeader *)BufAddr; + if (!CLASSD_ADDR(Dest)) + CTEMemCopy(Hdr, ENetBcst, ARP_802_ADDR_LENGTH); + else { + CTEMemCopy(Hdr, ENetMcst, ARP_802_ADDR_LENGTH); + Temp = (uint UNALIGNED *)&Hdr->eh_daddr[2]; + *Temp |= (Dest & ARP_MCAST_MASK); + } + + CTEMemCopy(Hdr->eh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + if (Interface->ai_snapsize == 0) { + // No snap on this interface, so just use ETypr. + Hdr->eh_type = net_short(ARP_ETYPE_IP); + } else { + ushort ShortDataLength; + + // We're using SNAP. Find the size of the packet. + NdisQueryPacket(Packet, NULL, NULL, NULL, + &DataLength); + ShortDataLength = (ushort)(DataLength + + sizeof(SNAPHeader)); + Hdr->eh_type = net_short(ShortDataLength); + SNAPPtr = (SNAPHeader UNALIGNED *) + (BufAddr + sizeof(ENetHeader)); + CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); + SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); + } + + break; + + case NdisMedium802_5: + + // This is token ring. We'll have to screw around with + // source routing. + + // BUGBUG Need to support 'real' TR functional address + // for multicast - see RFC 1469. + + TRHdr = (TRHeader *)BufAddr; + + CTEMemCopy(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr)); + CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + if (sIPAlwaysSourceRoute) + { + TRHdr->tr_saddr[0] |= TR_RII; + + RCPtr = (RC UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader)); + RCPtr->rc_blen = TrRii | RC_LEN; + RCPtr->rc_dlf = RC_BCST_LEN; + SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)RCPtr + sizeof(RC)); + } + else + { + + // + // Adjust the size of the buffer to account for the + // fact that we don't have the RC field. + // + NdisAdjustBufferLength(ARPBuffer,(Size - sizeof(RC))); + SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader)); + } + CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); + SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); + + break; + case NdisMediumFddi: + FHdr = (FDDIHeader *)BufAddr; + + if (!CLASSD_ADDR(Dest)) + CTEMemCopy(FHdr, FDDIBcst, + offsetof(FDDIHeader, fh_saddr)); + else { + CTEMemCopy(FHdr, FDDIMcst, + offsetof(FDDIHeader, fh_saddr)); + Temp = (uint UNALIGNED *)&FHdr->fh_daddr[2]; + *Temp |= (Dest & ARP_MCAST_MASK); + } + + CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + SNAPPtr = (SNAPHeader UNALIGNED *)(BufAddr + sizeof(FDDIHeader)); + CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader)); + SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP); + + break; + case NdisMediumArcnet878_2: + AHdr = (ARCNetHeader *)BufAddr; + AHdr->ah_saddr = Interface->ai_addr[0]; + AHdr->ah_daddr = 0; + AHdr->ah_prot = ARP_ARCPROT_IP; + break; + default: + DEBUGCHK; + *Status = NDIS_STATUS_UNSUPPORTED_MEDIA; + FreeARPBuffer(Interface, ARPBuffer); + return FALSE; + + } + + (Interface->ai_outpcount[AI_NONUCAST_INDEX])++; + Interface->ai_qlen++; + NdisChainBufferAtFront(Packet, ARPBuffer); + NdisSend(&MyStatus, Interface->ai_handle, Packet); + + *Status = MyStatus; + + if (MyStatus != NDIS_STATUS_PENDING) { // Send finished + // immediately. + if (MyStatus == NDIS_STATUS_SUCCESS) { + Interface->ai_outoctets += Packet->Private.TotalLength; + } else { + if (MyStatus == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + NdisUnchainBufferAtFront(Packet, &ARPBuffer); + FreeARPBuffer(Interface, ARPBuffer); + } + } else + *Status = NDIS_STATUS_RESOURCES; + } else + *Status = NDIS_STATUS_ADAPTER_NOT_READY; + + return TRUE; + + } else + return FALSE; +} + +//* ARPSendData - Send a frame to a specific destination address. +// +// Called when we need to send a frame to a particular address, after the +// ATE has been looked up. We take in an ATE and a packet, validate the state of the +// ATE, and either send or ARP for the address if it's not done resolving. We assume +// the lock on the ATE is held where we're called, and we'll free it before returning. +// +// Entry: Interface - A pointer to the AI structure. +// Packet - A pointer to the BufDesc chain to be sent. +// entry - A pointer to the ATE for the send. +// lhandle - Pointer to a lock handle for the ATE. +// +// Returns: Status of the transmit - success, an error, or pending. +// +NDIS_STATUS +ARPSendData(ARPInterface *Interface, PNDIS_PACKET Packet, ARPTableEntry *entry, + CTELockHandle lhandle) +{ + PNDIS_BUFFER ARPBuffer; // ARP Header buffer. + uchar *BufAddr; // Address of NDIS buffer + NDIS_STATUS Status; // Status of send. + + if (Interface->ai_state == INTERFACE_UP) { + + if (entry->ate_state == ARP_GOOD) { // Entry is valid + + entry->ate_useticks = ArpCacheLife; + if ((ARPBuffer = GetARPBuffer(Interface, &BufAddr, + entry->ate_addrlength)) != (PNDIS_BUFFER)NULL) { + + // Everything's in good shape, copy header and send packet. + + (Interface->ai_outpcount[AI_UCAST_INDEX])++; + Interface->ai_qlen++; + CTEMemCopy(BufAddr, entry->ate_addr, entry->ate_addrlength); + + // If we're on Ethernet, see if we're using SNAP here. + if (Interface->ai_media == NdisMedium802_3 && + entry->ate_addrlength != sizeof(ENetHeader)) { + ENetHeader *Header; + uint DataSize; + ushort ShortDataSize; + + // We're apparently using SNAP on Ethernet. Query the + // packet for the size, and set the length properly. + NdisQueryPacket(Packet, NULL, NULL, NULL, &DataSize); + ShortDataSize = (ushort)(DataSize + sizeof(SNAPHeader)); + Header = (ENetHeader *)BufAddr; + Header->eh_type = net_short(ShortDataSize); + } + + CTEFreeLock(&entry->ate_lock, lhandle); + NdisChainBufferAtFront(Packet, ARPBuffer); + NdisSend(&Status, Interface->ai_handle, Packet); + if (Status != NDIS_STATUS_PENDING) { // Send finished + // immediately. + if (Status == NDIS_STATUS_SUCCESS) { + Interface->ai_outoctets += Packet->Private.TotalLength; + } else { + if (Status == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + NdisUnchainBufferAtFront(Packet, &ARPBuffer); + FreeARPBuffer(Interface, ARPBuffer); + } + return Status; + } else { // No buffer, free lock and return. + CTEFreeLock(&entry->ate_lock, lhandle); + Interface->ai_outdiscards++; + return NDIS_STATUS_RESOURCES; + } + } + // The IP addresses match, but the state of the ARP entry indicates + // it's not valid. If the address is marked as resolving, we'll replace + // the current cached packet with this one. If it's been more than + // ARP_FLOOD_RATE ms. since we last sent an ARP request, we'll send + // another one now. + if (entry->ate_state <= ARP_RESOLVING) { + PNDIS_PACKET OldPacket = entry->ate_packet; + ulong Now = CTESystemUpTime(); + entry->ate_packet = Packet; + if ((Now - entry->ate_valid) > ARP_FLOOD_RATE) { + IPAddr Dest = entry->ate_dest; + + entry->ate_valid = Now; + entry->ate_state = ARP_RESOLVING_GLOBAL; // We're done this + // at least once. + CTEFreeLock(&entry->ate_lock, lhandle); + SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL, + NULL, TRUE); // Send a request. + } else + CTEFreeLock(&entry->ate_lock, lhandle); + + if (OldPacket) + IPSendComplete(Interface->ai_context, OldPacket, + NDIS_STATUS_SUCCESS); + + return NDIS_STATUS_PENDING; + } else { + DEBUGCHK; + CTEFreeLock(&entry->ate_lock, lhandle); + Interface->ai_outerrors++; + return NDIS_STATUS_INVALID_PACKET; + } + } else { + // Adapter is down. Just return the error. + CTEFreeLock(&entry->ate_lock, lhandle); + return NDIS_STATUS_ADAPTER_NOT_READY; + } +} + +//* CreateARPTableEntry - Create a new entry in the ARP table. +// +// A function to put an entry into the ARP table. We allocate memory if we +// need to. +// +// The first thing to do is get the lock on the ARP table, and see if the +// entry already exists. If it does, we're done. Otherwise we need to allocate +// memory and create a new entry. +// +// Entry: Interface - Interface for ARP table. +// Destination - Destination address to be mapped. +// Handle - Pointer to lock handle for entry. +// +// Returns: Pointer to newly created entry. +// +ARPTableEntry * +CreateARPTableEntry(ARPInterface *Interface, IPAddr Destination, + CTELockHandle *Handle) +{ + ARPTableEntry *NewEntry, *Entry; + CTELockHandle TableHandle; + int i = ARP_HASH(Destination); + int Size; + + // First look for it, and if we don't find it return try to create one. + CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle); + if ((Entry = ARPLookup(Interface, Destination, Handle)) != + (ARPTableEntry *)NULL) { + CTEFreeLock(&Interface->ai_ARPTblLock, *Handle); + *Handle = TableHandle; + return Entry; + } + + // Allocate memory for the entry. If we can't, fail the request. + Size = sizeof(ARPTableEntry) - 1 + + (Interface->ai_media == NdisMedium802_5 ? + ARP_MAX_MEDIA_TR : (Interface->ai_hdrsize + + Interface->ai_snapsize)); + + if ((NewEntry = CTEAllocMem(Size)) == (ARPTableEntry *)NULL) { + CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); + return (ARPTableEntry *)NULL; + } + + CTEMemSet(NewEntry, 0, Size); + NewEntry->ate_dest = Destination; + if (Interface->ai_media != NdisMedium802_5 || sArpAlwaysSourceRoute) + NewEntry->ate_state = ARP_RESOLVING_GLOBAL; + else + NewEntry->ate_state = ARP_RESOLVING_LOCAL; + + NewEntry->ate_rce = NULL; + + NewEntry->ate_valid = CTESystemUpTime(); + NewEntry->ate_useticks = ArpCacheLife; + CTEInitLock(&NewEntry->ate_lock); + + // Entry does not exist. Insert the new entry into the table at the appropriate spot. + // ARPLookup returns with the table lock held if it fails. + NewEntry->ate_next = (*Interface->ai_ARPTbl)[i]; + (*Interface->ai_ARPTbl)[i] = NewEntry; + Interface->ai_count++; + CTEGetLock(&NewEntry->ate_lock, Handle); + CTEFreeLock(&Interface->ai_ARPTblLock, *Handle); + *Handle = TableHandle; + return NewEntry; +} + + +//* ARPTransmit - Send a frame. +// +// The main ARP transmit routine, called by the upper layer. This routine +// takes as input a buf desc chain, RCE, and size. We validate the cached +// information in the RCE. If it is valid, we use it to send the frame. Otherwise +// we do a table lookup. If we find it in the table, we'll update the RCE and continue. +// Otherwise we'll queue the packet and start an ARP resolution. +// +// Entry: Context - A pointer to the AI structure. +// Packet - A pointer to the BufDesc chain to be sent. +// Destination - IP address of destination we're trying to reach, +// RCE - A pointer to an RCE which may have cached information. +// +// Returns: Status of the transmit - success, an error, or pending. +// +NDIS_STATUS +ARPTransmit(void *Context, PNDIS_PACKET Packet, IPAddr Destination, + RouteCacheEntry *RCE) +{ + ARPInterface *ai = (ARPInterface *)Context; // Set up as AI pointer. + ARPContext *ac; // ARP context pointer. + ARPTableEntry *entry; // Pointer to ARP tbl. entry + CTELockHandle lhandle; // Lock handle + CTELockHandle tlhandle; // Lock handle for ARP table. + NDIS_STATUS Status; + + CTEGetLock(&ai->ai_ARPTblLock, &tlhandle); + if (RCE != (RouteCacheEntry *)NULL) { // Have a valid RCE. + ac = (ARPContext *)RCE->rce_context; // Get pointer to context + entry = ac->ac_ate; + if (entry != (ARPTableEntry *)NULL) { // Have a valid ATE. + CTEGetLockAtDPC(&entry->ate_lock, &lhandle); // Lock this structure + if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) { + CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle); + return ARPSendData(ai, Packet, entry, tlhandle); // Send the data + } + + // We have an RCE that identifies the wrong ATE. We'll free it from + // this list and try and find an ATE that is valid. + ARPRemoveRCE(entry, RCE); + CTEFreeLock(&entry->ate_lock, lhandle); + // Fall through to 'no valid entry' code. + } + } + + // Here we have no valid ATE, either because the RCE is NULL or the ATE + // specified by the RCE was invalid. We'll try and find one in the table. If + // we find one, we'll fill in this RCE and send the packet. Otherwise we'll + // try to create one. At this point we hold the lock on the ARP table. + + if ((entry = ARPLookup(ai, Destination, &lhandle)) != (ARPTableEntry *)NULL) { + // Found a matching entry. ARPLookup returns with the ATE lock held. + if (RCE != (RouteCacheEntry *)NULL) { + ac->ac_next = entry->ate_rce; // Fill in context for next time. + entry->ate_rce = RCE; + ac->ac_ate = entry; + } + CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle); + return ARPSendData(ai, Packet, entry, tlhandle); + } + + // No valid entry in the ARP table. First we'll see if we're sending to a + // broadcast address or multicast address. If not, we'll try to create + // an entry in the table and get an ARP resolution going. ARPLookup returns + // with the table lock held when it fails, we'll free it here. + CTEFreeLock(&ai->ai_ARPTblLock, tlhandle); + + if (ARPSendBCast(ai, Destination, Packet, &Status)) + return Status; + + entry = CreateARPTableEntry(ai, Destination, &lhandle); + if (entry != NULL) { + if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry. + + // Someone else could have raced in and created the entry between + // the time we free the lock and the time we called + // CreateARPTableEntry(). We check this by looking at the packet + // on the entry. If there is no old packet we'll ARP. If there is, + // we'll call ARPSendData to figure out what to do. + + if (entry->ate_packet == NULL) { + entry->ate_packet = Packet; + CTEFreeLock(&entry->ate_lock, lhandle); + SendARPRequest(ai, Destination, entry->ate_state, NULL, TRUE); + // We don't know the state of the entry - we've freed the lock + // and yielded, and it could conceivably have timed out by now, + // or SendARPRequest could have failed, etc. We could take the + // lock, check the status from SendARPRequest, see if it's + // still the same packet, and then make a decision on the + // return value, but it's easiest just to return pending. If + // SendARPRequest failed, the entry will time out anyway. + return NDIS_STATUS_PENDING; + } else + return ARPSendData(ai, Packet, entry, lhandle); + + } else { + if (entry->ate_state == ARP_GOOD) // Yow! A valid entry. + return ARPSendData(ai, Packet, entry, lhandle); + else { // An invalid entry! + CTEFreeLock(&entry->ate_lock, lhandle); + return NDIS_STATUS_RESOURCES; + } + } + } else // Couldn't create an entry. + return NDIS_STATUS_RESOURCES; + +} + +//* RemoveARPTableEntry - Delete an entry from the ARP table. +// +// This is a simple utility function to delete an entry from the ATP table. We +// assume locks are held on both the table and the entry. +// +// Entry: Previous - The entry immediately before the one to be deleted. +// Entry - The entry to be deleted. +// +// Returns: Nothing. +// +void +RemoveARPTableEntry(ARPTableEntry *Previous, ARPTableEntry *Entry) +{ + RouteCacheEntry *RCE; // Pointer to route cache entry + ARPContext *AC; + + RCE = Entry->ate_rce; + // Loop through and invalidate all RCEs on this ATE. + while (RCE != (RouteCacheEntry *)NULL) { + AC = (ARPContext *)RCE->rce_context; + AC->ac_ate = (ARPTableEntry *)NULL; + RCE = AC->ac_next; + } + + // Splice this guy out of the list. + Previous->ate_next = Entry->ate_next; +} + +//* ARPXferData - Transfer data on behalf on an upper later protocol. +// +// This routine is called by the upper layer when it needs to transfer data +// from an NDIS driver. We just map his call down. +// +// Entry: Context - Context value we gave to IP (really a pointer to an AI). +// MACContext - Context value MAC gave us on a receive. +// MyOffset - Packet offset we gave to the protocol earlier. +// ByteOffset - Byte offset into packet protocol wants transferred. +// BytesWanted - Number of bytes to transfer. +// Packet - Pointer to packet to be used for transferring. +// Transferred - Pointer to where to return bytes transferred. +// +// Returns: NDIS_STATUS of command. +// +NDIS_STATUS +ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset, uint ByteOffset, + uint BytesWanted, PNDIS_PACKET Packet, uint *Transferred) +{ + ARPInterface *Interface = (ARPInterface *)Context; + NDIS_STATUS Status; + + NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset+MyOffset, + BytesWanted, Packet, Transferred); + + return Status; +} + + +//* ARPClose - Close an adapter. +// +// Called by IP when it wants to close an adapter, presumably due to an error condition. +// We'll close the adapter, but we won't free any memory. +// +// Entry: Context - Context value we gave him earlier. +// +// Returns: Nothing. +// +void +ARPClose(void *Context) +{ + ARPInterface *Interface = (ARPInterface *)Context; + NDIS_STATUS Status; + CTELockHandle LockHandle; + NDIS_HANDLE Handle; + + Interface->ai_operstate = IF_STATUS_DOWN; + Interface->ai_state = INTERFACE_DOWN; + CTEInitBlockStruc(&Interface->ai_block); + + CTEGetLock(&Interface->ai_lock, &LockHandle); + if (Interface->ai_handle != (NDIS_HANDLE)NULL) { + Handle = Interface->ai_handle; + Interface->ai_handle = NULL; + CTEFreeLock(&Interface->ai_lock, LockHandle); + + NdisCloseAdapter(&Status, Handle); + + if (Status == NDIS_STATUS_PENDING) + Status = CTEBlock(&Interface->ai_block); + + } else { + CTEFreeLock(&Interface->ai_lock, LockHandle); + } +} + +//* ARPInvalidate - Notification that an RCE is invalid. +// +// Called by IP when an RCE is closed or otherwise invalidated. We look up the ATE for +// the specified RCE, and then remove the RCE from the ATE list. +// +// Entry: Context - Context value we gave him earlier. +// RCE - RCE to be invalidated +// +// Returns: Nothing. +// +void +ARPInvalidate(void *Context, RouteCacheEntry *RCE) +{ + ARPInterface *Interface = (ARPInterface *)Context; + ARPTableEntry *ATE; + CTELockHandle Handle, ATEHandle; + ARPContext *AC = (ARPContext *)RCE->rce_context; + + CTEGetLock(&Interface->ai_ARPTblLock, &Handle); + if ((ATE = AC->ac_ate) == (ARPTableEntry *)NULL) { + CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE. + return; + } + + CTEGetLock(&ATE->ate_lock, &ATEHandle); + ARPRemoveRCE(ATE, RCE); + CTEMemSet(RCE->rce_context, 0, RCE_CONTEXT_SIZE); + CTEFreeLock(&Interface->ai_ARPTblLock, ATEHandle); + CTEFreeLock(&ATE->ate_lock, Handle); + +} + +//* ARPSetMCastList - Set the multicast address list for the adapter. +// +// Called to try and set the multicast reception list for the adapter. +// We allocate a buffer big enough to hold the new address list, and format +// the address list into the buffer. Then we submit the NDIS request to set +// the list. If we can't set the list because the multicast address list is +// full we'll put the card into all multicast mode. +// +// Input: Interface - Interface on which to set list. +// +// Returns: NDIS_STATUS of attempt. +// +NDIS_STATUS +ARPSetMCastList(ARPInterface *Interface) +{ + CTELockHandle Handle; + uchar *MCastBuffer, *CurrentPtr; + uint MCastSize; + NDIS_STATUS Status; + uint i; + ARPMCastAddr *AddrPtr; + IPAddr UNALIGNED *Temp; + + CTEGetLock(&Interface->ai_lock, &Handle); + MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH; + if (MCastSize != 0) + MCastBuffer = CTEAllocMem(MCastSize); + else + MCastBuffer = NULL; + + if (MCastBuffer != NULL || MCastSize == 0) { + // Got the buffer. Loop through, building the list. + AddrPtr = Interface->ai_mcast; + + CurrentPtr = MCastBuffer; + + for (i = 0; i < Interface->ai_mcastcnt; i++) { + CTEAssert(AddrPtr != NULL); + + if (Interface->ai_media == NdisMedium802_3) { + + CTEMemCopy(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH); + Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2); + *Temp |= AddrPtr->ama_addr; + } else + if (Interface->ai_media == NdisMediumFddi) { + CTEMemCopy(CurrentPtr, ((FDDIHeader *)FDDIMcst)->fh_daddr, + ARP_802_ADDR_LENGTH); + Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2); + *Temp |= AddrPtr->ama_addr; + } else + DEBUGCHK; + + CurrentPtr += ARP_802_ADDR_LENGTH; + AddrPtr = AddrPtr->ama_next; + } + + CTEFreeLock(&Interface->ai_lock, Handle); + + // We're built the list. Now give it to the driver to handle. + if (Interface->ai_media == NdisMedium802_3) { + Status = DoNDISRequest(Interface, NdisRequestSetInformation, + OID_802_3_MULTICAST_LIST, MCastBuffer, MCastSize, NULL); + } else + if (Interface->ai_media == NdisMediumFddi) { + Status = DoNDISRequest(Interface, NdisRequestSetInformation, + OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL); + } else + DEBUGCHK; + + if (MCastBuffer != NULL) { + CTEFreeMem(MCastBuffer); + } + + if (Status == NDIS_STATUS_MULTICAST_FULL) { + // Multicast list is full. Try to set the filter to all multicasts. + Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST; + + Status = DoNDISRequest(Interface, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, + sizeof(uint), NULL); + } + + } else { + CTEFreeLock(&Interface->ai_lock, Handle); + Status = NDIS_STATUS_RESOURCES; + } + + return Status; + +} + +//* ARPFindMCast - Find a multicast address structure on our list. +// +// Called as a utility to find a multicast address structure. If we find +// it, we return a pointer to it and it's predecessor. Otherwise we return +// NULL. We assume the caller holds the lock on the interface already. +// +// Input: Interface - Interface to search. +// Addr - Addr to find. +// Prev - Where to return previous pointer. +// +// Returns: Pointer if we find one, NULL otherwise. +// +ARPMCastAddr * +ARPFindMCast(ARPInterface *Interface, IPAddr Addr, ARPMCastAddr **Prev) +{ + ARPMCastAddr *AddrPtr, *PrevPtr; + + PrevPtr = STRUCT_OF(ARPMCastAddr, &Interface->ai_mcast, ama_next); + AddrPtr = PrevPtr->ama_next; + while (AddrPtr != NULL) { + if (IP_ADDR_EQUAL(AddrPtr->ama_addr, Addr)) + break; + else { + PrevPtr = AddrPtr; + AddrPtr = PrevPtr->ama_next; + } + } + + *Prev = PrevPtr; + return AddrPtr; +} + +//* ARPDelMCast - Delete a multicast address. +// +// Called when we want to delete a multicast address. We look for a matching +// (masked) address. If we find one, we'll dec. the reference count and if +// it goes to 0 we'll pull him from the list and reset the multicast list. +// +// Input: Interface - Interface on which to act. +// Addr - Address to be deleted. +// +// Returns: TRUE if it worked, FALSE otherwise. +// +uint +ARPDelMCast(ARPInterface *Interface, IPAddr Addr) +{ + ARPMCastAddr *AddrPtr, *PrevPtr; + CTELockHandle Handle; + uint Status = TRUE; + + // When we support TR (RFC 1469) fully we'll need to change this. + if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media == + NdisMediumFddi) { + // This is an interface that supports mcast addresses. + Addr &= ARP_MCAST_MASK; + + CTEGetLock(&Interface->ai_lock, &Handle); + AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr); + if (AddrPtr != NULL) { + // We found one. Dec. his refcnt, and if it's 0 delete him. + (AddrPtr->ama_refcnt)--; + if (AddrPtr->ama_refcnt == 0) { + // He's done. + PrevPtr->ama_next = AddrPtr->ama_next; + (Interface->ai_mcastcnt)--; + CTEFreeLock(&Interface->ai_lock, Handle); + CTEFreeMem(AddrPtr); + ARPSetMCastList(Interface); + CTEGetLock(&Interface->ai_lock, &Handle); + } + } else + Status = FALSE; + + CTEFreeLock(&Interface->ai_lock, Handle); + } + + return Status; +} +//* ARPAddMCast - Add a multicast address. +// +// Called when we want to start receiving a multicast address. We'll mask +// the address and look it up in our address list. If we find it, we'll just +// bump the reference count. Otherwise we'll try to create one and put him +// on the list. In that case we'll need to set the multicast address list for +// the adapter. +// +// Input: Interface - Interface to set on. +// Addr - Address to set. +// +// Returns: TRUE if we succeed, FALSE if we fail. +// +uint +ARPAddMCast(ARPInterface *Interface, IPAddr Addr) +{ + ARPMCastAddr *AddrPtr, *PrevPtr; + CTELockHandle Handle; + uint Status = TRUE; + + + if (Interface->ai_state != INTERFACE_UP) + return FALSE; + + // BUGBUG Currently we don't do anything with token ring, since we send + // all mcasts as TR broadcasts. When we comply with RFC 1469 we'll need to + // fix this. + if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media == + NdisMediumFddi) { + // This is an interface that supports mcast addresses. + Addr &= ARP_MCAST_MASK; + + CTEGetLock(&Interface->ai_lock, &Handle); + AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr); + if (AddrPtr != NULL) { + // We found one, just bump refcnt. + (AddrPtr->ama_refcnt)++; + } else { + // Didn't find one. Allocate space for one, link him in, and + // try to set the list. + AddrPtr = CTEAllocMem(sizeof(ARPMCastAddr)); + if (AddrPtr != NULL) { + // Got one. Link him in. + AddrPtr->ama_addr = Addr; + AddrPtr->ama_refcnt = 1; + AddrPtr->ama_next = Interface->ai_mcast; + Interface->ai_mcast = AddrPtr; + (Interface->ai_mcastcnt)++; + CTEFreeLock(&Interface->ai_lock, Handle); + + // Now try to set the list. + if (ARPSetMCastList(Interface) != NDIS_STATUS_SUCCESS) { + // Couldn't set the list. Call the delete routine to delete + // the address we just tried to set. + Status = ARPDelMCast(Interface, Addr); + if (!Status) + DEBUGCHK; + Status = FALSE; + } + CTEGetLock(&Interface->ai_lock, &Handle); + } else + Status = FALSE; // Couldn't get memory. + } + + // We've done out best. Free the lock and return. + CTEFreeLock(&Interface->ai_lock, Handle); + } + + return Status; +} + +//* ARPAddAddr - Add an address to the ARP table. +// +// This routine is called by IP to add an address as a local address, or +// or specify the broadcast address for this interface. +// +// Entry: Context - Context we gave IP earlier (really an ARPInterface pointer) +// Type - Type of address (local, p-arp, multicast, or +// broadcast). +// Address - Broadcast IP address to be added. +// Mask - Mask for address. +// +// Returns: 0 if we failed, non-zero otherwise +// +uint +ARPAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) +{ + ARPInterface *Interface = (ARPInterface *)Context; + CTELockHandle Handle; + + if (Type != LLIP_ADDR_LOCAL && Type != LLIP_ADDR_PARP) { + // Not a local address, must be broadcast or multicast. + + if (Type == LLIP_ADDR_BCAST) { + Interface->ai_bcast = Address; + return TRUE; + } else + if (Type == LLIP_ADDR_MCAST) { + return ARPAddMCast(Interface, Address); + } else + return FALSE; + } else { // This is a local address. + CTEGetLock(&Interface->ai_lock, &Handle); + if (Type != LLIP_ADDR_PARP) { + uint RetStatus = FALSE; + uint ArpForSelf = FALSE; + + if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, 0)) { + Interface->ai_ipaddr.aia_addr = Address; + Interface->ai_ipaddr.aia_mask = Mask; + Interface->ai_ipaddr.aia_age = ARPADDR_NEW_LOCAL; + if (Interface->ai_state == INTERFACE_UP) { + Interface->ai_ipaddr.aia_context = Context2; + ArpForSelf = TRUE; + } else { + Interface->ai_ipaddr.aia_context = NULL; + } + RetStatus = TRUE; + } else { + ARPIPAddr *NewAddr; + + NewAddr = CTEAllocMem(sizeof(ARPIPAddr)); + if (NewAddr != (ARPIPAddr *)NULL) { + NewAddr->aia_addr = Address; + NewAddr->aia_mask = Mask; + NewAddr->aia_age = ARPADDR_NEW_LOCAL; + NewAddr->aia_next = Interface->ai_ipaddr.aia_next; + if (Interface->ai_state == INTERFACE_UP) { + NewAddr->aia_context = Context2; + ArpForSelf = TRUE; + } else { + NewAddr->aia_context = NULL; + } + + Interface->ai_ipaddr.aia_next = NewAddr; + RetStatus = TRUE; + } + } + + CTEFreeLock(&Interface->ai_lock, Handle); + // ARP for the address we've added, to see it it already exists. + if (RetStatus == TRUE && ArpForSelf == TRUE) { + SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + return IP_PENDING; + } + + return RetStatus; + } else if (Type == LLIP_ADDR_PARP) { + ARPPArpAddr *NewPArp; + + // He's adding a proxy arp address. + NewPArp = CTEAllocMem(sizeof(ARPPArpAddr)); + if (NewPArp != NULL) { + NewPArp->apa_addr = Address; + NewPArp->apa_mask = Mask; + NewPArp->apa_next = Interface->ai_parpaddr; + Interface->ai_parpaddr = NewPArp; + Interface->ai_parpcount++; + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } + CTEFreeLock(&Interface->ai_lock, Handle); + return FALSE; + } + } + +} + +//* ARPDeleteAddr - Delete a local or proxy address. +// +// Called to delete a local or proxy address. +// +// Entry: Context - An ARPInterface pointer. +// Type - Type of address (local or p-arp). +// Address - IP address to be deleted. +// Mask - Mask for address. Used only for deleting proxy-ARP +// entries. +// +// Returns: 0 if we failed, non-zero otherwise +// +uint +ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) +{ + ARPInterface *Interface = (ARPInterface *)Context; + CTELockHandle Handle; + ARPIPAddr *DelAddr, *PrevAddr; + ARPPArpAddr *DelPAddr, *PrevPAddr; + + if (Type == LLIP_ADDR_LOCAL) { + CTEGetLock(&Interface->ai_lock, &Handle); + + if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) { + Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR; + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } else { + PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next); + DelAddr = PrevAddr->aia_next; + while (DelAddr != NULL) + if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address)) + break; + else { + PrevAddr = DelAddr; + DelAddr = DelAddr->aia_next; + } + + if (DelAddr != NULL) { + PrevAddr->aia_next = DelAddr->aia_next; + CTEFreeMem(DelAddr); + } + CTEFreeLock(&Interface->ai_lock, Handle); + return (DelAddr != NULL); + } + } else if (Type == LLIP_ADDR_PARP) { + CTEGetLock(&Interface->ai_lock, &Handle); + PrevPAddr = STRUCT_OF(ARPPArpAddr, &Interface->ai_parpaddr, apa_next); + DelPAddr = PrevPAddr->apa_next; + while (DelPAddr != NULL) + if (IP_ADDR_EQUAL(DelPAddr->apa_addr, Address) && + DelPAddr->apa_mask == Mask) + break; + else { + PrevPAddr = DelPAddr; + DelPAddr = DelPAddr->apa_next; + } + + if (DelPAddr != NULL) { + PrevPAddr->apa_next = DelPAddr->apa_next; + Interface->ai_parpcount--; + CTEFreeMem(DelPAddr); + } + CTEFreeLock(&Interface->ai_lock, Handle); + return (DelPAddr != NULL); + } else + if (Type == LLIP_ADDR_MCAST) + return ARPDelMCast(Interface, Address); + else + return FALSE; +} + +//* ARPTimeout - ARP timeout routine. +// +// This is the timeout routine that is called periodically. We scan the ARP table, looking +// for invalid entries that can be removed. +// +// Entry: Timer - Pointer to the timer that just fired. +// Context - Pointer to the interface to be timed out. +// +// Returns: Nothing. +// +void +ARPTimeout(CTEEvent *Timer, void *Context) +{ + ARPInterface *Interface = (ARPInterface *)Context; // Our interface. + ARPTable *Table; + ARPTableEntry *Current, *Previous; + int i; // Index variable. + ulong Now = CTESystemUpTime(), ValidTime; + CTELockHandle tblhandle, entryhandle; + uchar Deleted; + PNDIS_PACKET PList = (PNDIS_PACKET)NULL; + ARPIPAddr *Addr; + + // Walk down the list of addresses, decrementing the age. + CTEGetLock(&Interface->ai_lock, &tblhandle); + + Addr = &Interface->ai_ipaddr; + + do { + if (Addr->aia_age != ARPADDR_OLD_LOCAL) { + (Addr->aia_age)--; + if (Addr->aia_age == ARPADDR_OLD_LOCAL) { + if (Addr->aia_context != NULL) { + SetAddrControl *SAC; + SetAddrRtn Rtn; + + SAC = (SetAddrControl *)Addr->aia_context; + Rtn = (SetAddrRtn)SAC->sac_rtn; + CTEFreeLock(&Interface->ai_lock, tblhandle); + (*Rtn)(SAC, IP_SUCCESS); + CTEGetLock(&Interface->ai_lock, &tblhandle); + Addr->aia_context = NULL; + } + } else { + CTEFreeLock(&Interface->ai_lock, tblhandle); + SendARPRequest(Interface, Addr->aia_addr, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + CTEGetLock(&Interface->ai_lock, &tblhandle); + } + } + + Addr = Addr->aia_next; + } while (Addr != NULL); + + CTEFreeLock(&Interface->ai_lock, tblhandle); + + // Loop through the ARP table for this interface, and delete stale entries. + CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle); + Table = Interface->ai_ARPTbl; + for (i = 0; i < ARP_TABLE_SIZE;i++) { + Previous = (ARPTableEntry *)((uchar *)&((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next)); + Current = (*Table)[i]; + while (Current != (ARPTableEntry *)NULL) { + CTEGetLock(&Current->ate_lock, &entryhandle); + Deleted = 0; + + if (Current->ate_state == ARP_GOOD) { + // + // The ARP entry is valid for ARP_VALID_TIMEOUT by default. + // If a cache life greater than ARP_VALID_TIMEOUT has been + // configured, we'll make the entry valid for that time. + // + ValidTime = ArpCacheLife * ARP_TIMER_TIME; + + if (ValidTime < ARP_MIN_VALID_TIMEOUT) { + ValidTime = ARP_MIN_VALID_TIMEOUT; + } + } + else { + ValidTime = ARP_RESOLVE_TIMEOUT; + } + + if (Current->ate_valid != ALWAYS_VALID && + ( ((Now - Current->ate_valid) > ValidTime) || + (Current->ate_state == ARP_GOOD && + !(--(Current->ate_useticks))))) { + + if (Current->ate_state != ARP_RESOLVING_LOCAL) { + // Really need to delete this guy. + PNDIS_PACKET Packet = Current->ate_packet; + + if (Packet != (PNDIS_PACKET)NULL) { + ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link + = PList; + PList = Packet; + } + RemoveARPTableEntry(Previous, Current); + Interface->ai_count--; + Deleted = 1; + } else { + IPAddr Dest = Current->ate_dest; + // This entry is only resoving locally, presumably this is + // token ring. We'll need to transmit a 'global' resolution + // now. + CTEAssert(Interface->ai_media == NdisMedium802_5); + + Now = CTESystemUpTime(); + Current->ate_valid = Now; + Current->ate_state = ARP_RESOLVING_GLOBAL; + CTEFreeLock(&Current->ate_lock, entryhandle); + CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle); + // Send a global request. + SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle); + + // Since we've freed the locks, we need to start over from + // the start of this chain. + Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]), + ate_next); + Current = (*Table)[i]; + continue; + + } + } + + // If we deleted the entry, leave the previous pointer alone, advance the + // current pointer, and free the memory. Otherwise move both pointers forward. + // We can free the entry lock now because the next pointers are protected by + // the table lock, and we've removed it from the list so nobody else should + // find it anyway. + CTEFreeLock(&Current->ate_lock, entryhandle); + if (Deleted) { + ARPTableEntry *Temp = Current; + Current = Current->ate_next; + CTEFreeMem(Temp); + } else { + Previous = Current; + Current = Current->ate_next; + } + } + } + + CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle); + + while (PList != (PNDIS_PACKET)NULL) { + PNDIS_PACKET Packet = PList; + + PList = ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link; + IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); + } + + CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface); +} + +//* IsLocalAddr - Return info. about local status of address. +// +// Called when we need info. about whether or not a particular address is +// local. We return info about whether or not it is, and if it is how old +// it is. +// +// Entry: Interface - Pointer to interface structure to be searched. +// Address - Address in question. +// +// Returns: ARPADDR_*, for how old it is. +// +// +uint +IsLocalAddr(ARPInterface *Interface, IPAddr Address) +{ + CTELockHandle Handle; + ARPIPAddr *CurrentAddr; + uint Age; + + CTEGetLock(&Interface->ai_lock, &Handle); + + CurrentAddr = &Interface->ai_ipaddr; + Age = ARPADDR_NOT_LOCAL; + + do { + if (CurrentAddr->aia_addr == Address) { + Age = CurrentAddr->aia_age; + break; + } + CurrentAddr = CurrentAddr->aia_next; + } while (CurrentAddr != NULL); + + CTEFreeLock(&Interface->ai_lock, Handle); + return Age; +} + +//* ARPLocalAddr - Determine whether or not a given address if local. +// +// This routine is called when we receive an incoming packet and need to determine whether +// or not it's local. We look up the provided address on the specified interface. +// +// Entry: Interface - Pointer to interface structure to be searched. +// Address - Address in question. +// +// Returns: TRUE if it is a local address, FALSE if it's not. +// +uchar +ARPLocalAddr(ARPInterface *Interface, IPAddr Address) +{ + CTELockHandle Handle; + ARPPArpAddr *CurrentPArp; + IPMask Mask, NetMask; + IPAddr MatchAddress; + + // First, see if he's a local (not-proxy) address. + if (IsLocalAddr(Interface, Address) != ARPADDR_NOT_LOCAL) + return TRUE; + + CTEGetLock(&Interface->ai_lock, &Handle); + + // Didn't find him in out local address list. See if he exists on our + // proxy ARP list. + for (CurrentPArp = Interface->ai_parpaddr; CurrentPArp != NULL; + CurrentPArp = CurrentPArp->apa_next) { + // See if this guy matches. + Mask = CurrentPArp->apa_mask; + MatchAddress = Address & Mask; + if (IP_ADDR_EQUAL(CurrentPArp->apa_addr, MatchAddress)) { + // He matches. We need to make a few more checks to make sure + // we don't reply to a broadcast address. + if (Mask == HOST_MASK) { + // We're matching the whole address, so it's OK. + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } + // See if the non-mask part it all-zeros. Since the mask presumably + // covers a subnet, this trick will prevent us from replying to + // a zero host part. + if (IP_ADDR_EQUAL(MatchAddress, Address)) + continue; + + // See if the host part is all ones. + if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~Mask))) + continue; + + // If the mask we were given is not the net mask for this address, + // we'll need to repeat the above checks. + NetMask = IPNetMask(Address); + if (NetMask != Mask) { + + MatchAddress = Address & NetMask; + if (IP_ADDR_EQUAL(MatchAddress, Address)) + continue; + + if (IP_ADDR_EQUAL(Address, MatchAddress | + (IP_LOCAL_BCST & ~NetMask))) + continue; + } + + // If we get to this point we've passed all the tests, so it's + // local. + CTEFreeLock(&Interface->ai_lock, Handle); + return TRUE; + } + } + + CTEFreeLock(&Interface->ai_lock, Handle); + return FALSE; + +} + + +#ifdef VXD + + +#ifndef CHICAGO +extern void DisplayPopup(uchar *Msg); +uchar CMsg1[] = "The system has detected a conflict for IP address "; +uchar CMsg2[] = " with the system having hardware address "; +uchar CMsg3[] = ". The local interface has been disabled"; + +uchar CMsg[sizeof(CMsg1) - 1 + sizeof(CMsg2) - 1 + sizeof(CMsg3) - 1 + + ((sizeof(IPAddr) * 4) - 1) + ((ARP_802_ADDR_LENGTH * 3) - 1) + + 1 + 1]; +#else +extern void NotifyConflictProc(CTEEvent *Event, void *Context); +extern void DisplayConflictPopup(uchar *IPAddr, uchar *HWAddr, uint Shutoff); +#endif // CHICAGO + +#endif // NT + +//* NotifyConflictProc - Notify the user of an address conflict. +// +// Called when we need to notify the user of an address conflict. The +// exact mechanism is system dependent, but generally involves a popup. +// +// Input: Event - Event that fired. +// Context - Pointer to ARPNotifyStructure. +// +// Returns: Nothing. +// +void +#ifndef CHICAGO +NotifyConflictProc(CTEEvent *Event, void *Context) +#else +DisplayConflictProc(void *Context) +#endif +{ +#ifdef VXD + uchar IPAddrBuffer[(sizeof(IPAddr) * 4)]; + uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; + uint i; + uint IPAddrCharCount; + ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context; + +#ifndef CHICAGO + uint TotalSize; + + CTEMemCopy(CMsg, CMsg1, sizeof(CMsg1) - 1); + TotalSize = sizeof(CMsg1) - 1; +#endif + + // Convert the IP address into a string. + IPAddrCharCount = 0; + + for (i = 0; i < sizeof(IPAddr); i++) { + uint CurrentByte; + + CurrentByte = NotifyStruct->ans_addr & 0xff; + if (CurrentByte > 99) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0'; + CurrentByte %= 100; + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } else if (CurrentByte > 9) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } + + IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0'; + if (i != (sizeof(IPAddr) - 1)) + IPAddrBuffer[IPAddrCharCount++] = '.'; + + NotifyStruct->ans_addr >>= 8; + } + +#ifndef CHICAGO + CTEMemCopy(&CMsg[TotalSize], IPAddrBuffer, IPAddrCharCount); + TotalSize += IPAddrCharCount; + + CTEMemCopy(&CMsg[TotalSize], CMsg2, sizeof(CMsg2) - 1); + TotalSize += sizeof(CMsg2) - 1; +#else + IPAddrBuffer[IPAddrCharCount] = '\0'; +#endif + + for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) { + uchar CurrentHalf; + + CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4; + HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f; + HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + if (i != (NotifyStruct->ans_hwaddrlen - 1)) + HWAddrBuffer[(i*3)+2] = ':'; + } + +#ifndef CHICAGO + CTEMemCopy(&CMsg[TotalSize], HWAddrBuffer, + (NotifyStruct->ans_hwaddrlen * 3) - 1); + TotalSize += (NotifyStruct->ans_hwaddrlen * 3) - 1; + + if (NotifyStruct->ans_shutoff) { + CTEMemCopy(&CMsg[TotalSize], CMsg3, sizeof(CMsg3) - 1); + TotalSize += sizeof(CMsg3) - 1; + } + + CMsg[TotalSize] = '.'; + CMsg[TotalSize+1] = '\0'; + + DisplayPopup(CMsg); +#else + HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0'; + DisplayConflictPopup(IPAddrBuffer, HWAddrBuffer, NotifyStruct->ans_shutoff); + CTEFreeMem(NotifyStruct); +#endif + +#else // VXD + + ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context; + PWCHAR stringList[2]; + uchar IPAddrBuffer[(sizeof(IPAddr) * 4)]; + uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; + WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)]; + WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)]; + uint i; + uint IPAddrCharCount; + UNICODE_STRING unicodeString; + ANSI_STRING ansiString; + + + PAGED_CODE(); + + // + // Convert the IP address into a string. + // + IPAddrCharCount = 0; + + for (i = 0; i < sizeof(IPAddr); i++) { + uint CurrentByte; + + CurrentByte = NotifyStruct->ans_addr & 0xff; + if (CurrentByte > 99) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0'; + CurrentByte %= 100; + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } else if (CurrentByte > 9) { + IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0'; + CurrentByte %= 10; + } + + IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0'; + if (i != (sizeof(IPAddr) - 1)) + IPAddrBuffer[IPAddrCharCount++] = '.'; + + NotifyStruct->ans_addr >>= 8; + } + + // + // Convert the hardware address into a string. + // + for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) { + uchar CurrentHalf; + + CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4; + HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f; + HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' : + (CurrentHalf - 10) + 'A'); + if (i != (NotifyStruct->ans_hwaddrlen - 1)) + HWAddrBuffer[(i*3)+2] = ':'; + } + + // + // Unicode the strings. + // + *unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL; + + unicodeString.Buffer = unicodeIPAddrBuffer; + unicodeString.Length = 0; + unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1); + ansiString.Buffer = IPAddrBuffer; + ansiString.Length = IPAddrCharCount; + ansiString.MaximumLength = IPAddrCharCount; + + RtlAnsiStringToUnicodeString( + &unicodeString, + &ansiString, + FALSE + ); + + stringList[0] = unicodeIPAddrBuffer; + + unicodeString.Buffer = unicodeHWAddrBuffer; + unicodeString.Length = 0; + unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3); + ansiString.Buffer = HWAddrBuffer; + ansiString.Length = (NotifyStruct->ans_hwaddrlen * 3) - 1; + ansiString.MaximumLength = NotifyStruct->ans_hwaddrlen * 3; + + RtlAnsiStringToUnicodeString( + &unicodeString, + &ansiString, + FALSE + ); + + stringList[1] = unicodeHWAddrBuffer; + + // + // Kick off a popup and log an event. + // + if (NotifyStruct->ans_shutoff) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADDRESS_CONFLICT1, + 0, + 2, + stringList, + 0, + NULL + ); + + IoRaiseInformationalHardError( + STATUS_IP_ADDRESS_CONFLICT1, + NULL, + NULL + ); + } + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADDRESS_CONFLICT2, + 0, + 2, + stringList, + 0, + NULL + ); + + IoRaiseInformationalHardError( + STATUS_IP_ADDRESS_CONFLICT2, + NULL, + NULL + ); + } + + CTEFreeMem(NotifyStruct); + +#endif // VXD + + return; +} + + +//* HandleARPPacket - Process an incoming ARP packet. +// +// This is the main routine to process an incoming ARP packet. We look at all ARP frames, +// and update our cache entry for the source address if one exists. Else, if we are the +// target we create an entry if one doesn't exist. Finally, we'll handle the opcode, +// responding if this is a request or sending pending packets if this is a response. +// +// Entry: Interface - Pointer to interface structure for this adapter. +// Header - Pointer to header buffer. +// HeaderSize - Size of header buffer. +// ARPHdr - ARP packet header. +// ARPHdrSize - Size of ARP header. +// ProtOffset - Offset into original data field of arp header. +// Will be non-zero if we're using SNAP. +// +// Returns: An NDIS_STATUS value to be returned to the NDIS driver. +// +NDIS_STATUS +HandleARPPacket(ARPInterface *Interface, void *Header, uint HeaderSize, + ARPHeader UNALIGNED *ARPHdr, uint ARPHdrSize, uint ProtOffset) +{ + ARPTableEntry *Entry; // Entry in ARP table + CTELockHandle LHandle, TableHandle; + RC UNALIGNED *SourceRoute = (RC UNALIGNED *)NULL; // Pointer to Source Route info, if any. + uint SourceRouteSize = 0; + ulong Now = CTESystemUpTime(); + uchar LocalAddr; + uint LocalAddrAge; + uchar *SHAddr, *DHAddr; + IPAddr UNALIGNED *SPAddr, *DPAddr; + ENetHeader *ENetHdr; + TRHeader *TRHdr; + FDDIHeader *FHdr; + ARCNetHeader *AHdr; + ushort MaxMTU; + uint UseSNAP; + SetAddrControl *SAC; + SetAddrRtn Rtn = NULL; + + // We examine all ARP frames. If we find the source address in the ARP table, we'll + // update the hardware address and set the state to valid. If we're the + // target and he's not in the table, we'll add him. Otherwise if we're the + // target and this is a response we'll send any pending packets to him. + if (Interface->ai_media != NdisMediumArcnet878_2) { + if (ARPHdrSize < sizeof(ARPHeader)) + return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small. + + if (ARPHdr->ah_hw != net_short(ARP_HW_ENET) && + ARPHdr->ah_hw != net_short(ARP_HW_802)) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type + + if (ARPHdr->ah_hlen != ARP_802_ADDR_LENGTH) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length. + + if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize == 0) + UseSNAP = FALSE; + else + UseSNAP = (ProtOffset != 0); + + // Figure out SR size on TR. + if (Interface->ai_media == NdisMedium802_5) { + // Check for source route information. SR is present if the header + // size is greater than the standard TR header size. If the SR is + // only an RC field, we ignore it because it came from the same + // ring which is the same as no SR. + + if ((HeaderSize - sizeof(TRHeader)) > sizeof(RC)) { + SourceRouteSize = HeaderSize - sizeof(TRHeader); + SourceRoute = (RC UNALIGNED *)((uchar *)Header + + sizeof(TRHeader)); + } + } + + SHAddr = ARPHdr->ah_shaddr; + SPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_spaddr; + DHAddr = ARPHdr->ah_dhaddr; + DPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_dpaddr; + + } else { + if (ARPHdrSize < (sizeof(ARPHeader) - ARCNET_ARPHEADER_ADJUSTMENT)) + return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small. + + if (ARPHdr->ah_hw != net_short(ARP_HW_ARCNET)) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type + + if (ARPHdr->ah_hlen != 1) + return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length. + + UseSNAP = FALSE; + SHAddr = ARPHdr->ah_shaddr; + SPAddr = (IPAddr UNALIGNED *)(SHAddr + 1); + DHAddr = (uchar *)SPAddr + sizeof(IPAddr); + DPAddr = (IPAddr UNALIGNED *)(DHAddr + 1); + } + + if (ARPHdr->ah_pro != net_short(ARP_ETYPE_IP)) + return NDIS_STATUS_NOT_RECOGNIZED; // Unsupported protocol type. + + if (ARPHdr->ah_plen != sizeof(IPAddr)) + return NDIS_STATUS_NOT_RECOGNIZED; + + if (IP_ADDR_EQUAL(*SPAddr, NULL_IP_ADDR)) + return NDIS_STATUS_NOT_RECOGNIZED; + + // First, let's see if we have an address conflict. + LocalAddrAge = IsLocalAddr(Interface, *SPAddr); + if (LocalAddrAge != ARPADDR_NOT_LOCAL) { + // The source IP address is one of ours. See if the source h/w address + // is ours also. + if (ARPHdr->ah_hlen != Interface->ai_addrlen || + CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) { + + uint Shutoff; + ARPNotifyStruct *NotifyStruct; + + // This isn't from us; we must have an address conflict somewhere. + // We always log an error about this. If what triggered this is a + // response and the address in conflict is young, we'll turn off + // the interface. + if (LocalAddrAge != ARPADDR_OLD_LOCAL && + ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) { + // Send an arp request with the owner's address to reset the + // caches. + + CTEGetLock(&Interface->ai_lock, &LHandle); + Interface->ai_state = INTERFACE_DOWN; + Interface->ai_adminstate = IF_STATUS_DOWN; + if (Interface->ai_ipaddr.aia_context != NULL) { + SAC = (SetAddrControl *)Interface->ai_ipaddr.aia_context; + Rtn = (SetAddrRtn)SAC->sac_rtn; + Interface->ai_ipaddr.aia_context = NULL; + } + CTEFreeLock(&Interface->ai_lock, LHandle); + + SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL, + SHAddr, FALSE); // Send a request. + + Shutoff = TRUE; + + if (Rtn != NULL) { + // + // this is a dhcp adapter. report the conflict to + // CompleteIPSetNTEAddrRequest so IOCTL_IP_SET_ADDRESS will + // be completed + // + + (*Rtn)(SAC, IP_GENERAL_FAILURE); + + // + // don't display a warning dialog in this case - DHCP will + // alert the user + // + + goto no_dialog; + } + } else { + if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) && + (IsLocalAddr(Interface, *DPAddr) != ARPADDR_NOT_LOCAL)) { + // Send a response. + SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr, + SourceRoute, SourceRouteSize, UseSNAP); + } + Shutoff = FALSE; + } + + // Now allocate a structure, and schedule an event to notify + // the user. + NotifyStruct = CTEAllocMem(offsetof(ARPNotifyStruct, ans_hwaddr) + + ARPHdr->ah_hlen); + if (NotifyStruct != NULL) { + NotifyStruct->ans_addr = *SPAddr; + NotifyStruct->ans_shutoff = Shutoff; + NotifyStruct->ans_hwaddrlen = (uint)ARPHdr->ah_hlen; + CTEMemCopy(NotifyStruct->ans_hwaddr, SHAddr, + ARPHdr->ah_hlen); + CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc); + CTEScheduleEvent(&NotifyStruct->ans_event, NotifyStruct); + } + + + no_dialog: + ; + + } + return NDIS_STATUS_NOT_RECOGNIZED; + } + + CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle); + MaxMTU = Interface->ai_mtu; + + LocalAddr = ARPLocalAddr(Interface, *DPAddr); + Entry = ARPLookup(Interface, *SPAddr, &LHandle); + if (Entry == (ARPTableEntry *)NULL) { + + // Didn't find him, create one if it's for us. The call to ARPLookup + // returned with the ARPTblLock held, so we need to free it. + + CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle); + if (LocalAddr) + Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle); + else + return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us. + } else { + CTEFreeLock(&Interface->ai_ARPTblLock, LHandle); + LHandle = TableHandle; + } + + // At this point, entry should be valid, and we hold the lock on the entry + // in LHandle. + + if (Entry != (ARPTableEntry *)NULL) { + PNDIS_PACKET Packet; // Packet to be sent. + + // If the entry is already static, we'll want to leave it as static. + if (Entry->ate_valid == ALWAYS_VALID) + Now = ALWAYS_VALID; + + // OK, we have an entry to use, and hold the lock on it. Fill in the + // required fields. + switch (Interface->ai_media) { + + case NdisMedium802_3: + + // This is an Ethernet. + ENetHdr = (ENetHeader *)Entry->ate_addr; + + CTEMemCopy(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH); + CTEMemCopy(ENetHdr->eh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + ENetHdr->eh_type = net_short(ARP_ETYPE_IP); + + // If we're using SNAP on this entry, copy in the SNAP header. + if (UseSNAP) { + CTEMemCopy(&Entry->ate_addr[sizeof(ENetHeader)], ARPSNAP, + sizeof(SNAPHeader)); + Entry->ate_addrlength = (uchar)(sizeof(ENetHeader) + + sizeof(SNAPHeader)); + *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + } else + Entry->ate_addrlength = sizeof(ENetHeader); + + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; // Mark last time he was + // valid. + break; + + case NdisMedium802_5: + + // This is TR. + // For token ring we have to deal with source routing. There's + // a special case to handle multiple responses for an all-routes + // request - if the entry is currently good and we knew it was + // valid recently, we won't update the entry. + + + if (Entry->ate_state != ARP_GOOD || + (Now - Entry->ate_valid) > ARP_RESOLVE_TIMEOUT) { + + TRHdr = (TRHeader *)Entry->ate_addr; + + // We need to update a TR entry. + TRHdr->tr_ac = ARP_AC; + TRHdr->tr_fc = ARP_FC; + CTEMemCopy(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH); + CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + if (SourceRoute != (RC UNALIGNED *)NULL) { + uchar MaxIFieldBits; + + // We have source routing information. + CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)], + SourceRoute, SourceRouteSize); + MaxIFieldBits = (SourceRoute->rc_dlf & RC_LF_MASK) >> + LF_BIT_SHIFT; + MaxIFieldBits = MIN(MaxIFieldBits, MAX_LF_BITS); + MaxMTU = IFieldSize[MaxIFieldBits]; + + // The new MTU we've computed is the max I-field size, + // which doesn't include source routing info but + // does include SNAP info. Subtract off the SNAP size. + MaxMTU -= sizeof(SNAPHeader); + + TRHdr->tr_saddr[0] |= TR_RII; + (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_dlf ^= + RC_DIR; + // Make sure it's non-broadcast. + (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_blen &= + RC_LENMASK; + + } + CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)+SourceRouteSize], + ARPSNAP, sizeof(SNAPHeader)); + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; + Entry->ate_addrlength = (uchar)(sizeof(TRHeader) + + SourceRouteSize + sizeof(SNAPHeader)); + *(ushort *)&Entry->ate_addr[Entry->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + } + break; + case NdisMediumFddi: + FHdr = (FDDIHeader *)Entry->ate_addr; + + FHdr->fh_pri = ARP_FDDI_PRI; + CTEMemCopy(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH); + CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + CTEMemCopy(&Entry->ate_addr[sizeof(FDDIHeader)], ARPSNAP, + sizeof(SNAPHeader)); + Entry->ate_addrlength = (uchar)(sizeof(FDDIHeader) + + sizeof(SNAPHeader)); + *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; // Mark last time he was + // valid. + break; + case NdisMediumArcnet878_2: + AHdr = (ARCNetHeader *)Entry->ate_addr; + AHdr->ah_saddr = Interface->ai_addr[0]; + AHdr->ah_daddr = *SHAddr; + AHdr->ah_prot = ARP_ARCPROT_IP; + Entry->ate_addrlength = sizeof(ARCNetHeader); + Entry->ate_state = ARP_GOOD; + Entry->ate_valid = Now; // Mark last time he was + // valid. + break; + default: + DEBUGCHK; + break; + } + + // At this point we've updated the entry, and we still hold the lock + // on it. If we have a packet that was pending to be sent, send it now. + // Otherwise just free the lock. + + Packet = Entry->ate_packet; + + if (Packet != NULL) { + // We have a packet to send. + CTEAssert(Entry->ate_state == ARP_GOOD); + + Entry->ate_packet = NULL; + + if (ARPSendData(Interface, Packet, Entry, LHandle) != NDIS_STATUS_PENDING) + IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS); + } else + CTEFreeLock(&Entry->ate_lock, LHandle); + } + + // See if the MTU is less than our local one. This should only happen + // in the case of token ring source routing. + if (MaxMTU < Interface->ai_mtu) { + LLIPAddrMTUChange LAM; + + LAM.lam_mtu = MaxMTU; + LAM.lam_addr = *SPAddr; + + // It is less. Notify IP. + CTEAssert(Interface->ai_media == NdisMedium802_5); + IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE, + &LAM, sizeof(LLIPAddrMTUChange)); + + } + + // At this point we've updated the entry (if we had one), and we've free + // all locks. If it's for a local address and it's a request, reply to + // it. + if (LocalAddr) { // It's for us. + if (ARPHdr->ah_opcode == net_short(ARP_REQUEST)) { + // It's a request, and we need to respond. + SendARPReply(Interface, *SPAddr, *DPAddr, + SHAddr, SourceRoute, SourceRouteSize, UseSNAP); + } + } + + + return NDIS_STATUS_SUCCESS; +} + + +//* InitAdapter - Initialize an adapter. +// +// Called when an adapter is open to finish initialization. We set +// up our lookahead size and packet filter, and we're ready to go. +// +// Entry: +// adapter - Pointer to an adapter structure for the adapter to be +// initialized. +// +// Exit: Nothing +// +void +InitAdapter(ARPInterface *Adapter) +{ + NDIS_STATUS Status; + CTELockHandle Handle; + ARPIPAddr *Addr, *OldAddr; + + if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation, + OID_GEN_CURRENT_LOOKAHEAD, &ARPLookahead, sizeof(ARPLookahead), NULL)) + != NDIS_STATUS_SUCCESS) { + Adapter->ai_state = INTERFACE_DOWN; + return; + } + + if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint), + NULL)) == NDIS_STATUS_SUCCESS) { + Adapter->ai_adminstate = IF_STATUS_UP; + Adapter->ai_operstate = IF_STATUS_UP; + Adapter->ai_state = INTERFACE_UP; + // Now walk through any addresses we have, and ARP for them. + CTEGetLock(&Adapter->ai_lock, &Handle); + OldAddr = NULL; + Addr = &Adapter->ai_ipaddr; + do { + if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { + IPAddr Address = Addr->aia_addr; + + Addr->aia_age = ARPADDR_NEW_LOCAL; + CTEFreeLock(&Adapter->ai_lock, Handle); + OldAddr = Addr; + SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL, + NULL, TRUE); + CTEGetLock(&Adapter->ai_lock, &Handle); + } + Addr = &Adapter->ai_ipaddr; + while (Addr != OldAddr && Addr != NULL) + Addr = Addr->aia_next; + if (Addr != NULL) + Addr = Addr->aia_next; + } while (Addr != NULL); + + CTEFreeLock(&Adapter->ai_lock, Handle); + + } else + Adapter->ai_state = INTERFACE_DOWN; + +} + +//** ARPOAComplete - ARP Open adapter complete handler. +// +// This routine is called by the NDIS driver when an open adapter +// call completes. Presumably somebody is blocked waiting for this, so +// we'll wake him up now. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Status - Final status of command. +// ErrorStatus - Final error status. +// +// Exit: Nothing. +// +void NDIS_API +ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus) +{ + ARPInterface *ai = (ARPInterface *)Handle; // For compiler. + + CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status. + +} + +//** ARPCAComplete - ARP close adapter complete handler. +// +// This routine is called by the NDIS driver when a close adapter +// call completes. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status) +{ + ARPInterface *ai = (ARPInterface *)Handle; // For compiler. + + CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status. + +} + +//** ARPSendComplete - ARP send complete handler. +// +// This routine is called by the NDIS driver when a send completes. +// This is a pretty time critical operation, we need to get through here +// quickly. We'll strip our buffer off and put it back, and call the upper +// later send complete handler. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Packet - A pointer to the packet that was sent. +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status) +{ + ARPInterface *Interface = (ARPInterface *)Handle; + PacketContext *PC = (PacketContext *)Packet->ProtocolReserved; + PNDIS_BUFFER Buffer; + + Interface->ai_qlen--; +#ifdef VXD + CTEAssert(*(int *)&Interface->ai_qlen >= 0); +#endif + + if (Status == NDIS_STATUS_SUCCESS) { + Interface->ai_outoctets += Packet->Private.TotalLength; + } else { + if (Status == NDIS_STATUS_RESOURCES) + Interface->ai_outdiscards++; + else + Interface->ai_outerrors++; + } + + // Get first buffer on packet. + NdisUnchainBufferAtFront(Packet, &Buffer); +#ifdef DEBUG + if (Buffer == (PNDIS_BUFFER)NULL) // No buffer! + DEBUGCHK; +#endif + + FreeARPBuffer(Interface, Buffer); // Free it up. + if (PC->pc_common.pc_owner != PACKET_OWNER_LINK) { // We don't own this one. + IPSendComplete(Interface->ai_context, Packet, Status); + return; + } + + // This packet belongs to us, so free it. + NdisFreePacket(Packet); + +} + +//** ARPTDComplete - ARP transfer data complete handler. +// +// This routine is called by the NDIS driver when a transfer data +// call completes. Since we never transfer data ourselves, this must be +// from the upper layer. We'll just call his routine and let him deal +// with it. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Packet - A pointer to the packet used for the TD. +// Status - Final status of command. +// BytesCopied - Count of bytes copied. +// +// Exit: Nothing. +// +void NDIS_API +ARPTDComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status, + uint BytesCopied) +{ + ARPInterface *ai = (ARPInterface *)Handle; + + IPTDComplete(ai->ai_context, Packet, Status, BytesCopied); + +} + +//** ARPResetComplete - ARP reset complete handler. +// +// This routine is called by the NDIS driver when a reset completes. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPResetComplete(NDIS_HANDLE Handle, NDIS_STATUS Status) +{ +} + +//** ARPRequestComplete - ARP request complete handler. +// +// This routine is called by the NDIS driver when a general request +// completes. ARP blocks on all requests, so we'll just wake up +// whoever's blocked on this request. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Request - A pointer to the request that completed. +// Status - Final status of command. +// +// Exit: Nothing. +// +void NDIS_API +ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST Request, + NDIS_STATUS Status) +{ + ARPInterface *ai = (ARPInterface *)Handle; + + CTESignal(&ai->ai_block, (uint)Status); +} + +//** ARPRcv - ARP receive data handler. +// +// This routine is called when data arrives from the NDIS driver. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// Context - NDIS context to be used for TD. +// Header - Pointer to header +// HeaderSize - Size of header +// Data - Pointer to buffer of received data +// Size - Byte count of data in buffer. +// TotalSize - Byte count of total packet size. +// +// Exit: Status indicating whether or not we took the packet. +// +NDIS_STATUS NDIS_API +ARPRcv(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize, + void *Data, uint Size, uint TotalSize) +{ + ARPInterface *Interface = Handle; // Interface for this driver. + ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *)Header; + SNAPHeader UNALIGNED *SNAPHdr; + ushort type; // Protocol type + uint ProtOffset; // Offset in Data to non-media info. + uint NUCast; // TRUE if the frame is not + // a unicast frame. + + if (Interface->ai_state == INTERFACE_UP && + HeaderSize >= (uint)Interface->ai_hdrsize) { + + Interface->ai_inoctets += TotalSize; + + if (Interface->ai_media != NdisMediumArcnet878_2) { + if (Interface->ai_media == NdisMedium802_3 && + (type = net_short(EHdr->eh_type)) >= MIN_ETYPE) + ProtOffset = 0; + else { + SNAPHdr = (SNAPHeader UNALIGNED *)Data; + + if (Size >= sizeof(SNAPHeader) && + SNAPHdr->sh_dsap == SNAP_SAP && + SNAPHdr->sh_ssap == SNAP_SAP && + SNAPHdr->sh_ctl == SNAP_UI) { + type = net_short(SNAPHdr->sh_etype); + ProtOffset = sizeof(SNAPHeader); + } else { + // BUGBUG handle XID/TEST here. + Interface->ai_uknprotos++; + return NDIS_STATUS_NOT_RECOGNIZED; + } + } + } else { + ARCNetHeader UNALIGNED *AH = (ARCNetHeader UNALIGNED *)Header; + + ProtOffset = 0; + if (AH->ah_prot == ARP_ARCPROT_IP) + type = ARP_ETYPE_IP; + else + if (AH->ah_prot == ARP_ARCPROT_ARP) + type = ARP_ETYPE_ARP; + else + type = 0; + } + + NUCast = ((*((uchar UNALIGNED *)EHdr + Interface->ai_bcastoff) & + Interface->ai_bcastmask) == Interface->ai_bcastval) ? + AI_NONUCAST_INDEX : AI_UCAST_INDEX; + + if (type == ARP_ETYPE_IP) { + + (Interface->ai_inpcount[NUCast])++; + IPRcv(Interface->ai_context, (uchar *)Data+ProtOffset, + Size-ProtOffset, TotalSize-ProtOffset, Context, ProtOffset, + NUCast); + return NDIS_STATUS_SUCCESS; + } + else { + if (type == ARP_ETYPE_ARP) { + (Interface->ai_inpcount[NUCast])++; + return HandleARPPacket(Interface, Header, HeaderSize, + (ARPHeader *)((uchar *)Data+ProtOffset), Size-ProtOffset, + ProtOffset); + } else { + Interface->ai_uknprotos++; + return NDIS_STATUS_NOT_RECOGNIZED; + } + } + } else { + // Interface is marked as down. + return NDIS_STATUS_NOT_RECOGNIZED; + } +} + +//** ARPRcvComplete - ARP receive complete handler. +// +// This routine is called by the NDIS driver after some number of +// receives. In some sense, it indicates 'idle time'. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// +// Exit: Nothing. +// +void NDIS_API +ARPRcvComplete(NDIS_HANDLE Handle) +{ + IPRcvComplete(); + +} + + +//** ARPStatus - ARP status handler. +// +// Called by the NDIS driver when some sort of status change occurs. +// We take action depending on the type of status. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// GStatus - General type of status that caused the call. +// Status - Pointer to a buffer of status specific information. +// StatusSize - Size of the status buffer. +// +// Exit: Nothing. +// +void NDIS_API +ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint + StatusSize) +{ + ARPInterface *ai = (ARPInterface *)Handle; + + IPStatus(ai->ai_context, GStatus, Status, StatusSize); + +} + +//** ARPStatusComplete - ARP status complete handler. +// +// A routine called by the NDIS driver so that we can do postprocessing +// after a status event. +// +// Entry: +// Handle - The binding handle we specified (really a pointer to an AI). +// +// Exit: Nothing. +// +void NDIS_API +ARPStatusComplete(NDIS_HANDLE Handle) +{ + +} + +extern void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus, + NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2); + +extern void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus, + NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext); +extern void NDIS_API ARPUnloadProtocol(void); + +#ifndef NT +NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = { + NDIS_MAJOR_VERSION, + NDIS_MINOR_VERSION, + 0, + ARPOAComplete, + ARPCAComplete, + ARPSendComplete, + ARPTDComplete, + ARPResetComplete, + ARPRequestComplete, + ARPRcv, + ARPRcvComplete, + ARPStatus, + ARPStatusComplete, +#ifdef CHICAGO + ARPBindAdapter, + ARPUnbindAdapter, + ARPUnloadProtocol, +#endif + { sizeof(TCP_NAME), + sizeof(TCP_NAME), + 0 + } +}; +#else // NT +NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = { + NDIS_MAJOR_VERSION, + NDIS_MINOR_VERSION, + 0, + ARPOAComplete, + ARPCAComplete, + ARPSendComplete, + ARPTDComplete, + ARPResetComplete, + ARPRequestComplete, + ARPRcv, + ARPRcvComplete, + ARPStatus, + ARPStatusComplete, + { sizeof(TCP_NAME), + sizeof(TCP_NAME), + 0 +#ifdef _PNP_POWER + }, + NULL, + ARPBindAdapter, + ARPUnbindAdapter, + NULL +#else + } +#endif +}; +#endif + +//* ARPReadNext - Read the next entry in the ARP table. +// +// Called by the GetInfo code to read the next ATE in the table. We assume +// the context passed in is valid, and the caller has the ARP TableLock. +// +// Input: Context - Pointer to a IPNMEContext. +// Interface - Pointer to interface for table to read on. +// Buffer - Pointer to an IPNetToMediaEntry structure. +// +// Returns: TRUE if more data is available to be read, FALSE is not. +// +uint +ARPReadNext(void *Context, ARPInterface *Interface, void *Buffer) +{ + IPNMEContext *NMContext = (IPNMEContext *)Context; + IPNetToMediaEntry *IPNMEntry = (IPNetToMediaEntry *)Buffer; + CTELockHandle Handle; + ARPTableEntry *CurrentATE; + uint i; + ARPTable *Table = Interface->ai_ARPTbl; + uint AddrOffset; + + CurrentATE = NMContext->inc_entry; + + // Fill in the buffer. + CTEGetLock(&CurrentATE->ate_lock, &Handle); + IPNMEntry->inme_index = Interface->ai_index; + IPNMEntry->inme_physaddrlen = Interface->ai_addrlen; + + + switch (Interface->ai_media) { + case NdisMedium802_3: + AddrOffset = 0; + break; + case NdisMedium802_5: + AddrOffset = offsetof(struct TRHeader, tr_daddr); + break; + case NdisMediumFddi: + AddrOffset = offsetof(struct FDDIHeader, fh_daddr); + break; + case NdisMediumArcnet878_2: + AddrOffset = offsetof(struct ARCNetHeader, ah_daddr); + break; + default: + AddrOffset = 0; + break; + } + + CTEMemCopy(IPNMEntry->inme_physaddr, &CurrentATE->ate_addr[AddrOffset], + Interface->ai_addrlen); + IPNMEntry->inme_addr = CurrentATE->ate_dest; + + if (CurrentATE->ate_state == ARP_GOOD) + IPNMEntry->inme_type = (CurrentATE->ate_valid == ALWAYS_VALID ? + INME_TYPE_STATIC : INME_TYPE_DYNAMIC); + else + IPNMEntry->inme_type = INME_TYPE_INVALID; + CTEFreeLock(&CurrentATE->ate_lock, Handle); + + // We've filled it in. Now update the context. + if (CurrentATE->ate_next != NULL) { + NMContext->inc_entry = CurrentATE->ate_next; + return TRUE; + } else { + // The next ATE is NULL. Loop through the ARP Table looking for a new + // one. + i = NMContext->inc_index + 1; + while (i < ARP_TABLE_SIZE) { + if ((*Table)[i] != NULL) { + NMContext->inc_entry = (*Table)[i]; + NMContext->inc_index = i; + return TRUE; + break; + } else + i++; + } + + NMContext->inc_index = 0; + NMContext->inc_entry = NULL; + return FALSE; + } + +} + +//* ARPValidateContext - Validate the context for reading an ARP table. +// +// Called to start reading an ARP table sequentially. We take in +// a context, and if the values are 0 we return information about the +// first route in the table. Otherwise we make sure that the context value +// is valid, and if it is we return TRUE. +// We assume the caller holds the ARPInterface lock. +// +// Input: Context - Pointer to a RouteEntryContext. +// Interface - Pointer to an interface +// Valid - Where to return information about context being +// valid. +// +// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set +// to TRUE if input context is valid +// +uint +ARPValidateContext(void *Context, ARPInterface *Interface, uint *Valid) +{ + IPNMEContext *NMContext = (IPNMEContext *)Context; + uint i; + ARPTableEntry *TargetATE; + ARPTableEntry *CurrentATE; + ARPTable *Table = Interface->ai_ARPTbl; + + i = NMContext->inc_index; + TargetATE = NMContext->inc_entry; + + // If the context values are 0 and NULL, we're starting from the beginning. + if (i == 0 && TargetATE == NULL) { + *Valid = TRUE; + do { + if ((CurrentATE = (*Table)[i]) != NULL) { + break; + } + i++; + } while (i < ARP_TABLE_SIZE); + + if (CurrentATE != NULL) { + NMContext->inc_index = i; + NMContext->inc_entry = CurrentATE; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + if (i < ARP_TABLE_SIZE) { + CurrentATE = (*Table)[i]; + while (CurrentATE != NULL) { + if (CurrentATE == TargetATE) { + *Valid = TRUE; + return TRUE; + break; + } else { + CurrentATE = CurrentATE->ate_next; + } + } + + } + + // If we get here, we didn't find the matching ATE. + *Valid = FALSE; + return FALSE; + + } + +} + +#define IFE_FIXED_SIZE offsetof(struct IFEntry, if_descr) + +//* ARPQueryInfo - ARP query information handler. +// +// Called to query information about the ARP table or statistics about the +// actual interface. +// +// Input: IFContext - Interface context (pointer to an ARPInterface). +// ID - TDIObjectID for object. +// Buffer - Buffer to put data into. +// Size - Pointer to size of buffer. On return, filled with +// bytes copied. +// Context - Pointer to context block. +// +// Returns: Status of attempt to query information. +// +int +ARPQueryInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, + void *Context) +{ + ARPInterface *AI = (ARPInterface *)IFContext; + uint Offset = 0; + uint BufferSize = *Size; + CTELockHandle Handle; + uint ContextValid, DataLeft; + uint BytesCopied = 0; + uchar InfoBuff[sizeof(IFEntry)]; + uint Entity; + uint Instance; + + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if ((Entity != AT_ENTITY || Instance != AI->ai_atinst) && + (Entity != IF_ENTITY || Instance != AI->ai_ifinst)) { + return TDI_INVALID_REQUEST; + } + + *Size = 0; // In case of an error. + + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_id == ENTITY_TYPE_ID) { + // He's trying to see what type we are. + if (BufferSize >= sizeof(uint)) { + *(uint *)&InfoBuff[0] = (Entity == AT_ENTITY) ? AT_ARP : + IF_MIB; + (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } + + // Might be able to handle this. + if (Entity == AT_ENTITY) { + // It's an address translation object. It could be a MIB object or + // an implementation specific object (the generic objects were handled + // above). + + if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { + ARPPArpAddr *PArpAddr; + + // It's an implementation specific ID. The only ones we handle + // are the PARP_COUNT_ID and the PARP_ENTRY ID. + + if (ID->toi_id == AT_ARP_PARP_COUNT_ID) { + // He wants to know the count. Just return that to him. + if (BufferSize >= sizeof(uint)) { + + CTEGetLock(&AI->ai_lock, &Handle); + + (void)CopyToNdis(Buffer, (uchar *)&AI->ai_parpcount, + sizeof(uint), &Offset); + + CTEFreeLock(&AI->ai_lock, Handle); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + + if (ID->toi_id != AT_ARP_PARP_ENTRY_ID) + return TDI_INVALID_PARAMETER; + + // It's for Proxy ARP entries. The context should be either NULL + // or a pointer to the next one to be read. + CTEGetLock(&AI->ai_lock, &Handle); + + PArpAddr = *(ARPPArpAddr **)Context; + + if (PArpAddr != NULL) { + ARPPArpAddr *CurrentPARP; + + // Loop through the P-ARP addresses on the interface, and + // see if we can find this one. + CurrentPARP = AI->ai_parpaddr; + while (CurrentPARP != NULL) { + if (CurrentPARP == PArpAddr) + break; + else + CurrentPARP = CurrentPARP->apa_next; + } + + // If we found a match, PARPAddr points to where to begin + // reading. Otherwise, fail the request. + if (CurrentPARP == NULL) { + // Didn't find a match, so fail the request. + CTEFreeLock(&AI->ai_lock, Handle); + return TDI_INVALID_PARAMETER; + } + } else + PArpAddr = AI->ai_parpaddr; + + // PARPAddr points to the next entry to put in the buffer, if + // there is one. + while (PArpAddr != NULL) { + if ((int)(BufferSize - BytesCopied) >= + (int)sizeof(ProxyArpEntry)) { + ProxyArpEntry *TempPArp; + + TempPArp = (ProxyArpEntry *)InfoBuff; + TempPArp->pae_status = PAE_STATUS_VALID; + TempPArp->pae_addr = PArpAddr->apa_addr; + TempPArp->pae_mask = PArpAddr->apa_mask; + BytesCopied += sizeof(ProxyArpEntry); + Buffer = CopyToNdis(Buffer, (uchar *)TempPArp, + sizeof(ProxyArpEntry), &Offset); + PArpAddr = PArpAddr->apa_next; + } else + break; + } + + // We're done copying. Free the lock and return the correct + // status. + CTEFreeLock(&AI->ai_lock, Handle); + *Size = BytesCopied; + **(ARPPArpAddr ***)&Context = PArpAddr; + return (PArpAddr == NULL) ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW; + } + + if (ID->toi_id == AT_MIB_ADDRXLAT_INFO_ID) { + AddrXlatInfo *AXI; + + // It's for the count. Just return the number of entries in the + // table. + if (BufferSize >= sizeof(AddrXlatInfo)) { + *Size = sizeof(AddrXlatInfo); + AXI = (AddrXlatInfo *)InfoBuff; + AXI->axi_count = AI->ai_count; + AXI->axi_index = AI->ai_index; + (void)CopyToNdis(Buffer, (uchar *)AXI, sizeof(AddrXlatInfo), + &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + + if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID) { + // He's trying to read the table. + // Make sure we have a valid context. + CTEGetLock(&AI->ai_ARPTblLock, &Handle); + DataLeft = ARPValidateContext(Context, AI, &ContextValid); + + // If the context is valid, we'll continue trying to read. + if (!ContextValid) { + CTEFreeLock(&AI->ai_ARPTblLock, Handle); + return TDI_INVALID_PARAMETER; + } + + while (DataLeft) { + // The invariant here is that there is data in the table to + // read. We may or may not have room for it. So DataLeft + // is TRUE, and BufferSize - BytesCopied is the room left + // in the buffer. + if ((int)(BufferSize - BytesCopied) >= + (int)sizeof(IPNetToMediaEntry)) { + DataLeft = ARPReadNext(Context, AI, InfoBuff); + BytesCopied += sizeof(IPNetToMediaEntry); + Buffer = CopyToNdis(Buffer, InfoBuff, + sizeof(IPNetToMediaEntry), &Offset); + } else + break; + + } + + *Size = BytesCopied; + + CTEFreeLock(&AI->ai_ARPTblLock, Handle); + return (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); + } + + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + // He must be asking for interface level information. See if we support + // what he's asking for. + if (ID->toi_id == IF_MIB_STATS_ID) { + IFEntry *IFE = (IFEntry *)InfoBuff; + + + // He's asking for statistics. Make sure his buffer is at least big + // enough to hold the fixed part. + + if (BufferSize < IFE_FIXED_SIZE) { + return TDI_BUFFER_TOO_SMALL; + } + + // He's got enough to hold the fixed part. Build the IFEntry structure, + // and copy it to his buffer. + IFE->if_index = AI->ai_index; + switch (AI->ai_media) { + case NdisMedium802_3: + IFE->if_type = IF_TYPE_ETHERNET; + break; + case NdisMedium802_5: + IFE->if_type = IF_TYPE_TOKENRING; + break; + case NdisMediumFddi: + IFE->if_type = IF_TYPE_FDDI; + break; + case NdisMediumArcnet878_2: + default: + IFE->if_type = IF_TYPE_OTHER; + break; + } + IFE->if_mtu = AI->ai_mtu; + IFE->if_speed = AI->ai_speed; + IFE->if_physaddrlen = AI->ai_addrlen; + CTEMemCopy(IFE->if_physaddr,AI->ai_addr, AI->ai_addrlen); + IFE->if_adminstatus = (uint)AI->ai_adminstate; + IFE->if_operstatus = (uint)AI->ai_operstate; + IFE->if_lastchange = AI->ai_lastchange; + IFE->if_inoctets = AI->ai_inoctets; + IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX]; + IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX]; + IFE->if_indiscards = AI->ai_indiscards; + IFE->if_inerrors = AI->ai_inerrors; + IFE->if_inunknownprotos = AI->ai_uknprotos; + IFE->if_outoctets = AI->ai_outoctets; + IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX]; + IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX]; + IFE->if_outdiscards = AI->ai_outdiscards; + IFE->if_outerrors = AI->ai_outerrors; + IFE->if_outqlen = AI->ai_qlen; + IFE->if_descrlen = AI->ai_desclen; + Buffer = CopyToNdis(Buffer, (uchar *)IFE, IFE_FIXED_SIZE, &Offset); + + // See if he has room for the descriptor string. + if (BufferSize >= (IFE_FIXED_SIZE + AI->ai_desclen)) { + // He has room. Copy it. + if (AI->ai_desclen != 0) { + (void)CopyToNdis(Buffer, AI->ai_desc, AI->ai_desclen, &Offset); + } + *Size = IFE_FIXED_SIZE + AI->ai_desclen; + return TDI_SUCCESS; + } else { + // Not enough room to copy the desc. string. + *Size = IFE_FIXED_SIZE; + return TDI_BUFFER_OVERFLOW; + } + + } + + return TDI_INVALID_PARAMETER; + +} + +//* ARPSetInfo - ARP set information handler. +// +// The ARP set information handler. We support setting of an I/F admin +// status, and setting/deleting of ARP table entries. +// +// Input: Context - Pointer to I/F to set on. +// ID - The object ID +// Buffer - Pointer to buffer containing value to set. +// Size - Size in bytes of Buffer. +// +// Returns: Status of attempt to set information. +// +int +ARPSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size) +{ + ARPInterface *Interface = (ARPInterface *)Context; + CTELockHandle Handle, EntryHandle; + int Status; + IFEntry *IFE = (IFEntry *)Buffer; + IPNetToMediaEntry *IPNME; + ARPTableEntry *PrevATE, *CurrentATE; + ARPTable *Table; + ENetHeader *Header; + uint Entity, Instance; + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) && + (Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) { + return TDI_INVALID_REQUEST; + } + + if (ID->toi_type != INFO_TYPE_PROVIDER) { + return TDI_INVALID_PARAMETER; + } + + // Might be able to handle this. + if (Entity == IF_ENTITY) { + + // It's for the I/F level, see if it's for the statistics. + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + if (ID->toi_id == IF_MIB_STATS_ID) { + // It's for the stats. Make sure it's a valid size. + if (Size >= IFE_FIXED_SIZE) { + // It's a valid size. See what he wants to do. + CTEGetLock(&Interface->ai_lock, &Handle); + switch (IFE->if_adminstatus) { + case IF_STATUS_UP: + // He's marking it up. If the operational state is + // alse up, mark the whole interface as up. + Interface->ai_adminstate = IF_STATUS_UP; + if (Interface->ai_operstate == IF_STATUS_UP) + Interface->ai_state = INTERFACE_UP; + Status = TDI_SUCCESS; + break; + case IF_STATUS_DOWN: + // He's taking it down. Mark both the admin state and + // the interface state down. + Interface->ai_adminstate = IF_STATUS_DOWN; + Interface->ai_state = INTERFACE_DOWN; + Status = TDI_SUCCESS; + break; + case IF_STATUS_TESTING: + // He's trying to cause up to do testing, which we + // don't support. Just return success. + Status = TDI_SUCCESS; + break; + default: + Status = TDI_INVALID_PARAMETER; + break; + } + CTEFreeLock(&Interface->ai_lock, Handle); + return Status; + } else + return TDI_INVALID_PARAMETER; + } else { + return TDI_INVALID_PARAMETER; + } + } + + // Not for the interface level. See if it's an implementation or protocol + // class. + if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) { + ProxyArpEntry *PArpEntry; + ARPIPAddr *Addr; + IPAddr AddAddr; + IPMask Mask; + + // It's for the implementation. It should be the proxy-ARP ID. + if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry)) + return TDI_INVALID_PARAMETER; + + PArpEntry = (ProxyArpEntry *)Buffer; + AddAddr = PArpEntry->pae_addr; + Mask = PArpEntry->pae_mask; + + // See if he's trying to add or delete a proxy arp entry. + if (PArpEntry->pae_status == PAE_STATUS_VALID) { + // We're trying to add an entry. We won't allow an entry + // to be added that we believe to be invalid or conflicting + // with our local addresses. + + if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) || + IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) || + CLASSD_ADDR(AddAddr)) + return TDI_INVALID_PARAMETER; + + // Walk through the list of addresses on the interface, and see + // if they would match the AddAddr. If so, fail the request. + CTEGetLock(&Interface->ai_lock, &Handle); + + if (IsBCastOnIF(Interface, AddAddr & Mask)) { + CTEFreeLock(&Interface->ai_lock, Handle); + return TDI_INVALID_PARAMETER; + } + + Addr = &Interface->ai_ipaddr; + do { + if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) { + if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr)) + break; + } + Addr = Addr->aia_next; + } while (Addr != NULL); + + CTEFreeLock(&Interface->ai_lock, Handle); + if (Addr != NULL) + return TDI_INVALID_PARAMETER; + + // At this point, we believe we're ok. Try to add the address. + if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL)) + return TDI_SUCCESS; + else + return TDI_NO_RESOURCES; + } else { + if (PArpEntry->pae_status == PAE_STATUS_INVALID) { + // He's trying to delete a proxy ARP address. + if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask)) + return TDI_SUCCESS; + } + return TDI_INVALID_PARAMETER; + } + + } + + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + + if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID && + Size >= sizeof(IPNetToMediaEntry)) { + // He does want to set an ARP table entry. See if he's trying to + // create or delete one. + + IPNME = (IPNetToMediaEntry *)Buffer; + if (IPNME->inme_type == INME_TYPE_INVALID) { + uint Index = ARP_HASH(IPNME->inme_addr); + + // We're trying to delete an entry. See if we can find it, + // and then delete it. + CTEGetLock(&Interface->ai_ARPTblLock, &Handle); + Table = Interface->ai_ARPTbl; + PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next); + CurrentATE = (*Table)[Index]; + while (CurrentATE != (ARPTableEntry *)NULL) { + if (CurrentATE->ate_dest == IPNME->inme_addr) { + // Found him. Break out of the loop. + break; + } else { + PrevATE = CurrentATE; + CurrentATE = CurrentATE->ate_next; + } + } + + if (CurrentATE != NULL) { + CTEGetLock(&CurrentATE->ate_lock, &EntryHandle); + RemoveARPTableEntry(PrevATE, CurrentATE); + Interface->ai_count--; + CTEFreeLock(&CurrentATE->ate_lock, EntryHandle); + + if (CurrentATE->ate_packet != NULL) + IPSendComplete(Interface->ai_context, CurrentATE->ate_packet, + NDIS_STATUS_SUCCESS); + + CTEFreeMem(CurrentATE); + Status = TDI_SUCCESS; + } else + Status = TDI_INVALID_PARAMETER; + + CTEFreeLock(&Interface->ai_ARPTblLock, Handle); + return Status; + } + + // We're not trying to delete. See if we're trying to create. + if (IPNME->inme_type != INME_TYPE_DYNAMIC && + IPNME->inme_type != INME_TYPE_STATIC) { + // Not creating, return an error. + return TDI_INVALID_PARAMETER; + } + + // Make sure he's trying to create a valid address. + if (IPNME->inme_physaddrlen != Interface->ai_addrlen) + return TDI_INVALID_PARAMETER; + + // We're trying to create an entry. Call CreateARPTableEntry to create + // one, and fill it in. + CurrentATE = CreateARPTableEntry(Interface, IPNME->inme_addr, &Handle); + if (CurrentATE == NULL) { + return TDI_NO_RESOURCES; + } + + // We've created or found an entry. Fill it in. + Header = (ENetHeader *)CurrentATE->ate_addr; + + switch (Interface->ai_media) { + case NdisMedium802_5: + { + TRHeader *Temp = (TRHeader *)Header; + + // Fill in the TR specific parts, and set the length to the + // size of a TR header. + + Temp->tr_ac = ARP_AC; + Temp->tr_fc = ARP_FC; + CTEMemCopy(&Temp->tr_saddr[ARP_802_ADDR_LENGTH], ARPSNAP, + sizeof(SNAPHeader)); + + Header = (ENetHeader *)&Temp->tr_daddr; + CurrentATE->ate_addrlength = sizeof(TRHeader) + + sizeof(SNAPHeader); + } + break; + case NdisMedium802_3: + CurrentATE->ate_addrlength = sizeof(ENetHeader); + break; + case NdisMediumFddi: + { + FDDIHeader *Temp = (FDDIHeader *)Header; + + Temp->fh_pri = ARP_FDDI_PRI; + CTEMemCopy(&Temp->fh_saddr[ARP_802_ADDR_LENGTH], ARPSNAP, + sizeof(SNAPHeader)); + Header = (ENetHeader *)&Temp->fh_daddr; + CurrentATE->ate_addrlength = sizeof(FDDIHeader) + + sizeof(SNAPHeader); + } + break; + case NdisMediumArcnet878_2: + { + ARCNetHeader *Temp = (ARCNetHeader *)Header; + + Temp->ah_saddr = Interface->ai_addr[0]; + Temp->ah_daddr = IPNME->inme_physaddr[0]; + Temp->ah_prot = ARP_ARCPROT_IP; + CurrentATE->ate_addrlength = sizeof(ARCNetHeader); + } + break; + default: + DEBUGCHK; + break; + } + + + // Copy in the source and destination addresses. + + if (Interface->ai_media != NdisMediumArcnet878_2) { + CTEMemCopy(Header->eh_daddr, IPNME->inme_physaddr, + ARP_802_ADDR_LENGTH); + CTEMemCopy(Header->eh_saddr, Interface->ai_addr, + ARP_802_ADDR_LENGTH); + + // Now fill in the Ethertype. + *(ushort *)&CurrentATE->ate_addr[CurrentATE->ate_addrlength-2] = + net_short(ARP_ETYPE_IP); + } + + // If he's creating a static entry, mark it as always valid. Otherwise + // mark him as valid now. + if (IPNME->inme_type == INME_TYPE_STATIC) + CurrentATE->ate_valid = ALWAYS_VALID; + else + CurrentATE->ate_valid = CTESystemUpTime(); + + CurrentATE->ate_state = ARP_GOOD; + + CTEFreeLock(&CurrentATE->ate_lock, Handle); + return TDI_SUCCESS; + + } + + return TDI_INVALID_PARAMETER; + + +} + + +static uint ARPPackets = ARP_DEFAULT_PACKETS; +static uint ARPBuffers = ARP_DEFAULT_BUFFERS; + +#pragma BEGIN_INIT +//** ARPInit - Initialize the ARP module. +// +// This functions intializes all of the ARP module, including allocating +// the ARP table and any other necessary data structures. +// +// Entry: nothing. +// +// Exit: Returns 0 if we fail to init., !0 if we succeed. +// +int +ARPInit() +{ + NDIS_STATUS Status; // Status for NDIS calls. + +// BUGBUG - Get configuration information dynamically. + +#ifdef NT + RtlInitUnicodeString(&(ARPCharacteristics.Name), ARPName); +#else // NT + ARPCharacteristics.Name.Buffer = ARPName; +#endif // NT + + NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *) + &ARPCharacteristics, sizeof(ARPCharacteristics)); + + if (Status == NDIS_STATUS_SUCCESS) { + return(1); + } + else { + return(0); + } +} +#pragma END_INIT + +#ifndef CHICAGO +#pragma BEGIN_INIT +#else +#pragma code_seg("_LTEXT", "LCODE") +#endif + +//* FreeARPInterface - Free an ARP interface +// +// Called in the event of some sort of initialization failure. We free all +// the memory associated with an ARP interface. +// +// Entry: Interface - Pointer to interface structure to be freed. +// +// Returns: Nothing. +// +void +FreeARPInterface(ARPInterface *Interface) +{ + NDIS_STATUS Status; + ARPBufferTracker *Tracker; + ARPTable *Table; // ARP table. + uint i; // Index variable. + ARPTableEntry *ATE; + CTELockHandle LockHandle; + NDIS_HANDLE Handle; + + CTEStopTimer(&Interface->ai_timer); + +// If we're bound to the adapter, close it now. + CTEInitBlockStruc(&Interface->ai_block); + + CTEGetLock(&Interface->ai_lock, &LockHandle); + if (Interface->ai_handle != (NDIS_HANDLE)NULL) { + Handle = Interface->ai_handle; + Interface->ai_handle = NULL; + CTEFreeLock(&Interface->ai_lock, LockHandle); + + NdisCloseAdapter(&Status, Handle); + + if (Status == NDIS_STATUS_PENDING) + Status = CTEBlock(&Interface->ai_block); + } else { + CTEFreeLock(&Interface->ai_lock, LockHandle); + } + + // First free any outstanding ARP table entries. + Table = Interface->ai_ARPTbl; + if (Table != NULL) { + for (i = 0; i < ARP_TABLE_SIZE;i++) { + while ((*Table)[i] != NULL) { + ATE = (*Table)[i]; + RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]), + ate_next),ATE); + CTEFreeMem(ATE); + } + } + CTEFreeMem(Table); + } + + Interface->ai_ARPTbl = NULL; + + if (Interface->ai_ppool != (NDIS_HANDLE)NULL) + NdisFreePacketPool(Interface->ai_ppool); + + if (Interface->ai_bpool != (NDIS_HANDLE)NULL) + NdisFreeBufferPool(Interface->ai_bpool); + + Tracker = Interface->ai_buflist; + while (Tracker != NULL) { + Interface->ai_buflist = Tracker->abt_next; + NdisFreeBufferPool(Tracker->abt_handle); + CTEFreeMem(Tracker->abt_buffer); + CTEFreeMem(Tracker); + Tracker = Interface->ai_buflist; + } + + if (Interface->ai_bbbase != (uchar *)NULL) + CTEFreeMem(Interface->ai_bbbase); + + // Free the interface itself. + CTEFreeMem(Interface); +} + +//** ARPOpen - Open an adapter for reception. +// +// This routine is called when the upper layer is done initializing and wishes to +// begin receiveing packets. The adapter is actually 'open', we just call InitAdapter +// to set the packet filter and lookahead size. +// +// Input: Context - Interface pointer we gave to IP earlier. +// +// Returns: Nothing +// +void +ARPOpen(void *Context) +{ + ARPInterface *Interface = (ARPInterface *)Context; + InitAdapter(Interface); // Set the packet filter - we'll begin receiving. +} + +//* ARPGetEList - Get the entity list. +// +// Called at init time to get an entity list. We fill our stuff in, and +// then call the interfaces below us to allow them to do the same. +// +// Input: EntityList - Pointer to entity list to be filled in. +// Count - Pointer to number of entries in the list. +// +// Returns Status of attempt to get the info. +// +int +ARPGetEList(void *Context, TDIEntityID *EntityList, uint *Count) +{ + ARPInterface *Interface = (ARPInterface *)Context; + uint ECount; + uint MyATBase; + uint MyIFBase; + uint i; + + ECount = *Count; + + // Walk down the list, looking for existing AT or IF entities, and + // adjust our base instance accordingly. + + MyATBase = 0; + MyIFBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == AT_ENTITY) + MyATBase = MAX(MyATBase, EntityList->tei_instance + 1); + else + if (EntityList->tei_entity == IF_ENTITY) + MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1); + } + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. We need one for the ICMP instance, + // and one for the CL_NL instance. + + if ((ECount + 2) > MAX_TDI_ENTITIES) + return FALSE; + + // At this point we've figure out our base instance. Save for later use. + Interface->ai_atinst = MyATBase; + Interface->ai_ifinst = MyIFBase; + + // Now fill it in. + EntityList->tei_entity = AT_ENTITY; + EntityList->tei_instance = MyATBase; + EntityList++; + EntityList->tei_entity = IF_ENTITY; + EntityList->tei_instance = MyIFBase; + *Count += 2; + + return TRUE; +} + + +extern uint UseEtherSNAP(PNDIS_STRING Name); +extern void GetAlwaysSourceRoute(uint *pArpAlwaysSourceRoute, uint *pIPAlwaysSourceRoute); +extern uint GetArpCacheLife(void); + + +//** ARPRegister - Register a protocol with the ARP module. +// +// We register a protocol for ARP processing. We also open the +// NDIS adapter here. +// +// Note that much of the information passed in here is unused, as +// ARP currently only works with IP. +// +// Entry: +// Adapter - Name of the adapter to bind to. +// IPContext - Value to be passed to IP on upcalls. +// +#ifndef _PNP_POWER +int +ARPRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn, + IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn, + IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound) +#else +int +ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface) +#endif +{ + ARPInterface *ai; // Pointer to interface struct. for this + // interface. + NDIS_STATUS Status, OpenStatus; // Status values. + uint i = 0; // Medium index. + NDIS_MEDIUM MediaArray[MAX_MEDIA]; + uchar sbsize; + uchar *buffer; // Pointer to our buffers. + uint mss; + uint speed; + uint Needed; + uint MacOpts; + uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize; + uint OID; + uint PF; + PNDIS_BUFFER Buffer; + + if ((ai = CTEAllocMem(sizeof(ARPInterface))) == (ARPInterface *)NULL) + return FALSE; // Couldn't allocate memory for this one. + +#ifdef _PNP_POWER + *Interface = ai; +#endif + + CTEMemSet(ai, 0, sizeof(ARPInterface)); + CTEInitTimer(&ai->ai_timer); + +#ifdef NT + ExInitializeSListHead(&ai->ai_sblist); +#endif + + + MediaArray[MEDIA_DIX] = NdisMedium802_3; + MediaArray[MEDIA_TR] = NdisMedium802_5; + MediaArray[MEDIA_FDDI] = NdisMediumFddi; + MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2; + + // Initialize this adapter interface structure. + ai->ai_state = INTERFACE_INIT; + ai->ai_adminstate = IF_STATUS_DOWN; + ai->ai_operstate = IF_STATUS_DOWN; + ai->ai_bcast = IP_LOCAL_BCST; + ai->ai_maxhdrs = ARP_DEFAULT_MAXHDRS; + +#ifndef _PNP_POWER + ai->ai_index = NumIFBound + 1; + ai->ai_context = IPContext; + Info->lip_context = ai; + Info->lip_transmit = ARPTransmit; + Info->lip_transfer = ARPXferData; + Info->lip_close = ARPClose; + Info->lip_addaddr = ARPAddAddr; + Info->lip_deladdr = ARPDeleteAddr; + Info->lip_invalidate = ARPInvalidate; + Info->lip_open = ARPOpen; + Info->lip_qinfo = ARPQueryInfo; + Info->lip_setinfo = ARPSetInfo; + Info->lip_getelist = ARPGetEList; + + Info->lip_index = ai->ai_index; +#endif + + // Initialize the locks. + CTEInitLock(&ai->ai_lock); + CTEInitLock(&ai->ai_ARPTblLock); + + GetAlwaysSourceRoute(&sArpAlwaysSourceRoute, &sIPAlwaysSourceRoute); + + ArpCacheLife = GetArpCacheLife(); + + if (!ArpCacheLife) { + ArpCacheLife = 1; + } + + ArpCacheLife = (ArpCacheLife * 1000L) / ARP_TIMER_TIME; + + // Allocate the buffer and packet pools. + NdisAllocatePacketPool(&Status, &ai->ai_ppool, ARPPackets, sizeof(struct PCCommon)); + if (Status != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + + NdisAllocateBufferPool(&Status, &ai->ai_bpool, ARPBuffers); + if (Status != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + + // Allocate the ARP table + if ((ai->ai_ARPTbl = (ARPTable *)CTEAllocMem(ARP_TABLE_SIZE * sizeof(ARPTableEntry *))) == + (ARPTable *)NULL) { + FreeARPInterface(ai); + return FALSE; + } + + // + // NULL out the pointers + // + CTEMemSet(ai->ai_ARPTbl, 0, ARP_TABLE_SIZE * sizeof(ARPTableEntry *)); + + CTEInitBlockStruc(&ai->ai_block); + + // Open the NDIS adapter. + NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray, + MAX_MEDIA, ARPHandle, ai, Adapter, 0, NULL); + + // Block for open to complete. + if (Status == NDIS_STATUS_PENDING) + Status = (NDIS_STATUS)CTEBlock(&ai->ai_block); + + ai->ai_media = MediaArray[i]; // Fill in media type. + + // Open adapter completed. If it succeeded, we'll finish our intialization. + // If it failed, bail out now. + if (Status != NDIS_STATUS_SUCCESS) { + ai->ai_handle = NULL; + FreeARPInterface(ai); + return FALSE; + } + + // Read the local address. + switch (ai->ai_media) { + case NdisMedium802_3: + addrlen = ARP_802_ADDR_LENGTH; + bcastmask = ENET_BCAST_MASK; + bcastval = ENET_BCAST_VAL; + bcastoff = ENET_BCAST_OFF; + OID = OID_802_3_CURRENT_ADDRESS; + sbsize = ARP_MAX_MEDIA_ENET; + hdrsize = sizeof(ENetHeader); + if (!UseEtherSNAP(Adapter)) { + snapsize = 0; + } else { + snapsize = sizeof(SNAPHeader); + sbsize += sizeof(SNAPHeader); + } + + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | + NDIS_PACKET_TYPE_MULTICAST; + break; + case NdisMedium802_5: + addrlen = ARP_802_ADDR_LENGTH; + bcastmask = TR_BCAST_MASK; + bcastval = TR_BCAST_VAL; + bcastoff = TR_BCAST_OFF; + OID = OID_802_5_CURRENT_ADDRESS; + sbsize = ARP_MAX_MEDIA_TR; + hdrsize = sizeof(TRHeader); + snapsize = sizeof(SNAPHeader); + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; + break; + case NdisMediumFddi: + addrlen = ARP_802_ADDR_LENGTH; + bcastmask = FDDI_BCAST_MASK; + bcastval = FDDI_BCAST_VAL; + bcastoff = FDDI_BCAST_OFF; + OID = OID_FDDI_LONG_CURRENT_ADDR; + sbsize = ARP_MAX_MEDIA_FDDI; + hdrsize = sizeof(FDDIHeader); + snapsize = sizeof(SNAPHeader); + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | + NDIS_PACKET_TYPE_MULTICAST; + break; + case NdisMediumArcnet878_2: + addrlen = 1; + bcastmask = ARC_BCAST_MASK; + bcastval = ARC_BCAST_VAL; + bcastoff = ARC_BCAST_OFF; + OID = OID_ARCNET_CURRENT_ADDRESS; + sbsize = ARP_MAX_MEDIA_ARC; + hdrsize = sizeof(ARCNetHeader); + snapsize = 0; + PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED; + break; + default: + DEBUGCHK; + FreeARPInterface(ai); + return FALSE; + } + + ai->ai_bcastmask = bcastmask; + ai->ai_bcastval = bcastval; + ai->ai_bcastoff = bcastoff; + ai->ai_addrlen = addrlen; + ai->ai_hdrsize = hdrsize; + ai->ai_snapsize = snapsize; + ai->ai_pfilter = PF; + + Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID, + ai->ai_addr, addrlen, NULL); + + if (Status != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + +#ifndef _PNP_POWER + Info->lip_addrlen = addrlen; + Info->lip_addr = ai->ai_addr; +#endif + + // Read the maximum frame size. + if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL)) != NDIS_STATUS_SUCCESS) { + FreeARPInterface(ai); + return FALSE; + } + + // If this is token ring, figure out the RC len stuff now. + mss -= (uint)ai->ai_snapsize; + + if (ai->ai_media == NdisMedium802_5) { + mss -= (sizeof(RC) + (ARP_MAX_RD * sizeof(ushort))); + } else { + if (ai->ai_media == NdisMediumFddi) { + mss = MIN(mss, ARP_FDDI_MSS); + } + } + + ai->ai_mtu = (ushort)mss; + +#ifndef _PNP_POWER + Info->lip_mss = mss; +#endif + + // Read the speed for local purposes. + if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL)) == NDIS_STATUS_SUCCESS) { + ai->ai_speed = speed * 100L; +#ifndef _PNP_POWER + Info->lip_speed = ai->ai_speed; +#endif + } + + // Read and save the options. + Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS, + &MacOpts, sizeof(MacOpts), NULL); + + if (Status != NDIS_STATUS_SUCCESS) +#ifndef _PNP_POWER + Info->lip_flags = 0; +#else + *Flags = 0; +#endif + else +#ifndef _PNP_POWER + Info->lip_flags = +#else + *Flags = +#endif + (MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0; + + // Read and store the vendor description string. + Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_VENDOR_DESCRIPTION, &ai->ai_desc, 0, &Needed); + + if ((Status == NDIS_STATUS_INVALID_LENGTH) || + (Status == NDIS_STATUS_BUFFER_TOO_SHORT)) { + // We know the size we need. Allocate a buffer. + buffer = CTEAllocMem(Needed); + if (buffer != NULL) { + Status = DoNDISRequest(ai, NdisRequestQueryInformation, + OID_GEN_VENDOR_DESCRIPTION, buffer, Needed, NULL); + if (Status == NDIS_STATUS_SUCCESS) { + ai->ai_desc = buffer; + ai->ai_desclen = Needed; + } + } + } + + // Allocate our small and big buffer pools. + + if ((sbsize & 0x3)) { + // + // Must 32 bit align the buffers so pointers to them will be aligned. + // + sbsize = ((sbsize >> 2) + 1) << 2; + } + + ai->ai_sbsize = sbsize; + + // Pre-prime the ARP header buffer list. + Buffer = GrowARPHeaders(ai); + if (Buffer != NULL) { + FreeARPBuffer(ai, Buffer); + } + + + if ((buffer = CTEAllocMem((sbsize+sizeof(ARPHeader)) * ARPPackets)) == (uchar *)NULL) { + FreeARPInterface(ai); + return FALSE; + } + + // Link big buffers into the list. + ai->ai_bbbase = buffer; + ai->ai_bblist = (uchar *)NULL; + for (i = 0; i < ARPPackets; i++) { + *(char **)&*buffer = ai->ai_bblist; + ai->ai_bblist = buffer; + buffer += sbsize+sizeof(ARPHeader); + } + + // Everything's set up, so get the ARP timer running. + CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai); + + return TRUE; + +} + +#ifndef CHICAGO +#pragma END_INIT +#endif + +#ifdef _PNP_POWER + +//* ARPDynRegister - Dynamically register IP. +// +// Called by IP when he's about done binding to register with us. Since we +// call him directly, we don't save his info here. We do keep his context +// and index number. +// +// Input: See ARPRegister +// +// Returns: Nothing. +// +int +ARPDynRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn, + IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn, + IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound) +{ + ARPInterface *Interface = (ARPInterface *)Info->lip_context; + + Interface->ai_context = IPContext; + Interface->ai_index = NumIFBound; + + return TRUE; +} + +//* ARPBindAdapter - Bind and initialize an adapter. +// +// Called in a PNP environment to initialize and bind an adapter. We open +// the adapter and get it running, and then we call up to IP to tell him +// about it. IP will initialize, and if all goes well call us back to start +// receiving. +// +// Input: RetStatus - Where to return the status of this call. +// BindContext - Handle to use for calling BindAdapterComplete. +// AdapterName - Pointer to name of adapter. +// SS1 - System specific 1 parameter. +// SS2 - System specific 2 parameter. +// +// Returns: Nothing. +// +void NDIS_API +ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext, + PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2) +{ + uint Flags; // MAC binding flags. + ARPInterface *Interface; // Newly created interface. + PNDIS_STRING ConfigName; // Name used by IP for config. info. + IP_STATUS Status; // State of IPAddInterface call. + LLIPBindInfo BindInfo; // Binding informatio for IP. + NDIS_HANDLE Handle ; + + CTERefillMem(); + + if (!OpenIFConfig(SS1, &Handle)) { + *RetStatus = NDIS_STATUS_FAILURE; + return; + } + + // If IsLLInterfaceValueNull is FALSE then this means that some other ARP module is + // used for this device so we skip it. + // + if (IsLLInterfaceValueNull(Handle) == FALSE) { + *RetStatus = NDIS_STATUS_FAILURE; + CloseIFConfig(Handle); + return ; + } + + CloseIFConfig(Handle); + + + // First, open the adapter and get the info. + if (!ARPRegister(AdapterName, &Flags, &Interface)) { + *RetStatus = NDIS_STATUS_FAILURE; + return; + } + + CTERefillMem(); + + // OK, we're opened the adapter. Call IP to tell him about it. + BindInfo.lip_context = Interface; + BindInfo.lip_transmit = ARPTransmit; + BindInfo.lip_transfer = ARPXferData; + BindInfo.lip_close = ARPClose; + BindInfo.lip_addaddr = ARPAddAddr; + BindInfo.lip_deladdr = ARPDeleteAddr; + BindInfo.lip_invalidate = ARPInvalidate; + BindInfo.lip_open = ARPOpen; + BindInfo.lip_qinfo = ARPQueryInfo; + BindInfo.lip_setinfo = ARPSetInfo; + BindInfo.lip_getelist = ARPGetEList; + BindInfo.lip_mss = Interface->ai_mtu; + BindInfo.lip_speed = Interface->ai_speed; + BindInfo.lip_flags = Flags; + BindInfo.lip_addrlen = Interface->ai_addrlen; + BindInfo.lip_addr = Interface->ai_addr; + + Status = IPAddInterface((PNDIS_STRING)SS1, SS2, Interface, ARPDynRegister, + &BindInfo); + + if (Status != IP_SUCCESS) { + // Need to close the binding. FreeARPInterface will do that, as well + // as freeing resources. + + FreeARPInterface(Interface); + *RetStatus = NDIS_STATUS_FAILURE; + } else + *RetStatus = NDIS_STATUS_SUCCESS; + +} + +//* ARPUnbindAdapter - Unbind from an adapter. +// +// Called when we need to unbind from an adapter. We'll call up to IP to tell +// him. When he's done, we'll free our memory and return. +// +// Input: RetStatus - Where to return status from call. +// ProtBindContext - The context we gave NDIS earlier - really a +// pointer to an ARPInterface structure. +// UnbindContext - Context for completeing this request. +// +// Returns: Nothing. +// +void NDIS_API +ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext, + NDIS_HANDLE UnbindContext) +{ + ARPInterface *Interface = (ARPInterface *)ProtBindContext; + NDIS_STATUS Status; // Status of close call. + CTELockHandle LockHandle; + NDIS_HANDLE Handle; + + // Shut him up, so we don't get any more frames. + Interface->ai_pfilter = 0; + DoNDISRequest(Interface, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint), + NULL); + + // Mark him as down. + Interface->ai_state = INTERFACE_DOWN; + Interface->ai_adminstate = IF_STATUS_DOWN; + + // Now tell IP he's gone. We need to make sure that we don't tell him twice. + // To do this we set the context to NULL after we tell him the first time, + // and we check to make sure it's non-NULL before notifying him. + + if (Interface->ai_context != NULL) { + IPDelInterface(Interface->ai_context); + Interface->ai_context = NULL; + } + + // Finally, close him. We do this here so we can return a valid status. + + CTEGetLock(&Interface->ai_lock, &LockHandle); + + if (Interface->ai_handle != NULL) { + Handle = Interface->ai_handle; + Interface->ai_handle = NULL; + CTEFreeLock(&Interface->ai_lock, LockHandle); + + CTEInitBlockStruc(&Interface->ai_block); + NdisCloseAdapter(&Status, Handle); + + // Block for close to complete. + if (Status == NDIS_STATUS_PENDING) + Status = (NDIS_STATUS)CTEBlock(&Interface->ai_block); + } else { + CTEFreeLock(&Interface->ai_lock, LockHandle); + Status = NDIS_STATUS_SUCCESS; + } + + *RetStatus = Status; + + if (Status == NDIS_STATUS_SUCCESS) { + + FreeARPInterface(Interface); + } +} + +extern ulong VIPTerminate; + +//* ARPUnloadProtocol - Unload. +// +// Called when we need to unload. All we do is call up to IP, and return. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void NDIS_API +ARPUnloadProtocol(void) +{ + NDIS_STATUS Status; + +#ifdef CHICAGO + + IPULUnloadNotify(); + + if (VIPTerminate) { + NdisDeregisterProtocol(&Status, ARPHandle); + CTEUnload(NULL); + } + +#endif + +} + +#endif + diff --git a/private/ntos/tdi/tcpip/ip/arp.h b/private/ntos/tdi/tcpip/ip/arp.h new file mode 100644 index 000000000..d294b2dce --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arp.h @@ -0,0 +1,21 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** ARP.H - Exports from ARP. +// +// This file contains the public definitons from ARP. +extern int ARPInit(void); +#ifndef _PNP_POWER +extern int ARPRegister(PNDIS_STRING, void *, IPRcvRtn, IPTxCmpltRtn, + IPStatusRtn, IPTDCmpltRtn, IPRcvCmpltRtn, struct LLIPBindInfo *, + uint); +#else +int +ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface); +#endif + + + diff --git a/private/ntos/tdi/tcpip/ip/arpdef.h b/private/ntos/tdi/tcpip/ip/arpdef.h new file mode 100644 index 000000000..6278fdb60 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/arpdef.h @@ -0,0 +1,340 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** arpdef.h - ARP definitions +// +// This file containes all of the private ARP related definitions. + + +#define MEDIA_DIX 0 +#define MEDIA_TR 1 +#define MEDIA_FDDI 2 +#define MEDIA_ARCNET 3 + +#define MAX_MEDIA 4 + +#define INTERFACE_UP 0 // Interface is up. +#define INTERFACE_INIT 1 // Interface is initializing. +#define INTERFACE_DOWN 2 // Interface is down. + +#define LOOKAHEAD_SIZE 128 // A reasonable lookahead size + +// Definitions for state of an ATE. The 'RESOLVING' indicators must occur first. +#define ARP_RESOLVING_LOCAL 0 // Address is being resolved (on local ring, if TR) +#define ARP_RESOLVING_GLOBAL 1 // Address is being resolved globally. +#define ARP_RESOLVING ARP_RESOLVING_GLOBAL +#define ARP_GOOD 2 // ATE is good. +#define ARP_BAD 3 // ATE is bad. +#define ARP_FLOOD_RATE 1000L // No more than once a second. +#define ARP_802_ADDR_LENGTH 6 // Length of an 802 address. + +#define MIN_ETYPE 0x600 // Minimum valid Ethertype +#define SNAP_SAP 170 +#define SNAP_UI 3 + + +//* Structure of an Ethernet header. +struct ENetHeader { + uchar eh_daddr[ARP_802_ADDR_LENGTH]; + uchar eh_saddr[ARP_802_ADDR_LENGTH]; + ushort eh_type; +}; /* ENetHeader */ + +typedef struct ENetHeader ENetHeader; + +//* Structure of a token ring header. +struct TRHeader { + uchar tr_ac; + uchar tr_fc; + uchar tr_daddr[ARP_802_ADDR_LENGTH]; + uchar tr_saddr[ARP_802_ADDR_LENGTH]; +}; /* TRHeader */ +#define ARP_AC 0x10 +#define ARP_FC 0x40 +#define TR_RII 0x80 + +typedef struct TRHeader TRHeader; +struct RC { + uchar rc_blen; // Broadcast indicator and length. + uchar rc_dlf; // Direction and largest frame. +}; /* RC */ +#define RC_DIR 0x80 +#define RC_LENMASK 0x1f +#define RC_SRBCST 0xc2 // Single route broadcast RC. +#define RC_ARBCST 0x82 // All route broadcast RC. +#define RC_LMASK 0x1F // Mask for length field for route + // information +#define RC_LEN 0x2 // Length to put in the length bits + // when sending source routed + // frames +#define RC_BCST_LEN 0x70 // Length for a broadcast. +#define RC_LF_MASK 0x70 // Mask for length bits. + +typedef struct RC RC; + +//* Structure of source routing information. +struct SRInfo { + RC sri_rc; // Routing control info. + ushort sri_rd[1]; // Routing designators. +}; /* SRInfo */ + +#define ARP_MAX_RD 8 + +typedef struct SRInfo SRInfo; + +//* Structure of an FDDI header. +struct FDDIHeader { + uchar fh_pri; + uchar fh_daddr[ARP_802_ADDR_LENGTH]; + uchar fh_saddr[ARP_802_ADDR_LENGTH]; +}; /* FDDIHeader */ + +typedef struct FDDIHeader FDDIHeader; + +#define ARP_FDDI_PRI 0x57 +#define ARP_FDDI_MSS 4352 + +//* Structure of an ARCNET header. +struct ARCNetHeader { + uchar ah_saddr; + uchar ah_daddr; + uchar ah_prot; +}; /* ARCNetHeader */ + +typedef struct ARCNetHeader ARCNetHeader; + +//* Structure of a SNAP header. +struct SNAPHeader { + uchar sh_dsap; + uchar sh_ssap; + uchar sh_ctl; + uchar sh_protid[3]; + ushort sh_etype; +}; /* SNAPHeader */ + +typedef struct SNAPHeader SNAPHeader; + +#define ARP_MAX_MEDIA_ENET sizeof(ENetHeader) +#define ARP_MAX_MEDIA_TR (sizeof(TRHeader)+sizeof(RC)+(ARP_MAX_RD*sizeof(ushort))+sizeof(SNAPHeader)) +#define ARP_MAX_MEDIA_FDDI (sizeof(FDDIHeader)+sizeof(SNAPHeader)) +#define ARP_MAX_MEDIA_ARC sizeof(ARCNetHeader) + +#define ENET_BCAST_MASK 0x01 +#define TR_BCAST_MASK 0x80 +#define FDDI_BCAST_MASK 0x01 +#define ARC_BCAST_MASK 0xff + +#define ENET_BCAST_VAL 0x01 +#define TR_BCAST_VAL 0x80 +#define FDDI_BCAST_VAL 0x01 +#define ARC_BCAST_VAL 0x00 + +#define ENET_BCAST_OFF 0x00 +#define TR_BCAST_OFF offsetof(struct TRHeader, tr_daddr) +#define FDDI_BCAST_OFF offsetof(struct FDDIHeader, fh_daddr) +#define ARC_BCAST_OFF offsetof(struct ARCNetHeader, ah_daddr) + +//* Structure of an ARP table entry. +typedef struct ARPTableEntry { + struct ARPTableEntry *ate_next; // Next ATE in hash chain + ulong ate_valid; // Last time ATE was known to be valid. + IPAddr ate_dest; // IP address represented. + PNDIS_PACKET ate_packet; // Packet (if any) queued for resolution + RouteCacheEntry *ate_rce; // List of RCEs that reference this ATE. + DEFINE_LOCK_STRUCTURE(ate_lock) // Lock for this ATE. + uint ate_useticks; // Number of ticks left until this + // goes away. + uchar ate_addrlength; // Length of the address. + uchar ate_state; // State of this entry + uchar ate_addr[1]; // Address that maps to dest +} ARPTableEntry; + +#define ALWAYS_VALID 0xffffffff + +//* Structure of the ARP table. +#define ARP_TABLE_SIZE 32 +#define ARP_HASH(x) ((((uchar *)&(x))[3]) % ARP_TABLE_SIZE) +typedef ARPTableEntry *ARPTable[]; + +//* List structure for local representation of an IPAddress. +typedef struct ARPIPAddr { + struct ARPIPAddr *aia_next; // Next in list. + uint aia_age; + IPAddr aia_addr; // The address. + IPMask aia_mask; + void *aia_context; +} ARPIPAddr; + +#define ARPADDR_NOT_LOCAL 4 +#define ARPADDR_NEW_LOCAL 3 +#define ARPADDR_OLD_LOCAL 0 + +//* List structure for Proxy-ARP addresses. +typedef struct ARPPArpAddr { + struct ARPPArpAddr *apa_next; // Next in list. + IPAddr apa_addr; // The address. + IPMask apa_mask; // And the mask. +} ARPPArpAddr; + +//* List structure for a multicast IP address. +typedef struct ARPMCastAddr { + struct ARPMCastAddr *ama_next; // Next in list. + IPAddr ama_addr; // The (masked) address. + uint ama_refcnt; // Reference count for this address. +} ARPMCastAddr; + +#define ARP_MCAST_MASK 0xffff7f00 + +#define ARP_TIMER_TIME 1000L +#define ARP_RESOLVE_TIMEOUT 1000L +#define ARP_MIN_VALID_TIMEOUT 600000L + +#ifdef VXD +#define ARP_DEFAULT_MAXHDRS 100 +#else +#define ARP_DEFAULT_MAXHDRS 0xffffffff +#endif + +typedef struct ARPBufferTracker { + struct ARPBufferTracker *abt_next; + NDIS_HANDLE abt_handle; + uchar *abt_buffer; +} ARPBufferTracker; + +//* Structure of information we keep on a per-interface basis. +typedef struct ARPInterface { + void *ai_context; // Upper layer context info. + NDIS_HANDLE ai_handle; // NDIS bind handle. + NDIS_MEDIUM ai_media; // Media type. + NDIS_HANDLE ai_bpool; // Handle for buffer pool. + NDIS_HANDLE ai_ppool; // Handle for packet pool. + DEFINE_LOCK_STRUCTURE(ai_lock) // Lock for this structure. + DEFINE_LOCK_STRUCTURE(ai_ARPTblLock) // ARP Table lock for this structure. +#ifdef NT + SLIST_HEADER ai_sblist; // Free list of header buffers. +#else + PNDIS_BUFFER ai_sblist; // Free list of header buffers. +#endif + uchar *ai_bblist; // Free list of 'big' buffers. + ARPTable *ai_ARPTbl; // Pointer to the ARP table for this interface + ARPIPAddr ai_ipaddr; // Local IP address list. + ARPPArpAddr *ai_parpaddr; // Proxy ARP address list. + IPAddr ai_bcast; // Broadcast mask for this interface. + // SNMP required counters + uint ai_inoctets; // Input octets. + uint ai_inpcount[2]; // Count of nonunicast and unicast + // packets received. + uint ai_outoctets; // Output octets + uint ai_outpcount[2];// Count of nonunicast and unicast + // packets sent. + uint ai_qlen; // Output q length. + uchar ai_addr[ARP_802_ADDR_LENGTH]; // Local HW address. + uchar ai_sbsize; // Size of a small buffer + uchar ai_state; // State of the interface. Union of + // admin and operational states. + uchar ai_addrlen; // Length of ai_addr. + uchar ai_bcastmask; // Mask for checking unicast. + uchar ai_bcastval; // Value to check against. + uchar ai_bcastoff; // Offset in frame to check against. + uchar ai_hdrsize; // Size of 'typical' header. + uchar ai_snapsize; // Size of snap header, if any. + uchar ai_pad[2]; // PAD PAD + uint ai_pfilter; // Packet filter for this i/f. + uint ai_count; // Number of entries in the ARPTable. + uint ai_parpcount; // Number of proxy ARP entries. + CTETimer ai_timer; // ARP timer for this interface. + CTEBlockStruc ai_block; // Structure for blocking on. + ushort ai_mtu; // MTU for this interface. + uchar ai_adminstate; // Admin state. + uchar ai_operstate; // Operational state; + uint ai_speed; // Speed. + uint ai_lastchange; // Last change time. + uint ai_indiscards; // In discards. + uint ai_inerrors; // Input errors. + uint ai_uknprotos; // Unknown protocols received. + uint ai_outdiscards; // Output packets discarded. + uint ai_outerrors; // Output errors. + uint ai_desclen; // Length of desc. string. + uint ai_index; // Global I/F index ID. + uint ai_atinst; // AT instance number. + uint ai_ifinst; // IF instance number. + char *ai_desc; // Descriptor string. + ARPMCastAddr *ai_mcast; // Multicast list. + uint ai_mcastcnt; // Count of elements on mcast list. + void *ai_bbbase; // Base of big buffers. + uint ai_curhdrs; // Current number of headers. + uint ai_maxhdrs; // Maximum allowed number of headers. + ARPBufferTracker *ai_buflist; // List of header buffer handles. +} ARPInterface; + +typedef struct ARPNotifyStruct { + CTEEvent ans_event; + uint ans_shutoff; + IPAddr ans_addr; + uint ans_hwaddrlen; + uchar ans_hwaddr[1]; +} ARPNotifyStruct; + +//* NOTE: These two values MUST stay at 0 and 1. +#define AI_UCAST_INDEX 0 +#define AI_NONUCAST_INDEX 1 + +#define ARP_DEFAULT_PACKETS 10 // Default to this many packets. +#define ARP_DEFAULT_BUFFERS 50 // And this many buffers. + +#define ARP_HDRBUF_GROW_SIZE 32 // This many header buffers. + +//* Structure of information passed as context in RCE. +typedef struct ARPContext { + RouteCacheEntry *ac_next; // Next RCE in ARP table chain. + ARPTableEntry *ac_ate; // Back pointer to ARP table entry. +} ARPContext; + +typedef struct IPNMEContext { + uint inc_index; + ARPTableEntry *inc_entry; +} IPNMEContext; + +#ifdef NT +// +// This structure must be packed under NT. +// +#include +#endif // NT + +// Structure of an ARP header. +struct ARPHeader { + ushort ah_hw; // Hardware address space. + ushort ah_pro; // Protocol address space. + uchar ah_hlen; // Hardware address length. + uchar ah_plen; // Protocol address length. + ushort ah_opcode; // Opcode. + uchar ah_shaddr[ARP_802_ADDR_LENGTH]; // Source HW address. + IPAddr ah_spaddr; // Source protocol address. + uchar ah_dhaddr[ARP_802_ADDR_LENGTH]; // Destination HW address. + IPAddr ah_dpaddr; // Destination protocol address. +}; /* ARPHeader */ + +#ifdef NT +#include +#endif // NT + +typedef struct ARPHeader ARPHeader; + +#define ARP_ETYPE_IP 0x800 +#define ARP_ETYPE_ARP 0x806 +#define ARP_REQUEST 1 +#define ARP_RESPONSE 2 +#define ARP_HW_ENET 1 +#define ARP_HW_802 6 +#define ARP_HW_ARCNET 7 + +#define ARP_ARCPROT_ARP 0xd5 +#define ARP_ARCPROT_IP 0xd4 + +// The size we need to back off the buffer length because ARCNet address +// are one bytes instead of six. +#define ARCNET_ARPHEADER_ADJUSTMENT 10 diff --git a/private/ntos/tdi/tcpip/ip/dirs b/private/ntos/tdi/tcpip/ip/dirs new file mode 100644 index 000000000..0dab2f056 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/dirs @@ -0,0 +1,22 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS=up mp diff --git a/private/ntos/tdi/tcpip/ip/icmp.c b/private/ntos/tdi/tcpip/ip/icmp.c new file mode 100644 index 000000000..5faafa486 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/icmp.c @@ -0,0 +1,1698 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** icmp.c - IP ICMP routines. +// +// This module contains all of the ICMP related routines. +// + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "icmp.h" +#include "info.h" +#include "iproute.h" +#include "ipinit.h" +#include "ipxmit.h" +#include + +extern ProtInfo IPProtInfo[]; // Protocol information table. + +extern void *IPRegisterProtocol(uchar, void *, void *, void *, void *); +extern ULStatusProc FindULStatus(uchar); +extern uchar IPUpdateRcvdOptions(IPOptInfo *, IPOptInfo *, IPAddr, IPAddr); +extern void IPInitOptions(IPOptInfo *); +extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *); +extern IP_STATUS IPFreeOptions(IPOptInfo *); +extern uchar IPGetLocalAddr(IPAddr, IPAddr *); +void ICMPRouterTimer(NetTableEntry *); + +extern NDIS_HANDLE BufferPool; + +extern NetTableEntry *NetTableList; // Pointer to the net table list. +extern ProtInfo *RawPI; // Raw IP protinfo + +DEFINE_LOCK_STRUCTURE(ICMPHeaderLock) +ICMPHeader *ICMPHeaderList; +uint CurrentICMPHeaders; +uint MaxICMPHeaders; + +ICMPStats ICMPInStats; +ICMPStats ICMPOutStats; + + +#ifdef NT +#ifdef ALLOC_PRAGMA + +void ICMPInit(uint NumBuffers); + +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ); + +#pragma alloc_text(INIT, ICMPInit) +#pragma alloc_text(PAGE, ICMPEchoRequest) + +#endif // ALLOC_PRAGMA +#endif // NT + + +//* UpdateICMPStats - Update ICMP statistics. +// +// A routine to update the ICMP statistics. +// +// Input: Stats - Pointer to stat. structure to update (input or output). +// Type - Type of stat to update. +// +// Returns: Nothing. +// +void +UpdateICMPStats(ICMPStats *Stats, uchar Type) +{ + switch (Type) { + case ICMP_DEST_UNREACH: + Stats->icmps_destunreachs++; + break; + case ICMP_TIME_EXCEED: + Stats->icmps_timeexcds++; + break; + case ICMP_PARAM_PROBLEM: + Stats->icmps_parmprobs++; + break; + case ICMP_SOURCE_QUENCH: + Stats->icmps_srcquenchs++; + break; + case ICMP_REDIRECT: + Stats->icmps_redirects++; + break; + case ICMP_TIMESTAMP: + Stats->icmps_timestamps++; + break; + case ICMP_TIMESTAMP_RESP: + Stats->icmps_timestampreps++; + break; + case ICMP_ECHO: + Stats->icmps_echos++; + break; + case ICMP_ECHO_RESP: + Stats->icmps_echoreps++; + break; + case ADDR_MASK_REQUEST: + Stats->icmps_addrmasks++; + break; + case ADDR_MASK_REPLY: + Stats->icmps_addrmaskreps++; + break; + default: + break; + } + +} + +//** GetICMPBuffer - Get an ICMP buffer, and allocate an NDIS_BUFFER that maps it. +// +// A routine to allocate an ICMP buffer and map an NDIS_BUFFER to it. +// +// Entry: Size - Size in bytes header buffer should be mapped as. +// Buffer - Pointer to pointer to NDIS_BUFFER to return. +// +// Returns: Pointer to ICMP buffer if allocated, or NULL. +// +ICMPHeader * +GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer) +{ + CTELockHandle Handle; + ICMPHeader **Header; + NDIS_STATUS Status; + + + CTEGetLock(&ICMPHeaderLock, &Handle); + + Header = (ICMPHeader **)ICMPHeaderList; + + if (Header == NULL) { + // Couldn't get a header from our free list. Try to allocate one. + Header = CTEAllocMem(sizeof(ICMPHeader) + sizeof(IPHeader) + + sizeof(IPHeader) + MAX_OPT_SIZE + 8); + if (Header == NULL) { + CTEFreeLock(&ICMPHeaderLock, Handle); + return (ICMPHeader *) NULL; + } + CurrentICMPHeaders++; + } + else { + ICMPHeaderList = *Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + + NdisAllocateBuffer(&Status, Buffer, BufferPool, Header, Size + + sizeof(IPHeader)); + + if (Status == NDIS_STATUS_SUCCESS) { + NdisBufferLength(*Buffer) = Size; + Header = (ICMPHeader **)((uchar *)Header + sizeof(IPHeader)); + + (*(ICMPHeader **)&Header)->ich_xsum = 0; + return (ICMPHeader *)Header; + } + + // Couldn't get an NDIS_BUFFER, free the ICMP buffer. + CTEGetLock(&ICMPHeaderLock, &Handle); + + if (CurrentICMPHeaders > MaxICMPHeaders) { + CurrentICMPHeaders--; + CTEFreeMem(Header); + } else { + *Header = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + + return (ICMPHeader *)NULL; +} + +//** FreeICMPBuffer - Free an ICMP buffer. +// +// This routine puts an ICMP buffer back on our free list. +// +// Entry: Buffer - Pointer to NDIS_BUFFER to be freed. +// +// Returns: Nothing. +// +void +FreeICMPBuffer(PNDIS_BUFFER Buffer) +{ + CTELockHandle Handle; + ICMPHeader **Header; + uint Length; + + NdisQueryBuffer(Buffer, (PVOID *)&Header, &Length); + CTEGetLock(&ICMPHeaderLock, &Handle); + if (CurrentICMPHeaders > MaxICMPHeaders) { + CurrentICMPHeaders--; + CTEFreeMem(Header); + } else { + *Header = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)Header; + } + + CTEFreeLock(&ICMPHeaderLock, Handle); + NdisFreeBuffer(Buffer); +} + +//** DeleteEC - Remove an EchoControl from an NTE, and return a pointer to it. +// +// This routine is called when we need to remove an echo control structure from +// an NTE. We walk the list of EC structures on the NTE, and if we find a match +// we remove it and return a pointer to it. +// +// Entry: NTE - Pointer to NTE to be searched. +// Seq - Seq. # identifting the EC. +// +// Returns: Pointer to the EC if it finds it. +// +EchoControl * +DeleteEC(NetTableEntry *NTE, ushort Seq) +{ + EchoControl *Prev, *Current; + CTELockHandle Handle; + + CTEGetLock(&NTE->nte_lock, &Handle); + Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next); + Current = NTE->nte_echolist; + while(Current != (EchoControl *)NULL) + if (Current->ec_seq == Seq) { + Prev->ec_next = Current->ec_next; + break; + } + else { + Prev = Current; + Current = Current->ec_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + return Current; + +} + +//** ICMPSendComplete< - Complete an ICMP send. +// +// This routine is called when an ICMP send completes. We free the header buffer, +// the data buffer if there is one, and the NDIS_BUFFER chain. +// +// Entry: DataPtr - Pointer to data buffer, if any. +// BufferChain - Pointer to NDIS_BUFFER chain. +// +// Returns: Nothing +// +void +ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain) +{ + PNDIS_BUFFER DataBuffer; + + NdisGetNextBuffer(BufferChain, &DataBuffer); + FreeICMPBuffer(BufferChain); + + if (DataBuffer != (PNDIS_BUFFER)NULL) { // We had data with this ICMP send. +#ifdef DEBUG + if (DataPtr == (void *)NULL) + DEBUGCHK; +#endif + CTEFreeMem(DataPtr); + NdisFreeBuffer(DataBuffer); + } + +} + +//* XsumBufChain - Checksum a chain of buffers. +// +// Called when we need to checksum an IPRcvBuf chain. +// +// Input: BufChain - Buffer chain to be checksummed. +// +// Returns: The checksum. +// +ushort +XsumBufChain(IPRcvBuf *BufChain) +{ + ulong CheckSum = 0; + + if (BufChain == NULL) + DEBUGCHK; + + do { + CheckSum += (ulong)xsum(BufChain->ipr_buffer, BufChain->ipr_size); + BufChain = BufChain->ipr_next; + } while (BufChain != NULL); + + // Fold the checksum down. + CheckSum = (CheckSum >> 16) + (CheckSum & 0xffff); + CheckSum += (CheckSum >> 16); + + return (ushort)CheckSum; +} + + +//** SendEcho - Send an ICMP Echo or Echo response. +// +// This routine sends an ICMP echo or echo response. The Echo/EchoResponse may +// carry data. If it does we'll copy the data here. The request may also have +// options. Options are not copied, as the IPTransmit routine will copy options. +// +// Entry: Dest - Destination to send to. +// Source - Source to send from. +// Type - Type of request (ECHO or ECHO_RESP) +// ID - ID of request. +// Seq - Seq. # of request. +// Data - Pointer to data (NULL if none). +// DataLength - Length in bytes of data +// OptInfo - Pointer to IP Options structure. +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendEcho(IPAddr Dest, IPAddr Source, uchar Type, ushort ID, ushort Seq, + IPRcvBuf *Data, uint DataLength, IPOptInfo *OptInfo) +{ + uchar *DataBuffer = (uchar *)NULL; // Pointer to data buffer. + PNDIS_BUFFER HeaderBuffer, Buffer; // Buffers for our header and user data. + NDIS_STATUS Status; + ICMPHeader *Header; + ushort header_xsum; + IP_STATUS IStatus; // Status of transmit + + ICMPOutStats.icmps_msgs++; + + Header = GetICMPBuffer(sizeof(ICMPHeader), &HeaderBuffer); + if (Header == (ICMPHeader *)NULL) { + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + +#ifdef DEBUG + if (Type != ICMP_ECHO_RESP && Type != ICMP_ECHO) + DEBUGCHK; +#endif + + Header->ich_type = Type; + Header->ich_code = 0; + *(ushort *)&Header->ich_param = ID; + *((ushort *)&Header->ich_param + 1) = Seq; + header_xsum = xsum(Header, sizeof(ICMPHeader)); + Header->ich_xsum = ~header_xsum; + + // If there's data, get a buffer and copy it now. If we can't do this fail the request. + if (DataLength != 0) { + ulong TempXsum; + uint BytesToCopy, CopyIndex; + + DataBuffer = CTEAllocMem(DataLength); + if (DataBuffer == (void *)NULL) { // Couldn't get a buffer + FreeICMPBuffer(HeaderBuffer); + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + BytesToCopy = DataLength; + CopyIndex = 0; + do { + uint CopyLength; +#ifdef DEBUG + if (Data == NULL) { + DEBUGCHK; + break; + } +#endif + + CopyLength = MIN(BytesToCopy, Data->ipr_size); + + CTEMemCopy(DataBuffer + CopyIndex, Data->ipr_buffer, CopyLength); + Data = Data->ipr_next; + CopyIndex += CopyLength; + BytesToCopy -= CopyLength; + } while (BytesToCopy); + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, DataBuffer, DataLength); + if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get an NDIS_BUFFER + CTEFreeMem(DataBuffer); + FreeICMPBuffer(HeaderBuffer); + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + // Compute rest of xsum. + TempXsum = (ulong)header_xsum + (ulong)xsum(DataBuffer, DataLength); + TempXsum = (TempXsum >> 16) + (TempXsum & 0xffff); + TempXsum += (TempXsum >> 16); + Header->ich_xsum = ~(ushort)TempXsum; + NDIS_BUFFER_LINKAGE(HeaderBuffer) = Buffer; + } + + + UpdateICMPStats(&ICMPOutStats, Type); + + IStatus = IPTransmit(IPProtInfo, DataBuffer, HeaderBuffer, + DataLength + sizeof(ICMPHeader), Dest, Source, OptInfo, NULL, + PROT_ICMP); + + if (IStatus != IP_PENDING) + ICMPSendComplete(DataBuffer, HeaderBuffer); + + return IStatus; +} + +//** SendICMPMsg - Send an ICMP message +// +// This is the general ICMP message sending routine, called for most ICMP sends besides +// echo. Basically, all we do is get a buffer, format the info, copy the input +// header, and send the message. +// +// Entry: Src - IPAddr of source. +// Dest - IPAddr of destination +// Type - Type of request. +// Code - Subcode of request. +// Pointer - Pointer value for request. +// Data - Pointer to data (NULL if none). +// DataLength - Length in bytes of data +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendICMPMsg(IPAddr Src, IPAddr Dest, uchar Type, uchar Code, ulong Pointer, + uchar *Data, uchar DataLength) +{ + PNDIS_BUFFER HeaderBuffer; // Buffer for our header + ICMPHeader *Header; + IP_STATUS IStatus; // Status of transmit + IPOptInfo OptInfo; // Options for this transmit. + + + ICMPOutStats.icmps_msgs++; + + Header = GetICMPBuffer(sizeof(ICMPHeader) + DataLength, &HeaderBuffer); + if (Header == (ICMPHeader *)NULL) { + ICMPOutStats.icmps_errors++; + return IP_NO_RESOURCES; + } + + + Header->ich_type = Type; + Header->ich_code = Code; + Header->ich_param = Pointer; + if (Data) + CTEMemCopy(Header + 1, Data, DataLength); + Header->ich_xsum = ~xsum(Header, sizeof(ICMPHeader) + DataLength); + + IPInitOptions(&OptInfo); + + UpdateICMPStats(&ICMPOutStats, Type); + + IStatus = IPTransmit(IPProtInfo, NULL, HeaderBuffer, + DataLength + sizeof(ICMPHeader), Dest, Src, &OptInfo, NULL, + PROT_ICMP); + + if (IStatus != IP_PENDING) + ICMPSendComplete(NULL, HeaderBuffer); + + return IStatus; + +} + +//** SendICMPErr - Send an ICMP error message +// +// This is the routine used to send an ICMP error message, such as Destination Unreachable. +// We examine the header to find the length of the data, and also make sure we're not +// replying to another ICMP error message or a broadcast message. Then we call SendICMPMsg +// to send it. +// +// Entry: Src - IPAddr of source. +// Header - Pointer to IP Header that caused the problem. +// Type - Type of request. +// Code - Subcode of request. +// Pointer - Pointer value for request. +// +// Returns: IP_STATUS of request. +// +IP_STATUS +SendICMPErr(IPAddr Src, IPHeader UNALIGNED *Header, uchar Type, uchar Code, + ulong Pointer) +{ + uchar HeaderLength; // Length in bytes if header. + uchar DType; + + HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + + if (Header->iph_protocol == PROT_ICMP) { + ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *) + ((uchar *)Header + HeaderLength); + + if (ICH->ich_type != ICMP_ECHO) + return IP_SUCCESS; + } + + // Don't respond to sends to a broadcast destination. + DType = GetAddrType(Header->iph_dest); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType)) + return IP_SUCCESS; + + // Don't respond if the source address is bad. + DType = GetAddrType(Header->iph_src); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType) || + (IP_LOOPBACK(Header->iph_dest) && DType != DEST_LOCAL)) + return IP_SUCCESS; + + // Make sure the source we're sending from is good. + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR) || GetAddrType(Src) != DEST_LOCAL) + return IP_SUCCESS; + + // Double check to make sure it's an initial fragment. + if ((Header->iph_offset & IP_OFFSET_MASK) != 0) + return IP_SUCCESS; + + return SendICMPMsg(Src, Header->iph_src, Type, Code, Pointer, (uchar *)Header, + (uchar)(HeaderLength + 8)); + +} + + +//** ICMPTimer - Timer for ICMP +// +// This is the timer routine called periodically by global IP timer. We walk through +// the list of pending pings, and if we find one that's timed out we remove it and +// call the finish routine. +// +// Entry: NTE - Pointer to NTE being timed out. +// +// Returns: Nothing +// +void +ICMPTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + EchoControl *TimeoutList = (EchoControl *)NULL; // Timed out entries. + EchoControl *Prev, *Current; + ulong Now = CTESystemUpTime(); + + CTEGetLock(&NTE->nte_lock, &Handle); + Prev = STRUCT_OF(EchoControl, &NTE->nte_echolist, ec_next); + Current = NTE->nte_echolist; + while(Current != (EchoControl *)NULL) + if ((Current->ec_active) && (Current->ec_to < Now)) { // This one's timed out. + Prev->ec_next = Current->ec_next; + // Link him on timed out list. + Current->ec_next = TimeoutList; + TimeoutList = Current; + Current = Prev->ec_next; + } + else { + Prev = Current; + Current = Current->ec_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + // Now go through the timed out entries, and call the completion routine. + while (TimeoutList != (EchoControl *)NULL) { + EchoRtn Rtn; + + Current = TimeoutList; + TimeoutList = Current->ec_next; + + Rtn = (EchoRtn)Current->ec_rtn; + (*Rtn)(Current, IP_REQ_TIMED_OUT, NULL, 0, NULL); + } + + // + // [BUGBUG] Disabled for 4.0 sp2 + // + // ICMPRouterTimer(NTE); + +} + +//* CompleteEcho - Complete an echo request. +// +// Called when we need to complete an echo request, either because of a response +// or a received ICMP error message. We look it up, and then call the completion routine. +// +// Input: Header - Pointer to ICMP header causing completion. +// Status - Final status of request. +// Data - Data to be returned, if any. +// DataSize - Size in bytes of data. +// OptInfo - Option info structure. +// +// Returns: Nothing. +// +void +CompleteEcho(ICMPHeader UNALIGNED *Header, IP_STATUS Status, IPRcvBuf *Data, uint DataSize, + IPOptInfo *OptInfo) +{ + ushort NTEContext; + EchoControl *EC; + EchoRtn Rtn; + NetTableEntry *NTE; + + // Look up and remove the matching echo control block. + NTEContext = (*(ushort UNALIGNED *)&Header->ich_param); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if (NTEContext == NTE->nte_context) + break; + + if (NTE == NULL) + return; // Bad context value. + + EC = DeleteEC(NTE, *(((ushort UNALIGNED *)&Header->ich_param) + 1)); + if (EC != (EchoControl *)NULL) { // Found a match. + Rtn = (EchoRtn)EC->ec_rtn; + (*Rtn)(EC, Status, Data, DataSize, OptInfo); + } + + +} + +//** ICMPStatus - ICMP status handling procedure. +// +// This is the procedure called during a status change, either from an incoming ICMP +// message or a hardware status change. ICMP ignores most of these, unless we get an +// ICMP status message that was caused be an echo request. In that case we will complete +// the corresponding echo request with the appropriate error code. +// +// Input: StatusType - Type of status (NET or HW) +// StatusCode - Code identifying IP_STATUS. +// OrigDest - If this is net status, the original dest. of DG that triggered it. +// OrigSrc - " " " " " , the original src. +// Src - IP address of status originator (could be local or remote). +// Param - Additional information for status - i.e. the param field of +// an ICMP message. +// Data - Data pertaining to status - for net status, this is the first +// 8 bytes of the original DG. +// +// Returns: Nothing +// +void +ICMPStatus(uchar StatusType, IP_STATUS StatusCode, IPAddr OrigDest, IPAddr OrigSrc, IPAddr Src, + ulong Param, void *Data) +{ + if (StatusType == IP_NET_STATUS) { + ICMPHeader UNALIGNED *ICH = (ICMPHeader UNALIGNED *)Data; + // ICH is the datagram that caused the message. + + if (ICH->ich_type == ICMP_ECHO) { // And it was an echo request. + IPRcvBuf RcvBuf; + + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_buffer = (uchar *)&Src; + RcvBuf.ipr_size = sizeof(IPAddr); + CompleteEcho(ICH, StatusCode, &RcvBuf, sizeof(IPAddr), NULL); + } + } + +} + +//* ICMPMapStatus - Map an ICMP error to an IP status code. +// +// Called by ICMP status when we need to map from an incoming ICMP error code and type +// to an ICMP status. +// +// Entry: Type - Type of ICMP error. +// Code - Subcode of error. +// +// Returns: Corresponding IP status. +// +IP_STATUS +ICMPMapStatus(uchar Type, uchar Code) +{ + switch (Type) { + + case ICMP_DEST_UNREACH: + switch (Code) { + case NET_UNREACH: + case HOST_UNREACH: + case PROT_UNREACH: + case PORT_UNREACH: + return IP_DEST_UNREACH_BASE + Code; + break; + case FRAG_NEEDED: + return IP_PACKET_TOO_BIG; + break; + case SR_FAILED: + return IP_BAD_ROUTE; + break; + case DEST_NET_UNKNOWN: + case SRC_ISOLATED: + case DEST_NET_ADMIN: + case NET_UNREACH_TOS: + return IP_DEST_NET_UNREACHABLE; + break; + case DEST_HOST_UNKNOWN: + case DEST_HOST_ADMIN: + case HOST_UNREACH_TOS: + return IP_DEST_HOST_UNREACHABLE; + break; + default: + return IP_DEST_NET_UNREACHABLE; + } + break; + case ICMP_TIME_EXCEED: + if (Code == TTL_IN_TRANSIT) + return IP_TTL_EXPIRED_TRANSIT; + else + return IP_TTL_EXPIRED_REASSEM; + break; + case ICMP_PARAM_PROBLEM: + return IP_PARAM_PROBLEM; + break; + case ICMP_SOURCE_QUENCH: + return IP_SOURCE_QUENCH; + break; + default: + return IP_GENERAL_FAILURE; + break; + } + +} + +void +SendRouterSolicitation(NetTableEntry *NTE) +{ + if (NTE->nte_rtrdiscovery) { + SendICMPMsg(NTE->nte_addr, NTE->nte_rtrdiscaddr, ICMP_ROUTER_SOLICITATION, + 0, 0, NULL, 0); + } +} + +//** ICMPRouterTimer - Timeout default gateway entries +// +// This is the router advertisement timeout handler. When a router +// advertisement is received, we add the routers to our default gateway +// list if applicable. We then run a timer on the entries and refresh +// the list as new advertisements are received. If we fail to hear an +// update for a router within the specified lifetime we will delete the +// route from our routing tables. +// + +void +ICMPRouterTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + IPRtrEntry *rtrentry; + IPRtrEntry *temprtrentry; + IPRtrEntry *lastrtrentry = NULL; + uint SendIt = FALSE; + + CTEGetLock(&NTE->nte_lock, &Handle); + rtrentry = NTE->nte_rtrlist; + while (rtrentry != NULL) { + if (--rtrentry->ire_lifetime == 0) { + if (lastrtrentry == NULL) { + NTE->nte_rtrlist = rtrentry->ire_next; + } else { + lastrtrentry->ire_next = rtrentry->ire_next; + } + temprtrentry = rtrentry; + rtrentry = rtrentry->ire_next; +// DbgPrint("DeleteRoute: RtrAddr = %08x\n",temprtrentry->ire_addr); + DeleteRoute(NULL_IP_ADDR, DEFAULT_MASK, + temprtrentry->ire_addr, NTE->nte_if); + CTEFreeMem(temprtrentry); + } else { + lastrtrentry = rtrentry; + rtrentry = rtrentry->ire_next; + } + } + if (NTE->nte_rtrdisccount != 0) { + NTE->nte_rtrdisccount--; + if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_SOLICITING) && + ((NTE->nte_rtrdisccount%SOLICITATION_INTERVAL) == 0)) { + SendIt = TRUE; + } + if ((NTE->nte_rtrdiscstate == NTE_RTRDISC_DELAYING) && + (NTE->nte_rtrdisccount == 0)) { + NTE->nte_rtrdisccount = (SOLICITATION_INTERVAL)*(MAX_SOLICITATIONS-1); + NTE->nte_rtrdiscstate = NTE_RTRDISC_SOLICITING; + SendIt = TRUE; + } + } + CTEFreeLock(&NTE->nte_lock, Handle); + if (SendIt) { + SendRouterSolicitation(NTE); + } + +} + +//** ProcessRouterAdvertisement - Process a router advertisement +// +// This is the router advertisement handler. When a router advertisement +// is received, we add the routers to our default gateway list if applicable. +// + +uint +ProcessRouterAdvertisement(IPAddr Src, IPAddr LocalAddr, NetTableEntry *NTE, + ICMPRouterAdHeader UNALIGNED *AdHeader, IPRcvBuf *RcvBuf, uint Size) +{ + uchar NumAddrs = AdHeader->irah_numaddrs; + uchar AddrEntrySize = AdHeader->irah_addrentrysize; + ushort Lifetime = net_short(AdHeader->irah_lifetime); + ICMPRouterAdAddrEntry UNALIGNED *RouterAddr = (ICMPRouterAdAddrEntry UNALIGNED *)RcvBuf->ipr_buffer; + uint i; + CTELockHandle Handle; + IPRtrEntry *rtrentry; + IPRtrEntry *lastrtrentry = NULL; + int Update = FALSE; + +// DbgPrint("ProcessRouterAdvertisement: NumAddrs = %d\n",NumAddrs); +// DbgPrint("ProcessRouterAdvertisement: AddrEntrySize = %d\n",AddrEntrySize); +// DbgPrint("ProcessRouterAdvertisement: Lifetime = %d\n",Lifetime); + + if ((NumAddrs == 0) || (AddrEntrySize < 2)) // per rfc 1256 + return FALSE; + + CTEGetLock(&NTE->nte_lock, &Handle); + for ( i=0; iirae_addr & NTE->nte_mask) != (NTE->nte_addr & NTE->nte_mask)) { + continue; + } + if (!IsRouteICMP(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, NTE->nte_if)) { + continue; + } + +// DbgPrint("ProcessRouterAdvertisement: RtrAddr = %08x\n",RouterAddr->irae_addr); +// DbgPrint("ProcessRouterAdvertisement: RtrPreference = %d\n",net_long(RouterAddr->irae_preference)); + rtrentry = NTE->nte_rtrlist; + while (rtrentry != NULL) { + if (rtrentry->ire_addr == RouterAddr->irae_addr) { + rtrentry->ire_lifetime = Lifetime*2; + if (rtrentry->ire_preference != RouterAddr->irae_preference) { + rtrentry->ire_preference = RouterAddr->irae_preference; + Update = TRUE; + } + break; + } + lastrtrentry = rtrentry; + rtrentry = rtrentry->ire_next; + } + + if (rtrentry == NULL) { + rtrentry = (IPRtrEntry *) CTEAllocMem(sizeof(IPRtrEntry)); + if (rtrentry == NULL) { + return FALSE; + } + rtrentry->ire_next = NULL; + rtrentry->ire_addr = RouterAddr->irae_addr; + rtrentry->ire_preference = RouterAddr->irae_preference; + rtrentry->ire_lifetime = Lifetime*2; + if (lastrtrentry == NULL) { + NTE->nte_rtrlist = rtrentry; + } else { + lastrtrentry->ire_next = rtrentry; + } + Update = TRUE; + } + + if (Update && (RouterAddr->irae_preference != (long)0x00000080)) { // per rfc 1256 +// DbgPrint("AddRoute: RtrAddr = %08x\n",RouterAddr->irae_addr); + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, RouterAddr->irae_addr, + NTE->nte_if, NTE->nte_mss, + (uint)(1000-net_long(RouterAddr->irae_preference)), // invert for metric + IRE_PROTO_ICMP, ATYPE_OVERRIDE, NULL); + } + Update = FALSE; + } + CTEFreeLock(&NTE->nte_lock, Handle); + + return TRUE; +} + +//** ICMPRcv - Receive an ICMP datagram. +// +// Called by the main IP code when we receive an ICMP datagram. The action we +// take depends on what the DG is. For some DGs, we call upper layer status +// handlers. For Echo Requests, we call the echo responder. +// +// Entry: NTE - Pointer to NTE on which ICMP message was received. +// Dest - IPAddr of destionation. +// Src - IPAddr of source +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the +// packet +// IPHdr - Pointer to IP Header +// IPHdrLength - Bytes in Header. +// RcvBuf - ICMP message buffer. +// Size - Size in bytes of ICMP message. +// IsBCast - Boolean indicator of whether or not this came in +// as a bcast. +// Protocol - Protocol this came in on. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception +// +IP_STATUS +ICMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol, + IPOptInfo *OptInfo) +{ + ICMPHeader UNALIGNED *Header; + void *Data; // Pointer to data received. + IPHeader UNALIGNED *IPH; // Pointer to IP Header in error messages. + uint HeaderLength; // Size of IP header. + ULStatusProc ULStatus; // Pointer to upper layer status procedure. + IPOptInfo NewOptInfo; + uchar DType; + uint PassUp = FALSE; + + + ICMPInStats.icmps_msgs++; + + DType = GetAddrType(Src); + if (Size < sizeof(ICMPHeader) || DType == DEST_INVALID || + IS_BCAST_DEST(DType) || (IP_LOOPBACK(Dest) && DType != DEST_LOCAL) || + XsumBufChain(RcvBuf) != (ushort)0xffff) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Bad checksum. + } + + Header = (ICMPHeader UNALIGNED *)RcvBuf->ipr_buffer; + + + RcvBuf->ipr_buffer += sizeof(ICMPHeader); + RcvBuf->ipr_size -= sizeof(ICMPHeader); + + // Set up the data pointer for most requests, i.e. those that take less + // than MIN_FIRST_SIZE data. + + if (Size -= sizeof(ICMPHeader)) + Data = (void *)(Header + 1); + else + Data = (void *)NULL; + + switch (Header->ich_type) { + + case ICMP_DEST_UNREACH: + case ICMP_TIME_EXCEED: + case ICMP_PARAM_PROBLEM: + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + + if (Data == NULL || Size < sizeof(IPHeader)) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // No data, error. + } + + IPH = (IPHeader UNALIGNED *)Data; + HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + if (Size < (HeaderLength + MIN_ERRDATA_LENGTH)) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Not enough data for this + // ICMP message. + } + + // Make sure that the source address of the datagram that triggered + // the message is one of ours. + + if (GetAddrType(IPH->iph_src) != DEST_LOCAL) { + ICMPInStats.icmps_errors++; + return IP_SUCCESS; // Bad src in header. + } + + if (Header->ich_type != ICMP_REDIRECT) { + + UpdateICMPStats(&ICMPInStats, Header->ich_type); + + if (ULStatus = FindULStatus(IPH->iph_protocol)) { + (void)(*ULStatus)(IP_NET_STATUS, + ICMPMapStatus(Header->ich_type, Header->ich_code), + IPH->iph_dest, IPH->iph_src, Src, Header->ich_param, + (uchar *)IPH + HeaderLength); + } + if (Header->ich_code == FRAG_NEEDED) + RouteFragNeeded( + IPH, + (ushort)net_short( + *((ushort UNALIGNED *)&Header->ich_param + 1) + ) + ); + } else { + ICMPInStats.icmps_redirects++; + Redirect(NTE, Src, IPH->iph_dest, IPH->iph_src, + Header->ich_param); + } + + PassUp = TRUE; + + break; + + + case ICMP_ECHO_RESP: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_echoreps++; + // Look up and remove the matching echo control block. + CompleteEcho(Header, IP_SUCCESS, RcvBuf, Size, OptInfo); + + PassUp = TRUE; + + break; + + case ICMP_ECHO: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_echos++; + + // Create our new optinfo structure. + IPInitOptions(&NewOptInfo); + NewOptInfo.ioi_tos = OptInfo->ioi_tos; + NewOptInfo.ioi_flags = OptInfo->ioi_flags; + + // If we have options, we need to reverse them and update any + // record route info. We can use the option buffer supplied by the + // IP layer, since we're part of him. + if (OptInfo->ioi_options != (uchar *)NULL) + IPUpdateRcvdOptions(OptInfo, &NewOptInfo, Src, LocalAddr); + + + SendEcho(Src, LocalAddr, ICMP_ECHO_RESP, + *(ushort UNALIGNED *)&Header->ich_param, + *((ushort UNALIGNED *)&Header->ich_param + 1), + RcvBuf, Size, &NewOptInfo); + + IPFreeOptions(&NewOptInfo); + break; + + case ADDR_MASK_REQUEST: + if (IsBCast) + return IP_SUCCESS; // ICMP doesn't respond to bcast requests. + ICMPInStats.icmps_addrmasks++; + + Dest = Src; + + SendICMPMsg(LocalAddr, Dest, ADDR_MASK_REPLY, 0, Header->ich_param, + (uchar *)&NTE->nte_mask, sizeof(IPMask)); + break; + + case ICMP_ROUTER_ADVERTISEMENT: + if (Header->ich_code != 0) + return IP_SUCCESS; // Code must be 0 as per RFC1256 + if (NTE->nte_rtrdiscovery) { + if (!ProcessRouterAdvertisement(Src, LocalAddr, NTE, + (ICMPRouterAdHeader *)&Header->ich_param, RcvBuf, Size)) + return IP_SUCCESS; // An error was returned + } + PassUp = TRUE; + break; + + case ICMP_ROUTER_SOLICITATION: + if (Header->ich_code != 0) + return IP_SUCCESS; // Code must be 0 as per RFC1256 + PassUp = TRUE; + break; + + default: + PassUp = TRUE; + UpdateICMPStats(&ICMPInStats, Header->ich_type); + break; + } + + // + // Pass the packet up to the raw layer if applicable. + // + if (PassUp && (RawPI != NULL)) { + if (RawPI->pi_rcv != NULL) { + // + // Restore the original values. + // + RcvBuf->ipr_buffer -= sizeof(ICMPHeader); + RcvBuf->ipr_size += sizeof(ICMPHeader); + Size += sizeof(ICMPHeader); + Data = (void *) Header; + + (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr, + IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo); + } + } + + return IP_SUCCESS; +} + + +//** ICMPEcho - Send an echo to the specified address. +// +// Entry: ControlBlock - Pointer to an EchoControl structure. This structure +// must remain valid until the req. completes. +// Timeout - Time in milliseconds to wait for response. +// Data - Pointer to data to send with echo. +// DataSize - Size in bytes of data. +// Callback - Routine to call when request is responded to or times out. +// Dest - Address to be pinged. +// OptInfo - Pointer to opt info structure to use for ping. +// +// Returns: IP_STATUS of attempt to ping.. +// +IP_STATUS +ICMPEcho(EchoControl *ControlBlock, ulong Timeout, void *Data, uint DataSize, EchoRtn Callback, + IPAddr Dest, IPOptInfo *OptInfo) +{ + IPAddr Dummy; + NetTableEntry *NTE; + CTELockHandle Handle; + ushort Seq; + IP_STATUS Status; + IPOptInfo NewOptInfo; + IPRcvBuf RcvBuf; + uint MTU; + Interface *IF; + uchar DType; + EchoControl *Current; + + if (OptInfo->ioi_ttl == 0) + return IP_BAD_OPTION; + + IPInitOptions(&NewOptInfo); + NewOptInfo.ioi_ttl = OptInfo->ioi_ttl; + NewOptInfo.ioi_flags = OptInfo->ioi_flags; + NewOptInfo.ioi_tos = OptInfo->ioi_tos & 0xfc; + + if (OptInfo->ioi_optlength != 0) { + Status = IPCopyOptions(OptInfo->ioi_options, OptInfo->ioi_optlength, + &NewOptInfo); + + if (Status != IP_SUCCESS) + return Status; + } + + if (!IP_ADDR_EQUAL(NewOptInfo.ioi_addr, NULL_IP_ADDR)) + Dest = NewOptInfo.ioi_addr; + + DType = GetAddrType(Dest); + if (DType == DEST_INVALID) { + IPFreeOptions(&NewOptInfo); + return IP_BAD_DESTINATION; + } + + if ((IF = LookupNextHopWithBuffer(Dest, NULL_IP_ADDR, &Dummy, &MTU, 0x1, NULL, 0)) == NULL) { + IPFreeOptions(&NewOptInfo); + return IP_DEST_HOST_UNREACHABLE; // Don't know how to get there. + } + + // Loop through the NetTable, looking for a matching NTE. + CTEGetLock(&RouteTableLock, &Handle); + if (DHCPActivityCount != 0) + NTE = NULL; + else + NTE = BestNTEForIF(Dummy, IF); + CTEFreeLock(&RouteTableLock, Handle); + +#ifdef _PNP_POWER + // We're done with the interface, so dereference it. + DerefIF(IF); +#endif + + if (NTE == NULL) { + // Couldn't find a matching NTE. This is very bad. + //DEBUGCHK; + + DbgPrint("ICMP: Failed to find NTE when going to %x\n",Dest); + + IPFreeOptions(&NewOptInfo); + return IP_DEST_HOST_UNREACHABLE; + } + + // Figure out the timeout. + ControlBlock->ec_to = CTESystemUpTime() + Timeout; + ControlBlock->ec_rtn = Callback; + ControlBlock->ec_active = 0; // Prevent from timing out until sent + CTEGetLock(&NTE->nte_lock, &Handle); + // Link onto ping list, and get seq. # */ + Seq = ++NTE->nte_icmpseq; + ControlBlock->ec_seq = Seq; + ControlBlock->ec_next = NTE->nte_echolist; + NTE->nte_echolist = ControlBlock; + CTEFreeLock(&NTE->nte_lock, Handle); + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_buffer = Data; + RcvBuf.ipr_size = DataSize; + Status = SendEcho(Dest, NTE->nte_addr, ICMP_ECHO, NTE->nte_context, + Seq, &RcvBuf, DataSize, &NewOptInfo); + + IPFreeOptions(&NewOptInfo); + + if (Status != IP_PENDING && Status != IP_SUCCESS) { // We had an error on the send. + if (DeleteEC(NTE, Seq) != (EchoControl *)NULL) + return Status; // We found it. + } + + // + // If the request is still pending, activate the timer + // + CTEGetLock(&NTE->nte_lock, &Handle); + + for ( + Current = NTE->nte_echolist; + Current != (EchoControl *)NULL; + Current = Current->ec_next + ) { + if (Current == ControlBlock) { + ControlBlock->ec_active = 1; // start the timer + break; + } + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + return IP_PENDING; + +} + + +//** ICMPEchoRequest - Common dispatch routine for echo requests +// +// This is the routine called by the OS-specific code on behalf of a user to issue an +// echo request. +// +// Entry: InputBuffer - Pointer to an ICMP_ECHO_REQUEST structure. +// InputBufferLength - Size in bytes of the InputBuffer. +// ControlBlock - Pointer to an EchoControl structure. This +// structure must remain valid until the +// request completes. +// Callback - Routine to call when request is responded to +// or times out. +// +// Returns: IP_STATUS of attempt to ping. +// +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ) +{ + PICMP_ECHO_REQUEST requestBuffer; + struct IPOptInfo optionInfo; + PUCHAR endOfRequestBuffer; + IP_STATUS status; + + +#ifdef NT + + PAGED_CODE(); + +#endif //NT + + requestBuffer = (PICMP_ECHO_REQUEST) InputBuffer; + endOfRequestBuffer = ((PUCHAR) requestBuffer) + InputBufferLength; + + // + // Validate the request. + // + if (InputBufferLength < sizeof(ICMP_ECHO_REQUEST)) { + status = IP_BUF_TOO_SMALL; + goto common_echo_exit; + } + + if (requestBuffer->DataSize > 0) { + if ( (requestBuffer->DataOffset < sizeof(ICMP_ECHO_REQUEST)) + || + ( ( ((PUCHAR)requestBuffer) + requestBuffer->DataOffset + + requestBuffer->DataSize + ) + > + endOfRequestBuffer + ) + ) { + status = IP_GENERAL_FAILURE; + goto common_echo_exit; + } + } + + if (requestBuffer->OptionsSize > 0) { + if ( (requestBuffer->OptionsOffset < sizeof(ICMP_ECHO_REQUEST)) + || + ( ( ((PUCHAR)requestBuffer) + requestBuffer->OptionsOffset + + requestBuffer->OptionsSize + ) + > + endOfRequestBuffer + ) + ) { + status = IP_GENERAL_FAILURE; + goto common_echo_exit; + } + } + + // + // Copy the options to a local structure. + // + if (requestBuffer->OptionsValid) { + optionInfo.ioi_optlength = requestBuffer->OptionsSize; + + if (requestBuffer->OptionsSize > 0) { + optionInfo.ioi_options = ((uchar *) requestBuffer) + + requestBuffer->OptionsOffset; + } + else { + optionInfo.ioi_options = NULL; + } + optionInfo.ioi_addr = 0; + optionInfo.ioi_ttl = requestBuffer->Ttl; + optionInfo.ioi_tos = requestBuffer->Tos; + optionInfo.ioi_flags = requestBuffer->Flags; + } + else { + optionInfo.ioi_optlength = 0; + optionInfo.ioi_options = NULL; + optionInfo.ioi_addr = 0; + optionInfo.ioi_ttl = DEFAULT_TTL; + optionInfo.ioi_tos = 0; + optionInfo.ioi_flags = 0; + } + + status = ICMPEcho( + ControlBlock, + requestBuffer->Timeout, + ((uchar *)requestBuffer) + requestBuffer->DataOffset, + requestBuffer->DataSize, + Callback, + (IPAddr) requestBuffer->Address, + &optionInfo + ); + +common_echo_exit: + + return(status); + +} // ICMPEchoRequest + + +//** ICMPEchoComplete - Common completion routine for echo requests +// +// This is the routine is called by the OS-specific code to process an +// ICMP echo response. +// +// Entry: OutputBuffer - Pointer to an ICMP_ECHO_REPLY structure. +// OutputBufferLength - Size in bytes of the OutputBuffer. +// Status - The status of the reply. +// Data - The reply data (may be NULL). +// DataSize - The amount of reply data. +// OptionInfo - A pointer to the reply options +// +// Returns: The number of bytes written to the output buffer +// +ulong +ICMPEchoComplete( + EchoControl *ControlBlock, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ) +{ + PICMP_ECHO_REPLY replyBuffer; + IPRcvBuf *dataBuffer; + uchar optionsLength; + uchar *tmp; + ulong bytesReturned = sizeof(ICMP_ECHO_REPLY); + + + replyBuffer = (PICMP_ECHO_REPLY) ControlBlock->ec_replybuf; + dataBuffer = (IPRcvBuf *) Data; + + if (OptionInfo != NULL) { + optionsLength = OptionInfo->ioi_optlength; + } + else { + optionsLength = 0; + } + + // + // Initialize the reply buffer + // + replyBuffer->Options.OptionsSize = 0; + replyBuffer->Options.OptionsData = (unsigned char FAR *) (replyBuffer + 1); + replyBuffer->DataSize = 0; + replyBuffer->Data = replyBuffer->Options.OptionsData; + + if ( (Status != IP_SUCCESS) && (DataSize == 0)) { + // + // Timed out or internal error. + // + replyBuffer->Reserved = 0; // indicate no replies. + replyBuffer->Status = Status; + } + else { + if (Status != IP_SUCCESS) { + // + // A message other than an echo reply was received. + // The IP Address of the system that reported the error is + // in the data buffer. There is no other data. + // + CTEAssert(dataBuffer->ipr_size == sizeof(IPAddr)); + + CTEMemCopy( + &(replyBuffer->Address), + dataBuffer->ipr_buffer, + sizeof(IPAddr) + ); + + DataSize = 0; + dataBuffer = NULL; + } + // else { + // + // BUGBUG - we currently depend on the fact that the destination + // address is still in the request buffer. The reply address + // should just be a parameter to this function. In NT, this + // just works since the input and output buffers are the same. + // In the VXD, the destination address must be put into the + // reply buffer by the OS-specific code. + // } + + // + // Check that the reply buffer is large enough to hold all the data. + // + if ( ControlBlock->ec_replybuflen < + (sizeof(ICMP_ECHO_REPLY) + DataSize + optionsLength) + ) { + // + // Not enough space to hold the reply. + // + replyBuffer->Reserved = 0; // indicate no replies + replyBuffer->Status = IP_BUF_TOO_SMALL; + } + else { + replyBuffer->Reserved = 1; // indicate one reply + replyBuffer->Status = Status; + replyBuffer->RoundTripTime = CTESystemUpTime() - + ControlBlock->ec_starttime; + + // + // Copy the reply options. + // + if (OptionInfo != NULL) { + replyBuffer->Options.Ttl = OptionInfo->ioi_ttl; + replyBuffer->Options.Tos = OptionInfo->ioi_tos; + replyBuffer->Options.Flags = OptionInfo->ioi_flags; + replyBuffer->Options.OptionsSize = optionsLength; + + if (optionsLength > 0) { + + CTEMemCopy( + replyBuffer->Options.OptionsData, + OptionInfo->ioi_options, + optionsLength + ); + } + } + + // + // Copy the reply data + // + replyBuffer->DataSize = (ushort) DataSize; + replyBuffer->Data = replyBuffer->Options.OptionsData + + replyBuffer->Options.OptionsSize; + + if (DataSize > 0) { + uint bytesToCopy; + + CTEAssert(Data != NULL); + + tmp = replyBuffer->Data; + + while (DataSize) { + CTEAssert(dataBuffer != NULL); + + bytesToCopy = (DataSize > dataBuffer->ipr_size) ? + dataBuffer->ipr_size : DataSize; + + CTEMemCopy( + tmp, + dataBuffer->ipr_buffer, + bytesToCopy + ); + + tmp += bytesToCopy; + DataSize -= bytesToCopy; + dataBuffer = dataBuffer->ipr_next; + } + } + + bytesReturned += replyBuffer->DataSize + optionsLength; + + // + // Convert the kernel pointers to offsets from start of reply buffer. + // + replyBuffer->Options.OptionsData = (unsigned char FAR *) + (((unsigned long) replyBuffer->Options.OptionsData) - + ((unsigned long) replyBuffer)); + + replyBuffer->Data = (void FAR *) + (((unsigned long) replyBuffer->Data) - + ((unsigned long) replyBuffer)); + } + } + + return(bytesReturned); +} + + +#ifdef VXD + +struct _pending_echo { + EchoControl ControlBlock; + CTEBlockStruc BlockStruc; +}; + +typedef struct _pending_echo PendingEcho; + + +//** VXDEchoComplete - OS-specific icmp echo completion routine. +// +// This routine is called by the OS-indepenent code to process a completed +// ICMP echo request. It calls common code to package the response. +// +// Entry: Context - A pointer to an EchoControl structure. +// Status - The status of the request +// Data - A pointer to the response data. +// DataSize - The amount of response data. +// OptionInfo - A pointer to the options contained in the reply. +// +// Returns: Nothing. +// +void +VXDEchoComplete( + void *Context, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ) +{ + PendingEcho *pendingEcho; + EchoControl *controlBlock; + ulong bytesReturned; + + + controlBlock = (EchoControl *) Context; + + bytesReturned = ICMPEchoComplete( + controlBlock, + Status, + Data, + DataSize, + OptionInfo + ); + + // + // The request thread will copy the returned byte count from + // the control block. + // + controlBlock->ec_replybuflen = bytesReturned; + + // + // Signal waiting request thread. + // + pendingEcho = STRUCT_OF(PendingEcho, controlBlock, ControlBlock); + CTESignal(&(pendingEcho->BlockStruc), Status); + + return; +} + + +//** VXDEchoRequest - Send an echo to the specified address. +// +// This routine dispatches an echo request on the VXD platform to the +// common code +// +// Entry: InBuf - Pointer to input buffer. +// InBufLen - Pointer to input buffer length. +// OutBuf - Pointer to output buffer. +// OutBufLen - Pointer to output buffer length. +// +// Returns: DWORD Win32 completion status. +// +ULONG +VXDEchoRequest( + void * InBuf, + ulong * InBufLen, + void * OutBuf, + ulong * OutBufLen + ) +{ + IP_STATUS ipStatus; + PendingEcho pendingEcho; + + pendingEcho.ControlBlock.ec_starttime = CTESystemUpTime(); + pendingEcho.ControlBlock.ec_replybuf = OutBuf; + pendingEcho.ControlBlock.ec_replybuflen = *OutBufLen; + CTEInitBlockStruc(&pendingEcho.BlockStruc); + + ipStatus = ICMPEchoRequest( + InBuf, + *InBufLen, + &(pendingEcho.ControlBlock), + VXDEchoComplete + ); + + if (ipStatus == IP_PENDING) { + ipStatus = CTEBlock(&(pendingEcho.BlockStruc)); + + if (ipStatus == IP_SUCCESS) { + PICMP_ECHO_REQUEST requestBuffer; + PICMP_ECHO_REPLY replyBuffer; + + // + // BUGBUG: + // + // It is necessary to copy the original destination address into + // the reply buffer because the src address is not provided to + // the completion routine for an echo response. This is the only + // reason why the signalling status is the reply status instead + // of just success. + // + requestBuffer = (PICMP_ECHO_REQUEST) InBuf; + replyBuffer = (PICMP_ECHO_REPLY) OutBuf; + + replyBuffer->Address = requestBuffer->Address; + } + + // + // The ioctl is considered successful as long as we can submit + // the request. The status of the request is contained in the + // reply buffer. The completion routine stuffed the return + // buffer length back into the control block. + // + ipStatus = IP_SUCCESS; + *OutBufLen = pendingEcho.ControlBlock.ec_replybuflen; + } + else { + // + // An internal error of some kind occurred. Since the VXD can + // return the IP_STATUS directly, do so. + // + CTEAssert(ipStatus != IP_SUCCESS); + + *OutBufLen = 0; + } + + return(ipStatus); +} + +#endif // VXD + + +#pragma BEGIN_INIT +//** ICMPInit - Initialize ICMP. +// +// This routine initializes ICMP. All we do is allocate and link up some header buffers, +/// and register our protocol with IP. +// +// Entry: NumBuffers - Number of ICMP buffers to allocate. +// +// Returns: Nothing +// +void +ICMPInit(uint NumBuffers) +{ + ICMPHeader **IHP; // Pointer to current ICMP header. + + + CTEInitLock(&ICMPHeaderLock); + MaxICMPHeaders = NumBuffers; + CurrentICMPHeaders = 0; + ICMPHeaderList= (ICMPHeader *)NULL; + + while (NumBuffers--) { + IHP = (ICMPHeader **) CTEAllocMem( + sizeof(ICMPHeader) + sizeof(IPHeader) + + sizeof(IPHeader) + MAX_OPT_SIZE + 8 + ); + + if (IHP == (ICMPHeader **)NULL) { + break; + } + + *IHP = ICMPHeaderList; + ICMPHeaderList = (ICMPHeader *)IHP; + CurrentICMPHeaders++; + } + + IPRegisterProtocol(PROT_ICMP, ICMPRcv, ICMPSendComplete, ICMPStatus, NULL); + +} + +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/ip/icmp.h b/private/ntos/tdi/tcpip/ip/icmp.h new file mode 100644 index 000000000..f45dac536 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/icmp.h @@ -0,0 +1,70 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** icmp.h - IP ICMP header. +// +// This module contains private ICMP definitions. +// + +#define PROT_ICMP 1 + +#define ICMP_ECHO_RESP 0 +#define ICMP_ECHO 8 +#define ICMP_TIMESTAMP 13 +#define ICMP_TIMESTAMP_RESP 14 + +#define MIN_ERRDATA_LENGTH 8 // Minimum amount of data we need. + +// Structure of an ICMP header. + +struct ICMPHeader { + uchar ich_type; // Type of ICMP packet. + uchar ich_code; // Subcode of type. + ushort ich_xsum; // Checksum of packet. + ulong ich_param; // Type-specific parameter field. +}; /* ICMPHeader */ + +struct ICMPRouterAdHeader { + uchar irah_numaddrs; // Number of addresses + uchar irah_addrentrysize; // Address Entry Size + ushort irah_lifetime; // Lifetime +}; /* ICMPRouterAdHeader */ + +struct ICMPRouterAdAddrEntry { + IPAddr irae_addr; // Router Address + long irae_preference; // Preference Level +}; /* ICMPRouterAdAddrEntry */ + +/*NOINC*/ +typedef struct ICMPHeader ICMPHeader; +typedef struct ICMPRouterAdHeader ICMPRouterAdHeader; +typedef struct ICMPRouterAdAddrEntry ICMPRouterAdAddrEntry; + +typedef void (*EchoRtn)(void *, IP_STATUS, void *, uint, IPOptInfo *); +/*INC*/ + +struct EchoControl { + struct EchoControl *ec_next; // Next control structure in list. + ulong ec_to; // Timeout + void *ec_rtn; // Pointer to routine to call when completing request. + ushort ec_seq; // Seq. # of this ping request. + uchar ec_active; // Set when packet has been sent + uchar ec_pad; // Pad. + ulong ec_starttime; // time request was issued + void *ec_replybuf; // buffer to store replies + ulong ec_replybuflen; // size of reply buffer +}; /* EchoControl */ + +/*NOINC*/ +typedef struct EchoControl EchoControl; +/*INC*/ + +extern ICMPHeader *GetICMPBuffer(uint Size, PNDIS_BUFFER *Buffer); +extern void FreeICMPBuffer(PNDIS_BUFFER Buffer); +extern void ICMPSendComplete(void *DataPtr, PNDIS_BUFFER BufferChain); + + + diff --git a/private/ntos/tdi/tcpip/ip/igmp.c b/private/ntos/tdi/tcpip/ip/igmp.c new file mode 100644 index 000000000..340f0380f --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/igmp.c @@ -0,0 +1,799 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** igmp.c - IP multicast routines. +// +// This file contains all the routines related to the IGMP protocol. + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "igmp.h" +#include "icmp.h" +#include "ipxmit.h" +#include "llipif.h" +#include "iproute.h" + + +#define IGMP_QUERY 0x11 //Membership query +#define IGMP_REPORT_V1 0x12 //Version 1 membership report +#define IGMP_REPORT_V2 0x16 //Version 2 membership report +#define IGMP_LEAVE 0x17 //Leave Group + + +#define IGMPV1 2 //IGMP version 1 +#define IGMPV2 3 //IGMP version 2 + +// +// undefine for 4.0 sp2 +// +#undef IGMPV2 + +#define ALL_HOST_MCAST 0x010000E0 + +#define MAX_DELAY_TICKS 20 //used when sending a report after a + //mcast group has been added. The + //report is sent at a interval of + //500 msecs to 9.5 secs + +// +// The following values are used to initialize counters that keep time in +// 1/2 a sec. +// +#define MAX_DELAY_IGMPV1_QUERY_RESP 20 //10 secs + +// +// The amount of time we stay in the "IGMPV1 Router Present" state in the +// absence of an IGMPV1 query +// +#define VERSION1_ROUTER_TIMEOUT 800 //400 secs + +int RandomValue; +int Seed; + +// Structure of an IGMP header. +typedef struct IGMPHeader { + uchar igh_vertype; // Type of igmp message + uchar igh_rsvd; // max. resp. time for igmpv2 messages; will be 0 + // for igmpv1 messages + ushort igh_xsum; + IPAddr igh_addr; +} IGMPHeader; + + +typedef struct IGMPBlockStruct { + struct IGMPBlockStruct *ibs_next; + CTEBlockStruc ibs_block; +} IGMPBlockStruct; + +void *IGMPProtInfo; + +IGMPBlockStruct *IGMPBlockList; +uchar IGMPBlockFlag; + +DEFINE_LOCK_STRUCTURE(IGMPLock) + +extern ProtInfo *RawPI; // Raw IP protinfo + +extern IP_STATUS IPCopyOptions(uchar *, uint, IPOptInfo *); +extern void IPInitOptions(IPOptInfo *); +extern void *IPRegisterProtocol(uchar Protocol, void *RcvHandler, + void *XmitHandler, void *StatusHandler, void *RcvCmpltHandler); + + +#ifdef NT + +// +// All of the init code can be discarded +// +#ifdef ALLOC_PRAGMA + +uint IGMPInit(void); + +#pragma alloc_text(INIT, IGMPInit) + +#endif // ALLOC_PRAGMA + +#endif // NT + +//* IGMPRandomTicks - Generate a random value of timer ticks. +// +// A random number routine to generate a random number of timer ticks, +// between 1 and time (in units of half secs) passed. The random number +// algorithm is adapted from the book 'System Simulation' by Geoffrey Gordon. +// +// Input: Nothing. +// +// Returns: A random value between 1 and TimeDelayInHalfSec. +// +uint +IGMPRandomTicks(uint TimeDelayInHalfSec) +{ + + RandomValue = RandomValue * 1220703125; + + if (RandomValue < 0) { + RandomValue += 2147483647; // inefficient, but avoids warnings. + RandomValue++; + } + + // Not sure if RandomValue can get to 0, but if it does the algorithm + // degenerates, so fix this if it happens. + if (RandomValue == 0) + RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1; + + return (uint)(((uint)RandomValue % TimeDelayInHalfSec) + 1); +} + + +//* FindIGMPAddr - Find an mcast entry on an NTE. +// +// Called to search an NTE for an IGMP entry for a given class D address. +// We walk down the chain on the NTE looking for it. If we find it, +// we return a pointer to it and the one immediately preceding it. If we +// don't find it we return NULL. We assume the caller has taken the lock +// on the NTE before calling us. +// +// Input: NTE - NTE on which to search. +// Addr - Class D address to find. +// PrevPtr - Where to return pointer to preceding entry. +// +// Returns: Pointer to matching IGMPAddr structure if found, or NULL if not +// found. +// +IGMPAddr * +FindIGMPAddr(NetTableEntry *NTE, IPAddr Addr, IGMPAddr **PrevPtr) +{ + IGMPAddr *Current, *Temp; + + Temp = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next); + Current = NTE->nte_igmplist; + + while (Current != NULL) { + if (IP_ADDR_EQUAL(Current->iga_addr, Addr)) { + // Found a match, so return it. + *PrevPtr = Temp; + break; + } + Temp = Current; + Current = Current->iga_next; + } + + return Current; + +} + +//** IGMPRcv - Receive an IGMP datagram. +// +// Called by IP when we receive an IGMP datagram. We validate it to make sure +// it's reasonable. Then if it it's a query for a group to which we belong +// we'll start a response timer. If it's a report to a group to which we belong +// we'll stop any running timer. +// +// The IGMP header is only 8 bytes long, and so should always fit in exactly +// one IP rcv buffer. We check this to make sure, and if it takes multiple +// buffers we discard it. +// +// Entry: NTE - Pointer to NTE on which IGMP message was received. +// Dest - IPAddr of destination (should be a Class D address). +// Src - IPAddr of source +// LocalAddr - Local address of network which caused this to be +// received. +// SrcAddr - Address of local interface which received the +// packet +// IPHdr - Pointer to the IP Header. +// IPHdrLength - Bytes in IPHeader. +// RcvBuf - Pointer to IP receive buffer chain. +// Size - Size in bytes of IGMP message. +// IsBCast - Boolean indicator of whether or not this came in +// as a bcast (should always be true). +// Protocol - Protocol this came in on. +// OptInfo - Pointer to info structure for received options. +// +// Returns: Status of reception +IP_STATUS +IGMPRcv(NetTableEntry *NTE, IPAddr Dest, IPAddr Src, IPAddr LocalAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *IPHdr, uint IPHdrLength, + IPRcvBuf *RcvBuf, uint Size, uchar IsBCast, uchar Protocol, + IPOptInfo *OptInfo) +{ + IGMPHeader UNALIGNED *IGH; + CTELockHandle Handle; + IGMPAddr *AddrPtr, *PrevPtr; + uchar DType; + uint ReportingDelayInHalfSec; + + + CTEAssert(CLASSD_ADDR(Dest)); + CTEAssert(IsBCast); + + // Make sure we're running at least level 2 of IGMP support. + if (IGMPLevel != 2) + return IP_SUCCESS; + + // Discard packets with invalid or broadcast source addresses. + DType = GetAddrType(Src); + if (DType == DEST_INVALID || IS_BCAST_DEST(DType)) + return IP_SUCCESS; + + // Check the size to make sure it's valid. + if (Size != sizeof(IGMPHeader) || RcvBuf->ipr_size != sizeof(IGMPHeader)) + return IP_SUCCESS; + + // Now get the pointer to the header, and validate the xsum. + IGH = (IGMPHeader UNALIGNED *)RcvBuf->ipr_buffer; + + if (xsum(IGH, sizeof(IGMPHeader)) != 0xffff) { + // Bad checksum, so fail. + return IP_SUCCESS; + } + + // If we sent it, don't process this message. + if (IP_ADDR_EQUAL(Src, LocalAddr)) + return IP_SUCCESS; + + // OK, we may need to process this. See if we are a member of the + // destination group. If we aren't, there's no need to proceed further. + CTEGetLock(&NTE->nte_lock, &Handle); + + if (NTE->nte_flags & NTE_VALID) { + // + // The NTE is valid. Demux on type. + // + switch (IGH->igh_vertype) { + + case IGMP_QUERY: + + // + // If it is an IGMPV1 query, set the timer value for staying in + // igmpv1 mode + // +#ifdef IGMPV2 + if (IGH->igh_rsvd == 0) { + // + // Since for any interface we always get notified with + // same NTE, locking the NTE is fine. We don't have to + // lock the interface structure + // + if (NTE->nte_if->IgmpVersion == IGMPV2) { + NTE->nte_if->IgmpVersion = IGMPV1; + } + NTE->nte_if->IgmpVer1Timeout = VERSION1_ROUTER_TIMEOUT; + ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP; + } + else { + ReportingDelayInHalfSec = IGH->igh_rsvd * 5; //field's unit are in 100ms + } +#else + ReportingDelayInHalfSec = MAX_DELAY_IGMPV1_QUERY_RESP; +#endif + + // + // This is a query. Walk our list and set a random report timer for + // all those class D addresses that don't already have one running + // (except for the all host's address). + // + for (AddrPtr = NTE->nte_igmplist; AddrPtr != NULL; AddrPtr = AddrPtr->iga_next) { + if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) { + if (AddrPtr->iga_timer == 0) { + AddrPtr->iga_timer = IGMPRandomTicks(ReportingDelayInHalfSec); + } + } + } + + break; + + case IGMP_REPORT_V1: + case IGMP_REPORT_V2: + // + // This is a report. Check it's validity and see if we have a + // report timer running for that address. If we do, stop it. + // Make sure the destination address matches the address in the + // IGMP header. + // + if (IP_ADDR_EQUAL(Dest, IGH->igh_addr)) { + // The addresses match. See if we have a membership in this + // group. + AddrPtr = FindIGMPAddr(NTE, IGH->igh_addr, &PrevPtr); + if (AddrPtr != NULL) { + // We found a matching class D address. Stop the timer. + AddrPtr->iga_timer = 0; + } + } + + break; + + default: + break; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + // + // Pass the packet up to the raw layer if applicable. + // + if (RawPI != NULL) { + if (RawPI->pi_rcv != NULL) { + (*(RawPI->pi_rcv))(NTE, Dest, Src, LocalAddr, SrcAddr, IPHdr, + IPHdrLength, RcvBuf, Size, IsBCast, Protocol, OptInfo); + } + } + + return IP_SUCCESS; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + + return IP_SUCCESS; +} + +//* SendIGMPReport - Send an IGMP report. +// +// Called when we want to send an IGMP report for some reason. For this +// purpose we steal ICMP buffers. What we'll do is get one, fill it in, +// and send it. +// +// Input: Dest - Destination to send to. +// Src - Source to send from. +// +// Returns: Nothing. +// +void +SendIGMPReport(uint ChangeType, uint IgmpVersion, IPAddr Dest, IPAddr Src) +{ + IGMPHeader *IGH; + PNDIS_BUFFER Buffer; + IPOptInfo OptInfo; // Options for this transmit. + IP_STATUS Status; + int ReportType; + + CTEAssert(CLASSD_ADDR(Dest)); + CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)); + + // Make sure we never send a report for the all-hosts mcast address. + if (IP_ADDR_EQUAL(Dest, ALL_HOST_MCAST)) { + DEBUGCHK; + return; + } + // + // If the report to be sent is a "Leave Group" report but we have + // detected an igmp v1 router on this net, do not send the report + // +#ifdef IGMPV2 + if (IgmpVersion == IGMPV1) { + if (ChangeType == IGMP_DELETE) { + return; + } else { +#endif + ReportType = IGMP_REPORT_V1; +#ifdef IGMPV2 + } + } else { + if (ChangeType == IGMP_DELETE) { + ReportType = IGMP_LEAVE; + } else { + ReportType = IGMP_REPORT_V2; + } + } +#endif + + IGH = (IGMPHeader *)GetICMPBuffer(sizeof(IGMPHeader), &Buffer); + if (IGH != NULL) { + // We got the buffer. Fill it in and send it. + IGH->igh_vertype = ReportType; + IGH->igh_rsvd = 0; + IGH->igh_xsum = 0; + IGH->igh_addr = Dest; + IGH->igh_xsum = ~xsum(IGH, sizeof(IGMPHeader)); + + IPInitOptions(&OptInfo); + OptInfo.ioi_ttl = 1; + + Status = IPTransmit(IGMPProtInfo, NULL, Buffer, sizeof(IGMPHeader), + Dest, Src, &OptInfo, NULL, PROT_IGMP); + + if (Status != IP_PENDING) + ICMPSendComplete(NULL, Buffer); + } + +} + +//* IGMPAddrChange - Change the IGMP address list on an NTE. +// +// Called to add or delete an IGMP address. We're given the relevant NTE, +// the address, and the action to be performed. We validate the NTE, the +// address, and the IGMP level, and then attempt to perform the action. +// +// There are a bunch of strange race conditions that can occur during adding/ +// deleting addresses, related to trying to add the same address twice and +// having it fail, or adding and deleting the same address simultaneously. Most +// of these happen because we have to free the lock to call the interface, +// and the call to the interface can fail. To prevent this we serialize all +// access to this routine. Only one thread of execution can go through here +// at a time, all others are blocked. +// +// Input: NTE - NTE with list to be altered. +// Addr - Address affected. +// ChangeType - Type of change - IGMP_ADD, IGMP_DELETE, +// IGMP_DELETE_ALL. +// +// Returns: IP_STATUS of attempt to perform action. +// +IP_STATUS +IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr, uint ChangeType) +{ + CTELockHandle Handle; + IGMPAddr *AddrPtr, *PrevPtr; + IP_STATUS Status; + Interface *IF; + uint AddrAdded; + IGMPBlockStruct Block; + IGMPBlockStruct *BlockPtr; + uint IgmpVersion; + + // First make sure we're at level 2 of IGMP support. + + if (IGMPLevel != 2) + return IP_BAD_REQ; + + CTEInitBlockStruc(&Block.ibs_block); + + // Make sure we're the only ones in this routine. If someone else is + // already here, block. + + CTEGetLock(&IGMPLock, &Handle); + if (IGMPBlockFlag) { + + // Someone else is already here. Walk down the block list, and + // put ourselves on the end. Then free the lock and block on our + // IGMPBlock structure. + BlockPtr = STRUCT_OF(IGMPBlockStruct, &IGMPBlockList, ibs_next); + while (BlockPtr->ibs_next != NULL) + BlockPtr = BlockPtr->ibs_next; + + Block.ibs_next = NULL; + BlockPtr->ibs_next = &Block; + CTEFreeLock(&IGMPLock, Handle); + CTEBlock(&Block.ibs_block); + } else { + // Noone else here, set the flag so noone else gets in and free the + // lock. + IGMPBlockFlag = 1; + CTEFreeLock(&IGMPLock, Handle); + } + + // Now we're in the routine, and we won't be reentered here by another + // thread of execution. Make sure everything's valid, and figure out + // what to do. + + Status = IP_SUCCESS; + + // Now get the lock on the NTE and make sure it's valid. + CTEGetLock(&NTE->nte_lock, &Handle); + if ((NTE->nte_flags & NTE_VALID) || ChangeType == IGMP_DELETE_ALL) { + // The NTE is valid. Try to find an existing IGMPAddr structure + // that matches the input address. + AddrPtr = FindIGMPAddr(NTE, Addr, &PrevPtr); + IF = NTE->nte_if; + +#ifdef IGMPV2 + IgmpVersion = IF->IgmpVersion; +#else + IgmpVersion = IGMPV1; +#endif + // Now figure out the action to be performed. + switch (ChangeType) { + + case IGMP_ADD: + + // We're to add this. If AddrPtr is NULL, we'll need to + // allocate memory and link the new IGMP address in. Otherwise + // we can just increment the reference count on the existing + // address structure. + if (AddrPtr == NULL) { + // AddrPtr is NULL, i.e. the address doesn't currently + // exist. Allocate memory for it, then try to add the + // address locally. + + CTEFreeLock(&NTE->nte_lock, Handle); + + // If this is not a class D address, fail the request. + if (!CLASSD_ADDR(Addr)) { + Status = IP_BAD_REQ; + break; + } + + AddrPtr = CTEAllocMem(sizeof(IGMPAddr)); + if (AddrPtr != NULL) { + + // Got memory. Try to add the address locally. + AddrAdded = (*IF->if_addaddr)(IF->if_lcontext, + LLIP_ADDR_MCAST, Addr, 0, NULL); + + // See if we added it succesfully. If we did, fill in + // the stucture and link it in. + + if (AddrAdded) { + + AddrPtr->iga_addr = Addr; + AddrPtr->iga_refcnt = 1; + AddrPtr->iga_timer = 0; + + CTEGetLock(&NTE->nte_lock, &Handle); + AddrPtr->iga_next = NTE->nte_igmplist; + NTE->nte_igmplist = AddrPtr; + CTEFreeLock(&NTE->nte_lock, Handle); + + if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST)) { + // This isn't the all host address, so send a + // report for it. + AddrPtr->iga_timer = IGMPRandomTicks(MAX_DELAY_TICKS); + SendIGMPReport(ChangeType, IgmpVersion, Addr, + NTE->nte_addr); + } + } else { + // Couldn't add the local address. Free the memory + // and fail the request. + CTEFreeMem(AddrPtr); + Status = IP_NO_RESOURCES; + } + + } else { + Status = IP_NO_RESOURCES; + } + } else { + // Already have this one. Bump his count. + (AddrPtr->iga_refcnt)++; + CTEFreeLock(&NTE->nte_lock, Handle); + } + break; + case IGMP_DELETE: + + // This is a delete request. If we didn't find the requested + // address, fail the request. Otherwise dec his refcnt, and if + // it goes to 0 delete the address locally. + if (AddrPtr != NULL) { + // Have one. We won't let the all-hosts mcast address go + // away, but for other's we'll check to see if it's time + // to delete them. + if (!IP_ADDR_EQUAL(Addr, ALL_HOST_MCAST) && + --(AddrPtr->iga_refcnt) == 0) { + // This one is to be deleted. Pull him from the + // list, and call the lower interface to delete him. + PrevPtr->iga_next = AddrPtr->iga_next; + CTEFreeLock(&NTE->nte_lock, Handle); + + CTEFreeMem(AddrPtr); + (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST, + Addr, 0); + // + // Send a report to indicate that we are leaving the + // group + // + +#ifdef IGMPV2 + SendIGMPReport(ChangeType, IgmpVersion, Addr, + NTE->nte_addr); +#endif + } else + CTEFreeLock(&NTE->nte_lock, Handle); + } else { + CTEFreeLock(&NTE->nte_lock, Handle); + Status = IP_BAD_REQ; + } + break; + case IGMP_DELETE_ALL: + // We've been called to delete all of this addresses, + // regardless of their reference count. This should only + // happen when the NTE is going away. + AddrPtr = NTE->nte_igmplist; + NTE->nte_igmplist = NULL; + CTEFreeLock(&NTE->nte_lock, Handle); + + // Walk down the list, deleteing each one. + while (AddrPtr != NULL) { + (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_MCAST, + AddrPtr->iga_addr, 0); +#ifdef IGMPV2 + if (!IP_ADDR_EQUAL(AddrPtr->iga_addr, ALL_HOST_MCAST)) { + SendIGMPReport(IGMP_DELETE, IgmpVersion, AddrPtr->iga_addr, NTE->nte_addr); + } +#endif + PrevPtr = AddrPtr; + AddrPtr = AddrPtr->iga_next; + CTEFreeMem(PrevPtr); + } + + // All done. + break; + default: + DEBUGCHK; + break; + } + } else { + // NTE isn't valid. + CTEFreeLock(&NTE->nte_lock, Handle); + Status = IP_BAD_REQ; + } + + + // We finished the request, and Status contains the completion status. + // If there are any pending blocks for this routine, signal the next + // one now. Otherwise clear the block flag. + CTEGetLock(&IGMPLock, &Handle); + if ((BlockPtr = IGMPBlockList) != NULL) { + // Someone is blocking. Pull him from the list and signal him. + IGMPBlockList = BlockPtr->ibs_next; + CTEFreeLock(&IGMPLock, Handle); + + CTESignal(&BlockPtr->ibs_block, IP_SUCCESS); + } else { + // No one blocking, just clear the flag. + IGMPBlockFlag = 0; + CTEFreeLock(&IGMPLock, Handle); + } + + return Status; + +} + +//* IGMPTimer - Handle an IGMP timer event. +// +// This function is called every 500 ms. by IP. If we're at level 2 of +// IGMP functionality we run down the NTE looking for running timers. If +// we find one, we see if it has expired and if so we send an +// IGMP report. +// +// Input: NTE - Pointer to NTE to check. +// +// Returns: Nothing. +// +void +IGMPTimer(NetTableEntry *NTE) +{ + CTELockHandle Handle; + IGMPAddr *AddrPtr, *PrevPtr; + uint IgmpVersion; + + if (IGMPLevel == 2) { + // We are doing IGMP. Run down the addresses active on this NTE. + CTEGetLock(&NTE->nte_lock, &Handle); + + // + // if we haven't heard any query or report from an igmpv1 router or + // host during timeout period, revert to igmpv2. No need to check + // whether NTE is valid or not + // +#ifdef IGMPV2 + if ((NTE->nte_if->IgmpVer1Timeout != 0) && (--(NTE->nte_if->IgmpVer1Timeout) == 0)) { + NTE->nte_if->IgmpVersion = IGMPV2; + } +#endif + PrevPtr = STRUCT_OF(IGMPAddr, &NTE->nte_igmplist, iga_next); + AddrPtr = PrevPtr->iga_next; + while (AddrPtr != NULL) { + // We have one. See if it's running. + if (AddrPtr->iga_timer != 0) { + // It's running. See if it's expired. + if (--(AddrPtr->iga_timer) == 0 && NTE->nte_flags & NTE_VALID) { + // It's expired. Increment the ref count so it + // doesn't go away while we're here, and send a report. + AddrPtr->iga_refcnt++; +#ifdef IGMPV2 + IgmpVersion = NTE->nte_if->IgmpVersion; +#else + IgmpVersion = IGMPV1; +#endif + + CTEFreeLock(&NTE->nte_lock, Handle); + + SendIGMPReport(IGMP_ADD, IgmpVersion, AddrPtr->iga_addr, + NTE->nte_addr); + // Now get the lock, and decrement the refcnt. If it goes + // to 0, it's been deleted so we need to free it. + CTEGetLock(&NTE->nte_lock, &Handle); + if (--(AddrPtr->iga_refcnt) == 0) { + // It's been deleted. + PrevPtr->iga_next = AddrPtr->iga_next; + CTEFreeMem(AddrPtr); + AddrPtr = PrevPtr->iga_next; + continue; + } + } + } + // Either the timer isn't running or hasn't fired. Try the next + // one. + PrevPtr = AddrPtr; + AddrPtr = AddrPtr->iga_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + } + +} + +//* InitIGMPForNTE - Called to do per-NTE initialization. +// +// Called when an NTE becomes valid. If we're at level 2, we put the all-host +// mcast on the list and add the address to the interface. +// +// Input: NTE - NTE on which to act. +// +// Returns: Nothing. +// +void +InitIGMPForNTE(NetTableEntry *NTE) +{ + if (IGMPLevel == 2) { + IGMPAddrChange(NTE, ALL_HOST_MCAST, IGMP_ADD); + if (NTE->nte_rtrdiscovery && (NTE->nte_rtrdiscaddr == ALL_ROUTER_MCAST)) { + IGMPAddrChange(NTE, ALL_ROUTER_MCAST, IGMP_ADD); + } + } + if (Seed == 0) { + // No random seed yet. + Seed = (int)NTE->nte_addr; + + // Make sure the inital value is odd, and less than 9 decimal digits. + RandomValue = ((Seed + (int)CTESystemUpTime()) % 100000000) | 1; + } +} + +//* StopIGMPForNTE - Called to do per-NTE shutdown. +// +// Called when we're shutting down and NTE, and want to stop IGMP on hi, +// +// Input: NTE - NTE on which to act. +// +// Returns: Nothing. +// +void +StopIGMPForNTE(NetTableEntry *NTE) +{ + if (IGMPLevel == 2) { + IGMPAddrChange(NTE, NULL_IP_ADDR, IGMP_DELETE_ALL); + } +} + +#pragma BEGIN_INIT + +//** IGMPInit - Initialize IGMP. +// +// This bit of code initializes IGMP generally. There is also some amount +// of work done on a per-NTE basis that we do when each one is initialized. +// +// Input: Nothing. +/// +// Returns: TRUE if we init, FALSE if we don't. +// +uint +IGMPInit(void) +{ + + if (IGMPLevel != 2) + return TRUE; + + CTEInitLock(&IGMPLock); + IGMPBlockList = NULL; + IGMPBlockFlag = 0; + Seed = 0; + + // We fake things a little bit. We register our receive handler, but + // since we steal buffers from ICMP we register the ICMP send complete + // handler. + IGMPProtInfo = IPRegisterProtocol(PROT_IGMP, IGMPRcv, ICMPSendComplete, + NULL, NULL); + + if (IGMPProtInfo != NULL) + return TRUE; + else + return FALSE; + +} + +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/ip/igmp.h b/private/ntos/tdi/tcpip/ip/igmp.h new file mode 100644 index 000000000..ea7aefce5 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/igmp.h @@ -0,0 +1,42 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IGMP.H - IP multicast definitions. +// +// This file contains definitions related to IP multicast. + +#define PROT_IGMP 2 + +extern uint IGMPLevel; + +// Structure used for local mcast address tracking. +typedef struct IGMPAddr { + struct IGMPAddr *iga_next; + IPAddr iga_addr; + uint iga_refcnt; + uint iga_timer; +} IGMPAddr; + +#define IGMP_ADD 0 +#define IGMP_DELETE 1 +#define IGMP_DELETE_ALL 2 + +#define IGMPV1 2 //IGMP version 1 +#define IGMPV2 3 //IGMP version 2 + +// +// disable for 4.0 sp2 +// +#undef IGMPV2 + + +extern void InitIGMPForNTE(NetTableEntry *NTE); +extern void StopIGMPForNTE(NetTableEntry *NTE); +extern IP_STATUS IGMPAddrChange(NetTableEntry *NTE, IPAddr Addr, + uint ChangeType); +extern void IGMPTimer(NetTableEntry *NTE); + + diff --git a/private/ntos/tdi/tcpip/ip/info.c b/private/ntos/tdi/tcpip/ip/info.c new file mode 100644 index 000000000..a866c0cba --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/info.c @@ -0,0 +1,609 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1993 **/ +/********************************************************************/ +/* :ts=4 */ + +//** INFO.C - Routines for querying and setting IP information. +// +// This file contains the code for dealing with Query/Set information +// calls. +// + +#include "oscfg.h" +#include "ndis.h" +#include "cxport.h" +#include "ip.h" +#include "ipdef.h" +#include "info.h" +#include "tdi.h" +#include "tdiinfo.h" +#include "llinfo.h" +#include "tdistat.h" +#include "iproute.h" +#include "igmp.h" +#include "ipfilter.h" +#include "iprtdef.h" + +extern Interface *IFList; +extern NetTableEntry *NetTableList; +extern uint LoopIndex; // Index of loopback I/F. +extern uint DefaultTTL; +extern uint NumIF; +extern uint NumNTE; +extern RouteInterface DummyInterface; // Dummy interface. + +EXTERNAL_LOCK(RouteTableLock) + +extern uint RTEReadNext(void *Context, void *Buffer); +extern uint RTValidateContext(void *Context, uint *Valid); +extern uint RTReadNext(void *Context, void *Buffer); + +uint IPInstance; +uint ICMPInstance; + +//* CopyToNdis - Copy a flat buffer to an NDIS_BUFFER chain. +// +// A utility function to copy a flat buffer to an NDIS buffer chain. We +// assume that the NDIS_BUFFER chain is big enough to hold the copy amount; +// in a debug build we'll debugcheck if this isn't true. We return a pointer +// to the buffer where we stopped copying, and an offset into that buffer. +// This is useful for copying in pieces into the chain. +// +// Input: DestBuf - Destination NDIS_BUFFER chain. +// SrcBuf - Src flat buffer. +// Size - Size in bytes to copy. +// StartOffset - Pointer to start of offset into first buffer in +// chain. Filled in on return with the offset to +// copy into next. +// +// Returns: Pointer to next buffer in chain to copy into. +// +PNDIS_BUFFER +CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset) +{ + uint CopySize; + uchar *DestPtr; + uint DestSize; + uint Offset = *StartOffset; + uchar *VirtualAddress; + uint Length; + + CTEAssert(DestBuf != NULL); + CTEAssert(SrcBuf != NULL); + + NdisQueryBuffer(DestBuf, &VirtualAddress, &Length); + CTEAssert(Length >= Offset); + DestPtr = VirtualAddress + Offset; + DestSize = Length - Offset; + + for (;;) { + CopySize = MIN(Size, DestSize); + CTEMemCopy(DestPtr, SrcBuf, CopySize); + + DestPtr += CopySize; + SrcBuf += CopySize; + + if ((Size -= CopySize) == 0) + break; + + if ((DestSize -= CopySize) == 0) { + DestBuf = NDIS_BUFFER_LINKAGE(DestBuf); + CTEAssert(DestBuf != NULL); + NdisQueryBuffer(DestBuf, &VirtualAddress, &Length); + DestPtr = VirtualAddress; + DestSize = Length; + } + } + + *StartOffset = DestPtr - VirtualAddress; + + return DestBuf; + +} + + +//* IPQueryInfo - IP query information handler. +// +// Called by the upper layer when it wants to query information about us. +// We take in an ID, a buffer and length, and a context value, and return +// whatever information we can. +// +// Input: ID - Pointer to ID structure. +// Buffer - Pointer to buffer chain. +// Size - Pointer to size in bytes of buffer. On return, filled +// in with bytes read. +// Context - Pointer to context value. +// +// Returns: TDI_STATUS of attempt to read information. +// +long +IPQueryInfo(TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, void *Context) +{ + uint BufferSize = *Size; + uint BytesCopied = 0; + uint Offset = 0; + TDI_STATUS Status; + ushort NTEContext; + uchar InfoBuff[sizeof(IPRouteEntry)]; + IPAddrEntry *AddrEntry; + NetTableEntry *CurrentNTE; + uint Valid, DataLeft; + CTELockHandle Handle; + Interface *LowerIF; + IPInterfaceInfo *IIIPtr; + uint LLID = 0; + uint Entity; + uint Instance; + IPAddr IFAddr; + + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // See if it's something we might handle. + + if (Entity != CL_NL_ENTITY && Entity != ER_ENTITY) { + // We need to pass this down to the lower layer. Loop through until + // we find one that takes it. If noone does, error out. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_qinfo)(LowerIF->if_lcontext, ID, Buffer, + Size, Context); + if (Status != TDI_INVALID_REQUEST) + return Status; + } + // If we get here, noone took it. Return an error. + return TDI_INVALID_REQUEST; + + } + + if ((Entity == CL_NL_ENTITY && Instance != IPInstance) || + Instance != ICMPInstance) + return TDI_INVALID_REQUEST; + + // The request is for us. + *Size = 0; // Set to 0 in case of an error. + + // Make sure it's something we support. + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_type == INFO_TYPE_PROVIDER && ID->toi_id == ENTITY_TYPE_ID) { + // He's trying to see what type we are. + if (BufferSize >= sizeof(uint)) { + *(uint *)&InfoBuff[0] = (Entity == CL_NL_ENTITY) ? CL_NL_IP : + ER_ICMP; + (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } else + if (ID->toi_class != INFO_CLASS_PROTOCOL || + ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + // If it's ICMP, just copy the statistics. + if (Entity == ER_ENTITY) { + + // It is ICMP. Make sure the ID is valid. + if (ID->toi_id != ICMP_MIB_STATS_ID) + return TDI_INVALID_PARAMETER; + + // He wants the stats. Copy what we can. + if (BufferSize < sizeof(ICMPSNMPInfo)) + return TDI_BUFFER_TOO_SMALL; + + Buffer = CopyToNdis(Buffer, (uchar *)&ICMPInStats, sizeof(ICMPStats), + &Offset); + (void)CopyToNdis(Buffer, (uchar *)&ICMPOutStats, sizeof(ICMPStats), + &Offset); + + *Size = sizeof(ICMPSNMPInfo); + return TDI_SUCCESS; + } + + // It's not ICMP. We need to figure out what it is, and take the + // appropriate action. + + switch (ID->toi_id) { + + case IP_MIB_STATS_ID: + if (BufferSize < sizeof(IPSNMPInfo)) + return TDI_BUFFER_TOO_SMALL; + IPSInfo.ipsi_numif = NumIF; + IPSInfo.ipsi_numaddr = NumNTE; + IPSInfo.ipsi_defaultttl = DefaultTTL; + IPSInfo.ipsi_forwarding = ForwardPackets ? IP_FORWARDING : + IP_NOT_FORWARDING; + CopyToNdis(Buffer, (uchar *)&IPSInfo, sizeof(IPSNMPInfo), &Offset); + BytesCopied = sizeof(IPSNMPInfo); + Status = TDI_SUCCESS; + break; + case IP_MIB_ADDRTABLE_ENTRY_ID: + // He wants to read the address table. Figure out where we're + // starting from, and if it's valid begin copying from there. + NTEContext = *(ushort *)Context; + CurrentNTE = NetTableList; + + if (NTEContext != 0) { + for (;CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) + if (CurrentNTE->nte_context == NTEContext) + break; + if (CurrentNTE == NULL) + return TDI_INVALID_PARAMETER; + } + + AddrEntry = (IPAddrEntry *)InfoBuff; + for (; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { + if ((int)(BufferSize - BytesCopied) >= (int)sizeof(IPAddrEntry)) { + // We have room to copy it. Build the entry, and copy + // it. + if (CurrentNTE->nte_flags & NTE_ACTIVE) { + if (CurrentNTE->nte_flags & NTE_VALID) { + AddrEntry->iae_addr = CurrentNTE->nte_addr; + AddrEntry->iae_mask = CurrentNTE->nte_mask; + } else { + AddrEntry->iae_addr = NULL_IP_ADDR; + AddrEntry->iae_mask = NULL_IP_ADDR; + } + + AddrEntry->iae_index = CurrentNTE->nte_if->if_index; + AddrEntry->iae_bcastaddr = + *(int *)&(CurrentNTE->nte_if->if_bcast) & 1; + AddrEntry->iae_reasmsize = 0xffff; + AddrEntry->iae_context = CurrentNTE->nte_context; + Buffer = CopyToNdis(Buffer, (uchar *)AddrEntry, + sizeof(IPAddrEntry), &Offset); + BytesCopied += sizeof(IPAddrEntry); + } + } else + break; + } + + if (CurrentNTE == NULL) + Status = TDI_SUCCESS; + else { + Status = TDI_BUFFER_OVERFLOW; + **(ushort **)&Context = CurrentNTE->nte_context; + } + + break; + case IP_MIB_RTTABLE_ENTRY_ID: + // Make sure we have a valid context. + CTEGetLock(&RouteTableLock, &Handle); + DataLeft = RTValidateContext(Context, &Valid); + + // If the context is valid, we'll continue trying to read. + if (!Valid) { + CTEFreeLock(&RouteTableLock, 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(IPRouteEntry)) { + DataLeft = RTReadNext(Context, InfoBuff); + BytesCopied += sizeof(IPRouteEntry); + Buffer = CopyToNdis(Buffer, InfoBuff, sizeof(IPRouteEntry), + &Offset); + } else + break; + + } + + CTEFreeLock(&RouteTableLock, Handle); + Status = (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW); + break; + case IP_INTFC_INFO_ID: + + IFAddr = *(IPAddr *)Context; + // Loop through the NTE table, looking for a match. + for (CurrentNTE = NetTableList; CurrentNTE != NULL; CurrentNTE = CurrentNTE->nte_next) { + if ((CurrentNTE->nte_flags & NTE_VALID) && + IP_ADDR_EQUAL(CurrentNTE->nte_addr, IFAddr)) + break; + } + if (CurrentNTE == NULL) { + Status = TDI_INVALID_PARAMETER; + break; + } + + if (BufferSize < offsetof(IPInterfaceInfo, iii_addr)) { + Status = TDI_BUFFER_TOO_SMALL; + break; + } + + // We have the NTE. Get the interface, fill in a structure, + // and we're done. + LowerIF = CurrentNTE->nte_if; + IIIPtr = (IPInterfaceInfo *)InfoBuff; + IIIPtr->iii_flags = LowerIF->if_flags & IF_FLAGS_P2P ? + IP_INTFC_FLAG_P2P : 0; + IIIPtr->iii_mtu = LowerIF->if_mtu; + IIIPtr->iii_speed = LowerIF->if_speed; + IIIPtr->iii_addrlength = LowerIF->if_addrlen; + BytesCopied = offsetof(IPInterfaceInfo, iii_addr); + if (BufferSize >= (offsetof(IPInterfaceInfo, iii_addr) + + LowerIF->if_addrlen)) { + Status = TDI_SUCCESS; + Buffer = CopyToNdis(Buffer, InfoBuff, + offsetof(IPInterfaceInfo, iii_addr), &Offset); + CopyToNdis(Buffer, LowerIF->if_addr, LowerIF->if_addrlen, + &Offset); + BytesCopied += LowerIF->if_addrlen; + } else { + Status = TDI_BUFFER_TOO_SMALL; + } + break; + + default: + return TDI_INVALID_PARAMETER; + break; + } + + *Size = BytesCopied; + return Status; +} + +//* IPSetInfo - IP set information handler. +// +// Called by the upper layer when it wants to set an object, which could +// be a route table entry, an ARP table entry, or something else. +// +// Input: ID - Pointer to ID structure. +// Buffer - Pointer to buffer containing element to set.. +// Size - Pointer to size in bytes of buffer. +// +// Returns: TDI_STATUS of attempt to read information. +// +long +IPSetInfo(TDIObjectID *ID, void *Buffer, uint Size) +{ + uint Entity; + uint Instance; + Interface *LowerIF; + Interface *OutIF; + uint MTU; + IPRouteEntry *IRE; + NetTableEntry *OutNTE, *LocalNTE; + IP_STATUS Status; + IPAddr FirstHop, Dest, NextHop; + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // If it's not for us, pass it down. + if (Entity != CL_NL_ENTITY) { + // We need to pass this down to the lower layer. Loop through until + // we find one that takes it. If noone does, error out. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_setinfo)(LowerIF->if_lcontext, ID, Buffer, + Size); + if (Status != TDI_INVALID_REQUEST) + return Status; + } + // If we get here, noone took it. Return an error. + return TDI_INVALID_REQUEST; + } + + if (Instance != IPInstance) + return TDI_INVALID_REQUEST; + + // We're identified as the entity. Make sure the ID is correct. + if (ID->toi_id == IP_MIB_RTTABLE_ENTRY_ID) { + NetTableEntry *TempNTE; + + // This is an attempt to set a route table entry. Make sure the + // size if correct. + if (Size < sizeof(IPRouteEntry)) + return TDI_INVALID_PARAMETER; + + IRE = (IPRouteEntry *)Buffer; + + OutNTE = NULL; + LocalNTE = NULL; + + Dest = IRE->ire_dest; + NextHop = IRE->ire_nexthop; + + // Make sure that the nexthop is sensible. We don't allow nexthops + // to be broadcast or invalid or loopback addresses. + if (IP_ADDR_EQUAL(NextHop, NULL_IP_ADDR) || IP_LOOPBACK(NextHop) || + CLASSD_ADDR(NextHop) || CLASSE_ADDR(NextHop)) + return TDI_INVALID_PARAMETER; + + // Also make sure that the destination we're routing to is sensible. + // Don't allow routes to be added to Class D or E or loopback + // addresses. + if (IP_LOOPBACK(Dest) || CLASSD_ADDR(Dest) || CLASSE_ADDR(Dest)) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_index == LoopIndex) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_index != INVALID_IF_INDEX) { + + // First thing to do is to find the outgoing NTE for specified + // interface, and also make sure that it matches the destination + // if the destination is one of my addresses. + for (TempNTE = NetTableList; TempNTE != NULL; + TempNTE = TempNTE->nte_next) { + if (OutNTE == NULL && IRE->ire_index == TempNTE->nte_if->if_index) + OutNTE = TempNTE; + if (IP_ADDR_EQUAL(NextHop, TempNTE->nte_addr) && + (TempNTE->nte_flags & NTE_VALID)) + LocalNTE = TempNTE; + + // Don't let a route be set through a broadcast address. + if (IsBCastOnNTE(NextHop, TempNTE) != DEST_LOCAL) + return TDI_INVALID_PARAMETER; + + // Don't let a route to a broadcast address be added or deleted. + if (IsBCastOnNTE(Dest, TempNTE) != DEST_LOCAL) + return TDI_INVALID_PARAMETER; + } + + // At this point OutNTE points to the outgoing NTE, and LocalNTE + // points to the NTE for the local address, if this is a direct route. + // Make sure they point to the same interface, and that the type is + // reasonable. + if (OutNTE == NULL) + return TDI_INVALID_PARAMETER; + + if (LocalNTE != NULL) { + // He's routing straight out a local interface. The interface for + // the local address must match the interface passed in, and the + // type must be DIRECT (if we're adding) or INVALID (if we're + // deleting). + if (LocalNTE->nte_if->if_index != IRE->ire_index) + return TDI_INVALID_PARAMETER; + + if (IRE->ire_type != IRE_TYPE_DIRECT && + IRE->ire_type != IRE_TYPE_INVALID) + return TDI_INVALID_PARAMETER; + + OutNTE = LocalNTE; + } + + + // Figure out what the first hop should be. If he's routing straight + // through a local interface, or the next hop is equal to the + // destination, then the first hop is IPADDR_LOCAL. Otherwise it's the + // address of the gateway. + if (LocalNTE != NULL) + FirstHop = IPADDR_LOCAL; + else + if (IP_ADDR_EQUAL(Dest, NextHop)) + FirstHop = IPADDR_LOCAL; + else + FirstHop = NextHop; + + MTU = OutNTE->nte_mss; + OutIF = OutNTE->nte_if; + + } else { + OutIF = (Interface *)&DummyInterface; + MTU = DummyInterface.ri_if.if_mtu - sizeof(IPHeader); + if (IP_ADDR_EQUAL(Dest, NextHop)) + FirstHop = IPADDR_LOCAL; + else + FirstHop = NextHop; + } + + // We've done the validation. See if he's adding or deleting a route. + if (IRE->ire_type != IRE_TYPE_INVALID) { + // He's adding a route. + Status = AddRoute(Dest, IRE->ire_mask, FirstHop, OutIF, + MTU, IRE->ire_metric1, IRE->ire_proto, + ATYPE_OVERRIDE, IRE->ire_context); + + } else { + // He's deleting a route. + Status = DeleteRoute(Dest, IRE->ire_mask, FirstHop, OutIF); + } + + if (Status == IP_SUCCESS) + return TDI_SUCCESS; + else + if (Status == IP_NO_RESOURCES) + return TDI_NO_RESOURCES; + else + return TDI_INVALID_PARAMETER; + + } else { + if (ID->toi_id == IP_MIB_STATS_ID) { + IPSNMPInfo *Info = (IPSNMPInfo *)Buffer; + + // Setting information about TTL and/or routing. + if (Info->ipsi_defaultttl > 255 || (!RouterConfigured && + Info->ipsi_forwarding == IP_FORWARDING)) { + return TDI_INVALID_PARAMETER; + } + + DefaultTTL = Info->ipsi_defaultttl; + ForwardPackets = Info->ipsi_forwarding == IP_FORWARDING ? TRUE : + FALSE; + + return TDI_SUCCESS; + } + return TDI_INVALID_PARAMETER; + } + +} +#ifndef CHICAGO +#pragma BEGIN_INIT +#endif + +//* IPGetEList - 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. +// +long +IPGetEList(TDIEntityID *EList, uint *Count) +{ + uint ECount; + uint MyIPBase; + uint MyERBase; + int Status; + uint i; + Interface *LowerIF; + TDIEntityID *EntityList; + + ECount = *Count; + EntityList = EList; + + // Walk down the list, looking for existing CL_NL or ER entities, and + // adjust our base instance accordingly. + + MyIPBase = 0; + MyERBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == CL_NL_ENTITY) + MyIPBase = MAX(MyIPBase, EntityList->tei_instance + 1); + else + if (EntityList->tei_entity == ER_ENTITY) + MyERBase = MAX(MyERBase, EntityList->tei_instance + 1); + } + + // At this point we've figure out our base instance. Save for later use. + IPInstance = MyIPBase; + ICMPInstance = MyERBase; + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. We need one for the ICMP instance, + // and one for the CL_NL instance. + + if ((ECount + 2) > MAX_TDI_ENTITIES) + return TDI_REQ_ABORTED; + + // Now fill it in. + EntityList->tei_entity = CL_NL_ENTITY; + EntityList->tei_instance = IPInstance; + EntityList++; + EntityList->tei_entity = ER_ENTITY; + EntityList->tei_instance = ICMPInstance; + *Count += 2; + + // Loop through the interfaces, querying each of them. + for (LowerIF = IFList; LowerIF != NULL; LowerIF = LowerIF->if_next) { + Status = (*LowerIF->if_getelist)(LowerIF->if_lcontext, EList, Count); + if (!Status) + return TDI_BUFFER_TOO_SMALL; + } + + return TDI_SUCCESS; +} + +#ifndef CHICAGO +#pragma END_INIT +#endif diff --git a/private/ntos/tdi/tcpip/ip/info.h b/private/ntos/tdi/tcpip/ip/info.h new file mode 100644 index 000000000..e8de293e0 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/info.h @@ -0,0 +1,22 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +#include "ipinfo.h" + +extern IPSNMPInfo IPSInfo; +extern ICMPStats ICMPInStats; +extern ICMPStats ICMPOutStats; + +typedef struct RouteEntryContext { + uint rec_index; + struct RouteTableEntry *rec_rte; +} RouteEntryContext; + +extern long IPQueryInfo(struct TDIObjectID *ID, PNDIS_BUFFER Buffer, + uint *Size, void *Context); +extern long IPSetInfo(struct TDIObjectID *ID, void *Buffer, uint Size); +extern long IPGetEList(struct TDIEntityID *Buffer, uint *Count); + diff --git a/private/ntos/tdi/tcpip/ip/init.c b/private/ntos/tdi/tcpip/ip/init.c new file mode 100644 index 000000000..3f032a50a --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/init.c @@ -0,0 +1,3509 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** Init.c - IP VxD init routines. +// +// All C init routines are located in this file. We get +// config. information, allocate structures, and generally get things going. + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "ipinit.h" +#include "llipif.h" +#include "arp.h" +#include "info.h" +#include "iproute.h" +#include "iprtdef.h" +#include "ipxmit.h" +#include "igmp.h" +#include "icmp.h" +#include + +#ifdef NT +#include +#include +#endif + + +#define NUM_IP_NONHDR_BUFFERS 50 + +#define DEFAULT_RA_TIMEOUT 60 + +#define DEFAULT_ICMP_BUFFERS 5 + +extern IPConfigInfo *IPGetConfig(void); +extern void IPFreeConfig(IPConfigInfo *); +extern int IsIPBCast(IPAddr, uchar); + +extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle); +extern void CloseIFConfig(NDIS_HANDLE Handle); + +// The IPRcv routine. +extern void IPRcv(void *, void *, uint , uint , NDIS_HANDLE , uint , uint ); +// The transmit complete routine. +extern void IPSendComplete(void *, PNDIS_PACKET , NDIS_STATUS ); +// Status indication routine. +extern void IPStatus(void *, NDIS_STATUS, void *, uint); +// Transfer data complete routine. +extern void IPTDComplete(void *, PNDIS_PACKET , NDIS_STATUS , uint ); + +extern void IPRcvComplete(void); + +extern void ICMPInit(uint); +extern uint IGMPInit(void); +extern void ICMPTimer(NetTableEntry *); +extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong); +extern void TDUserRcv(void *, PNDIS_PACKET, NDIS_STATUS, uint); +extern void FreeRH(ReassemblyHeader *); +extern PNDIS_PACKET GrowIPPacketList(void); +extern PNDIS_BUFFER FreeIPPacket(PNDIS_PACKET Packet); + +extern ulong GetGMTDelta(void); +extern ulong GetTime(void); +extern ulong GetUnique32BitValue(void); + +extern void NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, + ushort IPContext, PVOID *Handle, PNDIS_STRING ConfigName, uint Added); + +uint IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn); + +extern NDIS_HANDLE BufferPool; +EXTERNAL_LOCK(HeaderLock) +#ifdef NT +extern SLIST_HEADER PacketList; +extern SLIST_HEADER HdrBufList; +#endif + +extern NetTableEntry *LoopNTE; + +extern uchar RouterConfigured; + +//NetTableEntry *NetTable; // Pointer to the net table. + +NetTableEntry *NetTableList; // List of NTEs. +int NumNTE; // Number of NTEs. + +AddrTypeCache ATCache[ATC_SIZE]; +uint ATCIndex; + +uchar RATimeout; // Number of seconds to time out a + // reassembly. +ushort NextNTEContext; // Next NTE context to use. + +#if 0 +DEFINE_LOCK_STRUCTURE(PILock) +#endif + +ProtInfo IPProtInfo[MAX_IP_PROT]; // Protocol information table. +ProtInfo *LastPI; // Last protinfo structure looked at. +int NextPI; // Next PI field to be used. +ProtInfo *RawPI = NULL; // Raw IP protinfo + +ulong TimeStamp; +ulong TSFlag; + +uint DefaultTTL; +uint DefaultTOS; +uchar TrRii = TR_RII_ALL; + +// Interface *IFTable[MAX_IP_NETS]; +Interface *IFList; // List of interfaces active. +Interface *FirstIF; // First 'real' IF. +ulong NumIF; + +#ifdef _PNP_POWER +#define BITS_PER_WORD 32 +ulong IFBitMask[(MAX_TDI_ENTITIES / BITS_PER_WORD) + 1]; +#endif // _PNP_POWER + +IPSNMPInfo IPSInfo; +uint DHCPActivityCount = 0; +uint IGMPLevel; + +#ifdef NT + +#ifndef _PNP_POWER + +extern NameMapping *AdptNameTable; +extern DriverRegMapping *DriverNameTable; + +#endif // _PNP_POWER + +VOID +SetPersistentRoutesForNTE( + IPAddr Address, + IPMask Mask, + ULONG IFIndex + ); + +#else // NT + +#ifndef _PNP_POWER + +extern NameMapping AdptNameTable[]; +extern DriverRegMapping DriverNameTable[]; + +#endif // _PNP_POWER + +#endif // NT + +#ifndef _PNP_POWER +extern uint NumRegDrivers; +uint MaxIPNets = 0; +#endif // _PNP_POWER + +uint InterfaceSize; // Size of a net interface. +NetTableEntry *DHCPNTE = NULL; + + +#ifdef NT + +#ifdef ALLOC_PRAGMA +// +// Make init code disposable. +// +void InitTimestamp(); +int InitNTE(NetTableEntry *NTE); +int InitInterface(NetTableEntry *NTE); +LLIPRegRtn GetLLRegPtr(PNDIS_STRING Name); +LLIPRegRtn FindRegPtr(PNDIS_STRING Name); +uint IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr); +void CleanAdaptTable(); +void OpenAdapters(); +int IPInit(); + +#if 0 // BUGBUG: These can eventually be made init time only. + +#pragma alloc_text(INIT, IPGetInfo) +#pragma alloc_text(INIT, IPTimeout) + +#endif // 0 + +#pragma alloc_text(INIT, InitTimestamp) +#ifndef _PNP_POWER +#pragma alloc_text(INIT, InitNTE) +#pragma alloc_text(INIT, InitInterface) +#endif +#pragma alloc_text(INIT, CleanAdaptTable) +#pragma alloc_text(INIT, OpenAdapters) +#pragma alloc_text(INIT, IPRegisterDriver) +#pragma alloc_text(INIT, GetLLRegPtr) +#pragma alloc_text(INIT, FindRegPtr) +#pragma alloc_text(INIT, IPInit) + + +// +// Pagable code +// +uint +IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask, + ushort *NTEContext, ulong *NTEInstance); + +#pragma alloc_text(PAGE, IPAddDynamicNTE) + +#endif // ALLOC_PRAGMA + +extern PDRIVER_OBJECT IPDriverObject; + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +// +// Debugging macros +// +#if DBG + +#define TCPTRACE(many_args) DbgPrint many_args + +#else // DBG + +#define TCPTRACE(many_args) + +#endif // DBG + + +// SetIFContext - Set the context on a particular interface. +// +// A routine to set the filter context on a particular interface. +// +// Input: Index - Interface index of i/f to be set. +// Context - Context to set. +// +// Returns: Status of attempt. +// +IP_STATUS +SetIFContext(uint Index, INTERFACE_CONTEXT *Context) +{ + Interface *IF; + + // Walk the list, looking for a matching index. + for (IF = IFList; IF != NULL; IF = IF->if_next) { + if (IF->if_index == Index) { + IF->if_filtercontext = Context; + break; + } + } + + // If we found one, return success. Otherwise fail. + if (IF != NULL) { + return IP_SUCCESS; + } else { + return IP_GENERAL_FAILURE; + } +} + +// SetFilterPtr - A routine to set the filter pointer. +// +// This routine sets the IP forwarding filter callout. +// +// Input: FilterPtr - Pointer to routine to call when filtering. May +// be NULL. +// +// Returns: IP_SUCCESS. +// +IP_STATUS +SetFilterPtr(IPPacketFilterPtr FilterPtr) +{ + Interface *IF; + + // + // If the pointer is being set to NULL, means filtering is + // being turned off. Remove all the contexts we have + // + + if(FilterPtr == NULL) + { + + for (IF = IFList; IF != NULL; IF = IF->if_next) + { + IF->if_filtercontext = NULL; + } + } + + ForwardFilterPtr = FilterPtr; + + return IP_SUCCESS; +} + +// SetMapRoutePtr - A routine to set the dial on demand callout pointer. +// +// This routine sets the IP dial on demand callout. +// +// Input: MapRoutePtr - Pointer to routine to call when we need to bring +// up a link. May be NULL +// +// Returns: IP_SUCCESS. +// +IP_STATUS +SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr) +{ + DODCallout = MapRoutePtr; + return IP_SUCCESS; +} + +#endif // NT + + +//** SetDHCPNTE +// +// Routine to identify which NTE is currently being DHCP'ed. We take as input +// an nte_context. If the context is less than the max NTE context, we look +// for a matching NTE and if we find him we save a pointer. If we don't we +// fail. If the context > max NTE context we're disabling DHCPing, and +// we NULL out the save pointer. +// +// Input: Context - NTE context value. +// +// Returns: TRUE if we succeed, FALSE if we don't. +// +uint +SetDHCPNTE(uint Context) +{ + CTELockHandle Handle; + NetTableEntry *NTE; + ushort NTEContext; + uint RetCode; + + CTEGetLock(&RouteTableLock, &Handle); + + if (Context <= 0xffff) { + // We're setting the DHCP NTE. Look for one matching the context. + + NTEContext = (ushort)Context; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if (NTE != LoopNTE && NTE->nte_context == NTEContext) { + // Found one. Save it and break out. + DHCPNTE = NTE; + break; + } + } + + RetCode = (NTE != NULL); + } else { + // The context is invalid, so we're deleting the DHCP NTE. + DHCPNTE = NULL; + RetCode = TRUE; + } + + CTEFreeLock(&RouteTableLock, Handle); + + return RetCode; +} + + +//** SetDHCPNTE +// +// Routine for upper layers to call to check if the IPContext value passed +// up to a RcvHandler identifies an interface that is currently being +// DHCP'd. +// +// Input: Context - Pointer to an NTE +// +// Returns: TRUE if we succeed, FALSE if we don't. +// +uint +IsDHCPInterface(void *IPContext) +{ +// CTELockHandle Handle; + uint RetCode; + NetTableEntry *NTE = (NetTableEntry *) IPContext; + + +// CTEGetLock(&RouteTableLock, &Handle); + + if (DHCPNTE == NTE) { + RetCode = TRUE; + } + else { + RetCode = FALSE; + } + +// CTEFreeLock(&RouteTableLock, Handle); + + return(RetCode); +} + + +//** CloseNets - Close active nets. +// +// Called when we need to close some lower layer interfaces. +// +// Entry: Nothing +// +// Returns: Nothing +// +void +CloseNets(void) +{ + NetTableEntry *nt; + + for (nt = NetTableList; nt != NULL; nt = nt->nte_next) + (*nt->nte_if->if_close)(nt->nte_if->if_lcontext); // Call close routine for this net. +} + +//** IPRegisterProtocol - Register a protocol with IP. +// +// Called by upper layer software to register a protocol. The UL supplies +// pointers to receive routines and a protocol value to be used on xmits/receives. +// +// Entry: +// Protocol - Protocol value to be returned. +// RcvHandler - Receive handler to be called when frames for Protocol are received. +// XmitHandler - Xmit. complete handler to be called when frames from Protocol are completed. +// StatusHandler - Handler to be called when status indication is to be delivered. +// +// Returns: +// Pointer to ProtInfo, +// +void * +IPRegisterProtocol(uchar Protocol, void *RcvHandler, void *XmitHandler, + void *StatusHandler, void *RcvCmpltHandler) +{ + ProtInfo *PI = (ProtInfo *)NULL; + int i; + int Incr; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + + // First check to see if it's already registered. If it is just replace it. + for (i = 0; i < NextPI; i++) + if (IPProtInfo[i].pi_protocol == Protocol) { + PI = &IPProtInfo[i]; + Incr = 0; + break; + } + + if (PI == (ProtInfo *)NULL) { + if (NextPI >= MAX_IP_PROT) { +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return NULL; + } + + PI = &IPProtInfo[NextPI]; + Incr = 1; + + if (Protocol == PROTOCOL_ANY) { + RawPI = PI; + } + } + + PI->pi_protocol = Protocol; + PI->pi_rcv = RcvHandler; + PI->pi_xmitdone = XmitHandler; + PI->pi_status = StatusHandler; + PI->pi_rcvcmplt = RcvCmpltHandler; + NextPI += Incr; + +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + +#ifndef _PNP_POWER +#ifdef SECFLTR + + // + // If this was a registration, call the status routine of each protocol + // to inform it of all existing interfaces. Yes, this is a hack, but + // it will work until PnP is turned on in NT. + // + // It is assumed that none of the upper layer status routines call back + // into IP. + // + // Note that we don't hold any locks here since no one manipulates the + // NTE list in a non-PNP build during the init phase. + // + if (StatusHandler != NULL) { + NetTableEntry *NTE; + NDIS_HANDLE ConfigHandle = NULL; + int i; + + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ( !(IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) && + !(IP_LOOPBACK_ADDR(NTE->nte_addr)) + ) + { + // + // Open a configuration key + // + if (!OpenIFConfig(&(NTE->nte_if->if_configname), &ConfigHandle)) + { + // + // Not much we can do. The transports will have + // to handle this. + // + CTEAssert(ConfigHandle == NULL); + } + + (* ((ULStatusProc) StatusHandler))(IP_HW_STATUS, IP_ADDR_ADDED, + NTE->nte_addr, NULL_IP_ADDR, NULL_IP_ADDR, 0, ConfigHandle ); + + if (ConfigHandle != NULL) { + CloseIFConfig(ConfigHandle); + ConfigHandle = NULL; + } + } + } + } + +#endif // SECFLTR +#endif // _PNP_POWER + + return PI; +} + +//** IPSetMCastAddr - Set/Delete a multicast address. +// +// Called by an upper layer protocol or client to set or delete an IP multicast +// address. +// +// Input: Address - Address to be set/deleted. +// IF - IP Address of interface to set/delete on. +// Action - TRUE if we're setting, FALSE if we're deleting. +// +// Returns: IP_STATUS of set/delete attempt. +// +IP_STATUS +IPSetMCastAddr(IPAddr Address, IPAddr IF, uint Action) +{ + NetTableEntry *LocalNTE; + + // Don't let him do this on the loopback address, since we don't have a + // route table entry for class D address on the loopback interface and + // we don't want a packet with a loopback source address to show up on + // the wire. + if (IP_LOOPBACK_ADDR(IF)) + return IP_BAD_REQ; + + for (LocalNTE = NetTableList; LocalNTE != NULL; + LocalNTE = LocalNTE->nte_next) { + if (LocalNTE != LoopNTE && ((LocalNTE->nte_flags & NTE_VALID) && + (IP_ADDR_EQUAL(IF, NULL_IP_ADDR) || + IP_ADDR_EQUAL(IF, LocalNTE->nte_addr)))) + break; + } + + if (LocalNTE == NULL) { + // Couldn't find a matching NTE. + return IP_BAD_REQ; + } + + return IGMPAddrChange(LocalNTE, Address, Action ? IGMP_ADD : IGMP_DELETE); + + +} + +//** IPGetAddrType - Return the type of a address. +// +// Called by the upper layer to determine the type of a remote address. +// +// Input: Address - The address in question. +// +// Returns: The DEST type of the address. +// +uchar +IPGetAddrType(IPAddr Address) +{ + return GetAddrType(Address); +} + +//** IPGetLocalMTU - Return the MTU for a local address +// +// Called by the upper layer to get the local MTU for a local address. +// +// Input: LocalAddr - Local address in question. +// MTU - Where to return the local MTU. +// +// Returns: TRUE if we found the MTU, FALSE otherwise. +// +uchar +IPGetLocalMTU(IPAddr LocalAddr, ushort *MTU) +{ + NetTableEntry *NTE; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if (IP_ADDR_EQUAL(NTE->nte_addr, LocalAddr) && + (NTE->nte_flags & NTE_VALID)) { + *MTU = NTE->nte_mss; + return TRUE; + } + } + + // Special case in case the local address is a loopback address other than + // 127.0.0.1. + if (IP_LOOPBACK_ADDR(LocalAddr)) { + *MTU = LoopNTE->nte_mss; + return TRUE; + } + + return FALSE; + +} + +//** IPUpdateRcvdOptions - Update options for use in replying. +// +// A routine to update options for use in a reply. We reverse any source route options, +// and optionally update the record route option. We also return the index into the +// options of the record route options (if we find one). The options are assumed to be +// correct - no validation is performed on them. We fill in the caller provided +// IPOptInfo with the new option buffer. +// +// Input: Options - Pointer to option info structure with buffer to be reversed. +// NewOptions - Pointer to option info structure to be filled in. +// Src - Source address of datagram that generated the options. +// LocalAddr - Local address responding. If this != NULL_IP_ADDR, then +// record route and timestamp options will be updated with this +// address. +// +// +// Returns: Index into options of record route option, if any. +// +IP_STATUS +IPUpdateRcvdOptions(IPOptInfo *OldOptions, IPOptInfo *NewOptions, IPAddr Src, IPAddr LocalAddr) +{ + uchar Length, Ptr; + uchar i; // Index variable + IPAddr UNALIGNED *LastAddr; // First address in route. + IPAddr UNALIGNED *FirstAddr; // Last address in route. + IPAddr TempAddr; // Temp used in exchange. + uchar *Options, OptLength; + OptIndex Index; // Optindex used by UpdateOptions. + + Options = CTEAllocMem(OptLength = OldOptions->ioi_optlength); + + if (!Options) + return IP_NO_RESOURCES; + + CTEMemCopy(Options, OldOptions->ioi_options, OptLength); + Index.oi_srindex = MAX_OPT_SIZE; + Index.oi_rrindex = MAX_OPT_SIZE; + Index.oi_tsindex = MAX_OPT_SIZE; + + NewOptions->ioi_flags &= ~IP_FLAG_SSRR; + + i = 0; + while(i < OptLength) { + if (Options[i] == IP_OPT_EOL) + break; + + if (Options[i] == IP_OPT_NOP) { + i++; + continue; + } + + Length = Options[i+IP_OPT_LENGTH]; + switch (Options[i]) { + case IP_OPT_SSRR: + NewOptions->ioi_flags |= IP_FLAG_SSRR; + case IP_OPT_LSRR: + // Have a source route. We save the last gateway we came through as + // the new address, reverse the list, shift the list forward one address, + // and set the Src address as the last gateway in the list. + + // First, check for an empty source route. If the SR is empty + // we'll skip most of this. + if (Length != (MIN_RT_PTR - 1)) { + // A non empty source route. + // First reverse the list in place. + Ptr = Options[i+IP_OPT_PTR] - 1 - sizeof(IPAddr); + LastAddr = (IPAddr *)(&Options[i + Ptr]); + FirstAddr = (IPAddr *)(&Options[i + IP_OPT_PTR + 1]); + NewOptions->ioi_addr = *LastAddr; // Save Last address as + // first hop of new route. + while (LastAddr > FirstAddr) { + TempAddr = *LastAddr; + *LastAddr-- = *FirstAddr; + *FirstAddr++ = TempAddr; + } + + // Shift the list forward one address. We'll copy all but + // one IP address. + CTEMemCopy(&Options[i + IP_OPT_PTR + 1], + &Options[i + IP_OPT_PTR + 1 + sizeof(IPAddr)], + Length - (sizeof(IPAddr) + (MIN_RT_PTR -1))); + + // Set source as last address of route. + *(IPAddr UNALIGNED *)(&Options[i + Ptr]) = Src; + } + + Options[i+IP_OPT_PTR] = MIN_RT_PTR; // Set pointer to min legal value. + i += Length; + break; + case IP_OPT_RR: + // Save the index in case LocalAddr is specified. If it isn't specified, + // reset the pointer and zero the option. + Index.oi_rrindex = i; + if (LocalAddr == NULL_IP_ADDR) { + CTEMemSet(&Options[i+MIN_RT_PTR-1], 0, Length - (MIN_RT_PTR-1)); + Options[i+IP_OPT_PTR] = MIN_RT_PTR; + } + i += Length; + break; + case IP_OPT_TS: + Index.oi_tsindex = i; + + // We have a timestamp option. If we're not going to update, reinitialize + // it for next time. For the 'unspecified' options, just zero the buffer. + // For the 'specified' options, we need to zero the timestamps without + // zeroing the specified addresses. + if (LocalAddr == NULL_IP_ADDR) { // Not going to update, reinitialize. + uchar Flags; + + Options[i+IP_OPT_PTR] = MIN_TS_PTR; // Reinitialize pointer. + Flags = Options[i+IP_TS_OVFLAGS] & IP_TS_FLMASK; // Get option type. + Options[i+IP_TS_OVFLAGS] = Flags; // Clear overflow count. + switch (Flags) { + uchar j; + ulong UNALIGNED *TSPtr; + + // The unspecified types. Just clear the buffer. + case TS_REC_TS: + case TS_REC_ADDR: + CTEMemSet(&Options[i+MIN_TS_PTR-1], 0, Length - (MIN_TS_PTR-1)); + break; + + // We have a list of addresses specified. Just clear the timestamps. + case TS_REC_SPEC: + // j starts off as the offset in bytes from start of buffer to + // first timestamp. + j = MIN_TS_PTR-1+sizeof(IPAddr); + // TSPtr points at timestamp. + TSPtr = (ulong UNALIGNED *)&Options[i+j]; + + // Now j is offset of end of timestamp being zeroed. + j += sizeof(ulong); + while (j <= Length) { + *TSPtr++ = 0; + j += sizeof(ulong); + } + break; + default: + break; + } + } + i += Length; + break; + + default: + i += Length; + break; + } + + } + + if (LocalAddr != NULL_IP_ADDR) { + UpdateOptions(Options, &Index, LocalAddr); + } + + NewOptions->ioi_optlength = OptLength; + NewOptions->ioi_options = Options; + return IP_SUCCESS; + +} + +//* ValidRouteOption - Validate a source or record route option. +// +// Called to validate that a user provided source or record route option is good. +// +// Entry: Option - Pointer to option to be checked. +// NumAddr - NumAddr that need to fit in option. +// BufSize - Maximum size of option. +// +// Returns: 1 if option is good, 0 if not. +// +uchar +ValidRouteOption(uchar *Option, uint NumAddr, uint BufSize) +{ + if (Option[IP_OPT_LENGTH] < (3 + (sizeof(IPAddr)*NumAddr)) || + Option[IP_OPT_LENGTH] > BufSize || + ((Option[IP_OPT_LENGTH] - 3) % sizeof(IPAddr))) // Routing options is too small. + return 0; + + if (Option[IP_OPT_PTR] != MIN_RT_PTR) // Pointer isn't correct. + return 0; + + return 1; +} + +//** IPInitOptions - Initialize an option buffer. +// +// Called by an upper layer routine to initialize an option buffer. We fill +// in the default values for TTL, TOS, and flags, and NULL out the options +// buffer and size. +// +// Input: Options - Pointer to IPOptInfo structure. +// +// Returns: Nothing. +// +void +IPInitOptions(IPOptInfo *Options) +{ + Options->ioi_addr = NULL_IP_ADDR; + + Options->ioi_ttl = (uchar)DefaultTTL; + Options->ioi_tos = (uchar)DefaultTOS; + Options->ioi_flags = 0; + + Options->ioi_options = (uchar *)NULL; + Options->ioi_optlength = 0; + +} + +//** IPCopyOptions - Copy the user's options into IP header format. +// +// This routine takes an option buffer supplied by an IP client, validates it, and +// creates an IPOptInfo structure that can be passed to the IP layer for transmission. This +// includes allocating a buffer for the options, munging any source route +// information into the real IP format. +// +// Note that we never lock this structure while we're using it. This may cause transitory +// incosistencies while the structure is being updated if it is in use during the update. +// This shouldn't be a problem - a packet or too might get misrouted, but it should +// straighten itself out quickly. If this is a problem the client should make sure not +// to call this routine while it's in the IPTransmit routine. +// +// Entry: Options - Pointer to buffer of user supplied options. +// Size - Size in bytes of option buffer +// OptInfoPtr - Pointer to IPOptInfo structure to be filled in. +// +// Returns: A status, indicating whether or not the options were valid and copied. +// +IP_STATUS +IPCopyOptions(uchar *Options, uint Size, IPOptInfo *OptInfoPtr) +{ + uchar *TempOptions; // Buffer of options we'll build + uint TempSize; // Size of options. + IP_STATUS TempStatus; // Temporary status + uchar OptSeen = 0; // Indicates which options we've seen. + + + OptInfoPtr->ioi_addr = NULL_IP_ADDR; + + OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR; + + if (Size == 0) { + CTEAssert(FALSE); + OptInfoPtr->ioi_options = (uchar *)NULL; + OptInfoPtr->ioi_optlength = 0; + return IP_SUCCESS; + } + + + // Option size needs to be rounded to multiple of 4. + if ((TempOptions = CTEAllocMem(((Size & 3) ? (Size & ~3) + 4 : Size))) == (uchar *)NULL) + return IP_NO_RESOURCES; // Couldn't get a buffer, return error. + + CTEMemSet(TempOptions, 0, ((Size & 3) ? (Size & ~3) + 4 : Size)); + + // OK, we have a buffer. Loop through the provided buffer, copying options. + TempSize = 0; + TempStatus = IP_PENDING; + while (Size && TempStatus == IP_PENDING) { + uint SRSize; // Size of a source route option. + + switch (*Options) { + case IP_OPT_EOL: + TempStatus = IP_SUCCESS; + break; + case IP_OPT_NOP: + TempOptions[TempSize++] = *Options++; + Size--; + break; + case IP_OPT_SSRR: + if (OptSeen & (OPT_LSRR | OPT_SSRR)) { + TempStatus = IP_BAD_OPTION; // We've already seen a record route. + break; + } + OptInfoPtr->ioi_flags |= IP_FLAG_SSRR; + OptSeen |= OPT_SSRR; // Fall through to LSRR code. + case IP_OPT_LSRR: + if ( (*Options == IP_OPT_LSRR) && + (OptSeen & (OPT_LSRR | OPT_SSRR)) + ) { + TempStatus = IP_BAD_OPTION; // We've already seen a record route. + break; + } + if (*Options == IP_OPT_LSRR) + OptSeen |= OPT_LSRR; + if (!ValidRouteOption(Options, 2, Size)) { + TempStatus = IP_BAD_OPTION; + break; + } + + // Option is valid. Copy the first hop address to NewAddr, and move all + // of the other addresses forward. + TempOptions[TempSize++] = *Options++; // Copy option type. + SRSize = *Options++; + Size -= SRSize; + SRSize -= sizeof(IPAddr); + TempOptions[TempSize++] = SRSize; + TempOptions[TempSize++] = *Options++; // Copy pointer. + OptInfoPtr->ioi_addr = *(IPAddr UNALIGNED *)Options; + Options += sizeof(IPAddr); // Point to address beyond first hop. + CTEMemCopy(&TempOptions[TempSize], Options, SRSize - 3); + TempSize += (SRSize - 3); + Options += (SRSize - 3); + break; + case IP_OPT_RR: + if (OptSeen & OPT_RR) { + TempStatus = IP_BAD_OPTION; // We've already seen a record route. + break; + } + OptSeen |= OPT_RR; + if (!ValidRouteOption(Options, 1, Size)) { + TempStatus = IP_BAD_OPTION; + break; + } + SRSize = Options[IP_OPT_LENGTH]; + CTEMemCopy(&TempOptions[TempSize], Options, SRSize); + TempSize += SRSize; + Options += SRSize; + Size -= SRSize; + break; + case IP_OPT_TS: + { + uchar Overflow, Flags; + + if (OptSeen & OPT_TS) { + TempStatus = IP_BAD_OPTION; // We've already seen a time stamp + break; + } + OptSeen |= OPT_TS; + Flags = Options[IP_TS_OVFLAGS] & IP_TS_FLMASK; + Overflow = (Options[IP_TS_OVFLAGS] & IP_TS_OVMASK) >> 4; + + if (Overflow || (Flags != TS_REC_TS && Flags != TS_REC_ADDR && + Flags != TS_REC_SPEC)) { + TempStatus = IP_BAD_OPTION; // Bad flags or overflow value. + break; + } + + SRSize = Options[IP_OPT_LENGTH]; + if (SRSize > Size || SRSize < 8 || + Options[IP_OPT_PTR] != MIN_TS_PTR) { + TempStatus = IP_BAD_OPTION; // Option size isn't good. + break; + } + CTEMemCopy(&TempOptions[TempSize], Options, SRSize); + TempSize += SRSize; + Options += SRSize; + Size -= SRSize; + } + break; + default: + TempStatus = IP_BAD_OPTION; // Unknown option, error. + break; + } + } + + if (TempStatus == IP_PENDING) // We broke because we hit the end of the buffer. + TempStatus = IP_SUCCESS; // that's OK. + + if (TempStatus != IP_SUCCESS) { // We had some sort of an error. + CTEFreeMem(TempOptions); + return TempStatus; + } + + // Check the option size here to see if it's too big. We check it here at the end + // instead of at the start because the option size may shrink if there are source route + // options, and we don't want to accidentally error out a valid option. + TempSize = (TempSize & 3 ? (TempSize & ~3) + 4 : TempSize); + if (TempSize > MAX_OPT_SIZE) { + CTEFreeMem(TempOptions); + return IP_OPTION_TOO_BIG; + } + OptInfoPtr->ioi_options = TempOptions; + OptInfoPtr->ioi_optlength = TempSize; + + return IP_SUCCESS; + +} + +//** IPFreeOptions - Free options we're done with. +// +// Called by the upper layer when we're done with options. All we need to do is free +// the options. +// +// Input: OptInfoPtr - Pointer to IPOptInfo structure to be freed. +// +// Returns: Status of attempt to free options. +// +IP_STATUS +IPFreeOptions(IPOptInfo *OptInfoPtr) +{ + if (OptInfoPtr->ioi_options) { + // We have options to free. Save the pointer and zero the structure field before + // freeing the memory to try and present race conditions with it's use. + uchar *TempPtr = OptInfoPtr->ioi_options; + + OptInfoPtr->ioi_options = (uchar *)NULL; + CTEFreeMem(TempPtr); + OptInfoPtr->ioi_optlength = 0; + OptInfoPtr->ioi_addr = NULL_IP_ADDR; + OptInfoPtr->ioi_flags &= ~IP_FLAG_SSRR; + } + return IP_SUCCESS; +} + + +//BUGBUG - After we're done testing, move BEGIN_INIT up here. + +//** ipgetinfo - Return pointers to our NetInfo structures. +// +// Called by upper layer software during init. time. The caller +// passes a buffer, which we fill in with pointers to NetInfo +// structures. +// +// Entry: +// Buffer - Pointer to buffer to be filled in. +// Size - Size in bytes of buffer. +// +// Returns: +// Status of command. +// +IP_STATUS +IPGetInfo(IPInfo *Buffer, int Size) +{ + if (Size < sizeof(IPInfo)) + return IP_BUF_TOO_SMALL; // Not enough buffer space. + + Buffer->ipi_version = IP_DRIVER_VERSION; + Buffer->ipi_hsize = sizeof(IPHeader); + Buffer->ipi_xmit = IPTransmit; + Buffer->ipi_protreg = IPRegisterProtocol; + Buffer->ipi_openrce = OpenRCE; + Buffer->ipi_closerce = CloseRCE; + Buffer->ipi_getaddrtype = IPGetAddrType; + Buffer->ipi_getlocalmtu = IPGetLocalMTU; + Buffer->ipi_getpinfo = IPGetPInfo; + Buffer->ipi_checkroute = IPCheckRoute; + Buffer->ipi_initopts = IPInitOptions; + Buffer->ipi_updateopts = IPUpdateRcvdOptions; + Buffer->ipi_copyopts = IPCopyOptions; + Buffer->ipi_freeopts = IPFreeOptions; + Buffer->ipi_qinfo = IPQueryInfo; + Buffer->ipi_setinfo = IPSetInfo; + Buffer->ipi_getelist = IPGetEList; + Buffer->ipi_setmcastaddr = IPSetMCastAddr; + Buffer->ipi_invalidsrc = InvalidSourceAddress; + Buffer->ipi_isdhcpinterface = IsDHCPInterface; + + return IP_SUCCESS; + +} + +//** IPTimeout - IP timeout handler. +// +// The timeout routine called periodically to time out various things, such as entries +// being reassembled and ICMP echo requests. +// +// Entry: Timer - Timer being fired. +// Context - Pointer to NTE being time out. +// +// Returns: Nothing. +// +void +IPTimeout(CTEEvent *Timer, void *Context) +{ + NetTableEntry *NTE = STRUCT_OF(NetTableEntry, Timer, nte_timer); + CTELockHandle NTEHandle; + ReassemblyHeader *PrevRH, *CurrentRH, *TempList = (ReassemblyHeader *)NULL; + + ICMPTimer(NTE); + IGMPTimer(NTE); + if (Context) { + CTEGetLock(&NTE->nte_lock, &NTEHandle); + PrevRH = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next); + CurrentRH = PrevRH->rh_next; + while (CurrentRH) { + if (--CurrentRH->rh_ttl == 0) { // This guy timed out. + PrevRH->rh_next = CurrentRH->rh_next; // Take him out. + CurrentRH->rh_next = TempList; // And save him for later. + TempList = CurrentRH; + IPSInfo.ipsi_reasmfails++; + } else + PrevRH = CurrentRH; + + CurrentRH = PrevRH->rh_next; + } + + // We've run the list. If we need to free anything, do it now. This may + // include sending an ICMP message. + CTEFreeLock(&NTE->nte_lock, NTEHandle); + while (TempList) { + CurrentRH = TempList; + TempList = CurrentRH->rh_next; + // If this wasn't sent to a bcast address and we already have the first fragment, + // send a time exceeded message. + if (CurrentRH->rh_headersize != 0) + SendICMPErr(NTE->nte_addr, (IPHeader *)CurrentRH->rh_header, ICMP_TIME_EXCEED, + TTL_IN_REASSEM, 0); + FreeRH(CurrentRH); + } + + CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NULL); + } else + CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, NTE); + +} + +//* IPpSetNTEAddr - Set the IP address of an NTE. +// +// Called by the DHCP client to set or delete the IP address of an NTE. We +// make sure he's specifiying a valid NTE, then mark it up or down as needed, +// notify the upper layers of the change if necessary, and then muck with +// the routing tables. +// +// Input: Context - Context of NTE to alter. +// Addr - IP address to set. +// Mask - Subnet mask for Addr. +// +// Returns: TRUE if we changed the address, FALSE otherwise. +// +IP_STATUS +IPpSetNTEAddr(NetTableEntry *NTE, IPAddr Addr, IPMask Mask, + CTELockHandle *RouteTableHandle, SetAddrControl *ControlBlock, SetAddrRtn Rtn) +{ + Interface *IF; + uint (*CallFunc)(struct RouteTableEntry *, void *, void *); + + IF = NTE->nte_if; + DHCPActivityCount++; + + if (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) { + // We're deleting an address. + if (NTE->nte_flags & NTE_VALID) { + // The address is currently valid. Fix that. + + NTE->nte_flags &= ~NTE_VALID; + + // + // If the old address is in the ATCache, flush it out. + // + FlushATCache(NTE->nte_addr); + + if (--(IF->if_ntecount) == 0) { + // This is the last one, so we'll need to delete relevant + // routes. + CallFunc = DeleteRTEOnIF; + } else + CallFunc = InvalidateRCEOnIF; + + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + StopIGMPForNTE(NTE); + + // Now call the upper layers, and tell them that address is + // gone. We really need to do something about locking here. +#ifdef _PNP_POWER + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, NULL, FALSE); + +#else // _PNP_POWER + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL, + NTE->nte_context, NULL, NULL, FALSE); + +#endif // _PNP_POWER + + // Call RTWalk to take the appropriate action on the RTEs. + RTWalk(CallFunc, IF, NULL); + + // Delete the route to the address itself. + DeleteRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, + LoopNTE->nte_if); + + // Tell the lower interface this address is gone. + (*IF->if_deladdr)(IF->if_lcontext, LLIP_ADDR_LOCAL, NTE->nte_addr, + NULL_IP_ADDR); + + CTEGetLock(&RouteTableLock, RouteTableHandle); + } + + DHCPActivityCount--; + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + return IP_SUCCESS; + } else { + uint Status; + + // We're not deleting, we're setting the address. + if (!(NTE->nte_flags & NTE_VALID)) { + uint index; + + // The address is invalid. Save the info, mark him as valid, + // and add the routes. + NTE->nte_addr = Addr; + NTE->nte_mask = Mask; + NTE->nte_flags |= NTE_VALID; + IF->if_ntecount++; + index = IF->if_index; + + // + // If the new address is in the ATCache, flush it out, otherwise + // TdiOpenAddress may fail. + // + FlushATCache(Addr); + + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + if (AddNTERoutes(NTE)) + Status = TRUE; + else + Status = FALSE; + + // Need to tell the lower layer about it. + if (Status) { + Interface *IF = NTE->nte_if; + + ControlBlock->sac_rtn = Rtn; + Status = (*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL, + Addr, Mask, ControlBlock ); + } + + if (Status == FALSE) { + // Couldn't add the routes. Recurively mark this NTE as down. + IPSetNTEAddr(NTE->nte_context, NULL_IP_ADDR, 0, NULL, NULL); + } else { + InitIGMPForNTE(NTE); + + // Now call the upper layers, and tell them that address is + // is here. We really need to do something about locking here. +#ifdef _PNP_POWER + +#ifdef SECFLTR + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, + NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle, + &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, + NTE->nte_pnpcontext, NTE->nte_context, &NTE->nte_addrhandle, + NULL, TRUE); +#endif // SECFLTR + +#else // _PNP_POWER + +#ifdef SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL, + NTE->nte_context, NULL, &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NULL, + NTE->nte_context, NULL, NULL, TRUE); + +#endif // SECFLTR +#endif // _PNP_POWER + +#ifdef NT + if (!IP_ADDR_EQUAL(Addr, NULL_IP_ADDR)) { + SetPersistentRoutesForNTE( + net_long(Addr), + net_long(Mask), + index + ); + } +#endif // NT + + if ( (Status != IP_PENDING) && (Rtn != NULL) ) { + (*Rtn)(ControlBlock, IP_SUCCESS); + } + } + + CTEGetLock(&RouteTableLock, RouteTableHandle); + NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY; + NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING; + } else + Status = FALSE; + + DHCPActivityCount--; + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + if (Status) { + return IP_PENDING; + } else { + return IP_GENERAL_FAILURE; + } + } +} + +//* IPSetNTEAddr - Set the IP address of an NTE. +// +// Wrapper routine for IPpSetNTEAddr +// +// Input: Context - Context of NTE to alter. +// Addr - IP address to set. +// Mask - Subnet mask for Addr. +// +// Returns: TRUE if we changed the address, FALSE otherwise. +// +uint +IPSetNTEAddr(ushort Context, IPAddr Addr, IPMask Mask, SetAddrControl *ControlBlock, SetAddrRtn Rtn) +{ + CTELockHandle Handle; + uint Status; + NetTableEntry *NTE; + + + CTEGetLock(&RouteTableLock, &Handle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if (NTE->nte_context == Context) + break; + + if (NTE == NULL || NTE == LoopNTE) { + // Can't alter the loopback NTE, or one we didn't find. + CTEFreeLock(&RouteTableLock, Handle); + return IP_GENERAL_FAILURE; + } + + Status = IPpSetNTEAddr(NTE, Addr, Mask, &Handle, ControlBlock, Rtn); + + return(Status); +} + + +#pragma BEGIN_INIT + +extern NetTableEntry *InitLoopback(IPConfigInfo *); + +//** InitTimestamp - Intialize the timestamp for outgoing packets. +// +// Called at initialization time to setup our first timestamp. The timestamp we use +// is the in ms since midnite GMT at which the system started. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +InitTimestamp() +{ + ulong GMTDelta; // Delta in ms from GMT. + ulong Now; // Milliseconds since midnight. + + TimeStamp = 0; + + if ((GMTDelta = GetGMTDelta()) == 0xffffffff) { // Had some sort of error. + TSFlag = 0x80000000; + return; + } + + if ((Now = GetTime()) > (24L*3600L*1000L)) { // Couldn't get time since midnight. + TSFlag = net_long(0x80000000); + return; + } + + TimeStamp = Now + GMTDelta - CTESystemUpTime(); + TSFlag = 0; + +} +#pragma END_INIT + +#ifndef CHICAGO +#pragma BEGIN_INIT +#else +#pragma code_seg("_LTEXT", "LCODE") +#endif + +//** InitNTE - Initialize an NTE. +// +// This routine is called during initialization to initialize an NTE. We +// allocate memory, NDIS resources, etc. +// +// +// Entry: NTE - Pointer to NTE to be initalized. +// +// Returns: 0 if initialization failed, non-zero if it succeeds. +// +int +InitNTE(NetTableEntry *NTE) +{ + Interface *IF; + NetTableEntry *PrevNTE; + + NTE->nte_ralist = NULL; + NTE->nte_echolist = NULL; + + // + // Taken together, the context and instance numbers uniquely identify + // a network entry, even across boots of the system. The instance number + // will have to become dynamic if contexts are ever reused. + // + NTE->nte_context = NextNTEContext++; + NTE->nte_rtrlist = NULL; + NTE->nte_instance = GetUnique32BitValue(); + + // Now link him on the IF chain, and bump the count. + IF = NTE->nte_if; + PrevNTE = STRUCT_OF(NetTableEntry, &IF->if_nte, nte_ifnext); + while (PrevNTE->nte_ifnext != NULL) + PrevNTE = PrevNTE->nte_ifnext; + + PrevNTE->nte_ifnext = NTE; + NTE->nte_ifnext = NULL; + + if (NTE->nte_flags & NTE_VALID) { + IF->if_ntecount++; + } + + CTEInitTimer(&NTE->nte_timer); + CTEStartTimer(&NTE->nte_timer, IP_TIMEOUT, IPTimeout, (void *)NULL); + return TRUE; +} + +//** InitInterface - Initialize with an interface. +// +// Called when we need to initialize with an interface. We set the appropriate NTE +// info, then register our local address and any appropriate broadcast addresses +// with the interface. We assume the NTE being initialized already has an interface +// pointer set up for it. We also allocate at least one TD buffer for use on the interface. +// +// Input: NTE - NTE to initialize with the interface. +// +// Returns: TRUE is we succeeded, FALSE if we fail. +// +int +InitInterface(NetTableEntry *NTE) +{ + IPMask netmask = IPNetMask(NTE->nte_addr); + uchar *TDBuffer; // Pointer to tdbuffer + PNDIS_PACKET Packet; + NDIS_HANDLE TDbpool; // Handle for TD buffer pool. + NDIS_HANDLE TDppool; + PNDIS_BUFFER TDBufDesc; // Buffer descriptor for TDBuffer. + NDIS_STATUS Status; + Interface *IF; // Interface for this NTE. + CTELockHandle Handle; + + + IF = NTE->nte_if; + + CTEAssert(NTE->nte_mss > sizeof(IPHeader)); + CTEAssert(IF->if_mtu > 0); + + NTE->nte_mss = MIN((NTE->nte_mss - sizeof(IPHeader)), IF->if_mtu); + + CTERefillMem(); + + // Allocate resources needed for xfer data calls. The TD buffer has to be as large + // as any frame that can be received, even though our MSS may be smaller, because we + // can't control what might be sent at us. + TDBuffer = CTEAllocMem(IF->if_mtu); + if (TDBuffer == (uchar *)NULL) + return FALSE; + + NdisAllocatePacketPool(&Status, &TDppool, 1, sizeof(TDContext)); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(TDBuffer); + return FALSE; + } + + NdisAllocatePacket(&Status, &Packet, TDppool); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; + } + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(TDContext)); + + NdisAllocateBufferPool(&Status, &TDbpool, 1); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; + } + + NdisAllocateBuffer(&Status,&TDBufDesc, TDbpool, TDBuffer, + (IF->if_mtu + sizeof(IPHeader))); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(TDbpool); + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; + } + + NdisChainBufferAtFront(Packet, TDBufDesc); + + ((TDContext *)Packet->ProtocolReserved)->tdc_buffer = TDBuffer; + + + if (NTE->nte_flags & NTE_VALID) { + + // Add our local IP address. + if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_LOCAL, + NTE->nte_addr, NTE->nte_mask, NULL)) { + NdisFreeBufferPool(TDbpool); + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; // Couldn't add local address. + } + } + + // Set up the broadcast addresses for this interface, iff we're the + // 'primary' NTE on the interface. + if (NTE->nte_flags & NTE_PRIMARY) { + + if (!(*IF->if_addaddr)(IF->if_lcontext, LLIP_ADDR_BCAST, + NTE->nte_if->if_bcast, 0, NULL)) { + NdisFreeBufferPool(TDbpool); + NdisFreePacketPool(TDppool); + CTEFreeMem(TDBuffer); + return FALSE; // Couldn't add broadcast address. + } + } + + if (IF->if_llipflags & LIP_COPY_FLAG) { + NTE->nte_flags |= NTE_COPY; + } + + CTEGetLock(&IF->if_lock, &Handle); + ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link = IF->if_tdpacket; + IF->if_tdpacket = Packet; + CTEFreeLock(&IF->if_lock, Handle); + + return TRUE; +} + +#ifndef _PNP_POWER +//* CleanAdaptTable - Clean up the adapter name table. +// +// +void +CleanAdaptTable() +{ + int i = 0; + + while (AdptNameTable[i].nm_arpinfo != NULL) { + CTEFreeMem(AdptNameTable[i].nm_arpinfo); + CTEFreeString(&AdptNameTable[i].nm_name); + if (AdptNameTable[i].nm_driver.Buffer != NULL) + CTEFreeString(&AdptNameTable[i].nm_driver); + i++; + } +} + + +//* OpenAdapters - Clean up the adapter name table. +// +// Used at the end of initialization. We loop through and 'open' all the adapters. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +OpenAdapters() +{ + int i = 0; + LLIPBindInfo *ABI; + + while ((ABI = AdptNameTable[i++].nm_arpinfo) != NULL) { + (*(ABI->lip_open))(ABI->lip_context); + } +} + + +//* IPRegisterDriver - Called during init time to register a driver. +// +// Called during init time when we have a non-LAN (or non-ARPable) driver +// that wants to register with us. We try to find a free slot in the table +// to register him. +// +// Input: Name - Pointer to the name of the driver to be registered. +// Ptr - Pointer to driver's registration function. +// +// Returns: TRUE if we succeeded, FALSE if we fail. +// +uint +IPRegisterDriver(PNDIS_STRING Name, LLIPRegRtn Ptr) +{ + uint i; + + CTERefillMem(); + + // First, find a slot for him. + for (i = 0; i < MaxIPNets; i++) { + if (DriverNameTable[i].drm_driver.Buffer == NULL) { + // Found a slot. Try and allocate and copy a string for him. + if (!CTEAllocateString(&DriverNameTable[i].drm_driver, + CTELengthString(Name))) + return FALSE; + // Got the space. Copy the string and the pointer. + CTECopyString(&DriverNameTable[i].drm_driver, Name); + DriverNameTable[i].drm_regptr = Ptr; + NumRegDrivers++; + return TRUE; + } + } + + +} + + +#ifdef NT + +//* GetLLRegPtr - Called during init time to get a lower driver's registration +// routine. +// +// Called during init time to locate the registration function of a +// non-LAN (or non-ARPable) driver. +// +// Input: Name - Pointer to the name of the driver to be registered. +// +// Returns: A pointer to the driver's registration routine or NULL on failure. +// +LLIPRegRtn +GetLLRegPtr(PNDIS_STRING Name) +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + LLIPIF_REGISTRATION_DATA registrationData; + IO_STATUS_BLOCK ioStatusBlock; + PIRP irp; + KEVENT ioctlEvent; +extern POBJECT_TYPE *IoDeviceObjectType; + + + registrationData.RegistrationFunction = NULL; + + KeInitializeEvent(&ioctlEvent, SynchronizationEvent, FALSE); + + status = IoGetDeviceObjectPointer( + Name, + SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, + &fileObject, + &deviceObject + ); + + if (status != STATUS_SUCCESS) { + CTEPrint("IP failed to open the lower layer driver\n"); + return(NULL); + } + + // + // Reference the device object. + // + ObReferenceObject(deviceObject); + + // + // IoGetDeviceObjectPointer put a reference on the file object. + // + ObDereferenceObject(fileObject); + + irp = IoBuildDeviceIoControlRequest( + IOCTL_LLIPIF_REGISTER, + deviceObject, + NULL, // input Buffer + 0, // input buffer length + ®istrationData, + sizeof(LLIPIF_REGISTRATION_DATA), + FALSE, // not an InternalDeviceControl + &ioctlEvent, + &ioStatusBlock + ); + + if (irp == NULL) { + ObDereferenceObject(deviceObject); + return(NULL); + } + + status = IoCallDriver(deviceObject, irp); + + if (status == STATUS_PENDING) { + status = KeWaitForSingleObject( + &ioctlEvent, + Executive, + KernelMode, + FALSE, // not alertable + NULL // no timeout + ); + + } + + ObDereferenceObject(deviceObject); + + if (status != STATUS_SUCCESS) { + return(NULL); + } + + if (registrationData.RegistrationFunction != NULL) { + // + // Cache the driver registration for future reference. + // + IPRegisterDriver(Name, registrationData.RegistrationFunction); + } + + return(registrationData.RegistrationFunction); + +} // GetLLRegPtr + +#endif // NT +#endif // _PNP_POWER + + +#ifndef _PNP_POWER + +//* FindRegPtr - Find a driver's registration routine. +// +// Called during init time when we have a non-LAN (or non-ARPable) driver to +// register with. We take in the driver name, and try to find a registration +// pointer for the driver. +// +// Input: Name - Pointer to the name of the driver to be found. +// +// Returns: Pointer to the registration routine, or NULL if there is none. +// +LLIPRegRtn +FindRegPtr(PNDIS_STRING Name) +{ + uint i; + + for (i = 0; i < NumRegDrivers; i++) { + if (CTEEqualString(&(DriverNameTable[i].drm_driver), Name)) + return (LLIPRegRtn)(DriverNameTable[i].drm_regptr); + } + +#ifdef NT + // + // For NT, we open the lower driver and issue an IOCTL to get a pointer to + // its registration function. We then cache this in the table for future + // reference. + // + return(GetLLRegPtr(Name)); +#else + return NULL; +#endif // NT +} + +#endif // _PNP_POWER + +#ifdef CHICAGO +#pragma BEGIN_INIT +#endif + +//* FreeNets - Free nets we have allocated. +// +// Called during init time if initialization fails. We walk down our list +// of nets, and free them. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +FreeNets(void) +{ + NetTableEntry *NTE; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + CTEFreeMem(NTE); +} + +#ifdef CHICAGO +#pragma END_INIT +#pragma code_seg("_LTEXT", "LCODE") +#endif + + +#ifdef _PNP_POWER + +extern uint GetGeneralIFConfig(IFGeneralConfig *GConfigInfo, NDIS_HANDLE Handle); +extern IFAddrList *GetIFAddrList(uint *NumAddr, NDIS_HANDLE Handle); + + +#ifdef CHICAGO + +extern void RequestDHCPAddr(ushort context); + +#define MAX_NOTIFY_CLIENTS 8 + +typedef void (*AddrNotifyRtn)(IPAddr Addr, IPMask Mask, void *Context, + ushort IPContext, uint Added); + +AddrNotifyRtn AddrNotifyTable[MAX_NOTIFY_CLIENTS]; + +typedef void (*InterfaceNotifyRtn)(ushort Context, uint Added); + +InterfaceNotifyRtn InterfaceNotifyTable[MAX_NOTIFY_CLIENTS]; + +//* RegisterAddrNotify - Register an address notify routine. +// +// A routine called to register an address notify routine. +// +// Input: Rtn - Routine to register. +// Register - True to register, False to deregister. +// +// Returns: TRUE if we succeed, FALSE if we don't/ +// +uint +RegisterAddrNotify(AddrNotifyRtn Rtn, uint Register) +{ + uint i; + AddrNotifyRtn NewRtn, OldRtn; + + if (Register) { + NewRtn = Rtn; + OldRtn = NULL; + } else { + NewRtn = NULL; + OldRtn = Rtn; + } + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (AddrNotifyTable[i] == OldRtn) { + AddrNotifyTable[i] = NewRtn; + return TRUE; + } + } + + return FALSE; +} + + +//* NotifyInterfaceChange - Notify clients of a change in an interface. +// +// Called when we want to notify registered clients that an interface has come +// or gone. We loop through our InterfaceNotify table, calling each one. +// +// Input: Context - Context for interface that has changed. +// Added - True if the interface is coming, False if it's +// going. +// +// Returns: Nothing. +// +void +NotifyInterfaceChange(ushort IPContext, uint Added) +{ + uint i; + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (InterfaceNotifyTable[i] != NULL) + (*(InterfaceNotifyTable[i]))(IPContext, Added); + } +} + +//* RegisterInterfaceNotify - Register an interface notify routine. +// +// A routine called to register an interface notify routine. +// +// Input: Rtn - Routine to register. +// Register - True to register, False to deregister. +// +// Returns: TRUE if we succeed, FALSE if we don't/ +// +uint +RegisterInterfaceNotify(InterfaceNotifyRtn Rtn, uint Register) +{ + uint i; + InterfaceNotifyRtn NewRtn, OldRtn; + + if (Register) { + NewRtn = Rtn; + OldRtn = NULL; + } else { + NewRtn = NULL; + OldRtn = Rtn; + } + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (InterfaceNotifyTable[i] == OldRtn) { + InterfaceNotifyTable[i] = NewRtn; + return TRUE; + } + } + + return FALSE; +} + +//* NotifyAddrChange - Notify clients of a change in addresses. +// +// Called when we want to notify registered clients that an address has come +// or gone. We loop through our AddrNotify table, calling each one. +// +// Input: Addr - Addr that has changed. +// Mask - Mask that has changed. +// Context - PNP context for address +// IPContext - NTE context for NTE +// Handle - Pointer to where to get/set address registration +// handle +// ConfigName - Registry name to use to retrieve config info. +// Added - True if the addr is coming, False if it's going. +// +// Returns: Nothing. +// +void +NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + PVOID *Handle, PNDIS_STRING ConfigName, uint Added) +{ + uint i; + + for (i = 0; i < MAX_NOTIFY_CLIENTS; i++) { + if (AddrNotifyTable[i] != NULL) + (*(AddrNotifyTable[i]))(Addr, Mask, Context, IPContext, Added); + } +} + + +#else // CHICAGO + +//* NotifyAddrChange - Notify clients of a change in addresses. +// +// Called when we want to notify registered clients that an address has come +// or gone. We call TDI to perform this function. +// +// Input: Addr - Addr that has changed. +// Mask - Mask that has changed. +// Context - PNP context for address +// IPContext - NTE context for NTE +// Handle - Pointer to where to get/set address registration +// handle +// ConfigName - Registry name to use to retrieve config info. +// Added - True if the addr is coming, False if it's going. +// +// Returns: Nothing. +// +void +NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + PVOID *Handle, PNDIS_STRING ConfigName, uint Added) +{ + uchar Address[sizeof(TA_ADDRESS) + sizeof(TDI_ADDRESS_IP)]; + PTA_ADDRESS AddressPtr; + PTDI_ADDRESS_IP IPAddressPtr; + NTSTATUS Status; + +#ifdef SECFLTR + + IP_STATUS StatusType; + NDIS_HANDLE ConfigHandle = NULL; + int i; + ULStatusProc StatProc; + +#endif // SECFLTR + + + AddressPtr = (PTA_ADDRESS)Address; + + AddressPtr->AddressLength = sizeof(TDI_ADDRESS_IP); + AddressPtr->AddressType = TDI_ADDRESS_TYPE_IP; + + IPAddressPtr = (PTDI_ADDRESS_IP)AddressPtr->Address; + + CTEMemSet(IPAddressPtr, 0, sizeof(TDI_ADDRESS_IP)); + + IPAddressPtr->in_addr = Addr; + +#ifdef SECFLTR + + // + // Call the status entrypoint of the transports so they can + // adjust their security filters. + // + if (Added) { + StatusType = IP_ADDR_ADDED; + + // + // Open a configuration key + // + if (!OpenIFConfig(ConfigName, &ConfigHandle)) { + // + // Not much we can do. The transports will have + // to handle this. + // + CTEAssert(ConfigHandle == NULL); + } + } + else { + StatusType = IP_ADDR_DELETED; + } + + for ( i = 0; i < NextPI; i++) { + StatProc = IPProtInfo[i].pi_status; + if (StatProc != NULL) + (*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR, + NULL_IP_ADDR, 0, ConfigHandle ); + } + + if (ConfigHandle != NULL) { + CloseIFConfig(ConfigHandle); + } + +#endif // SECFLTR + + // + // Notify any interested parties via TDI. The transports all register + // for this notification as well. + // + if (Added) { + Status = TdiRegisterNetAddress(AddressPtr, Handle); + if (Status != STATUS_SUCCESS) { + *Handle = NULL; + } + } else { + if (*Handle != NULL) { + TdiDeregisterNetAddress(*Handle); + *Handle = NULL; + } + } + +} + +#endif // CHICAGO + + +//* IPAddNTE - Add a new NTE to an interface +// +// Called to create a new network entry on an interface. +// +// Input: GConfigInfo - Configuration information for the interface +// PNPContext - The PNP context value associated with the interface +// RegRtn - Routine to call to register with ARP. +// BindInfo - Pointer to NDIS bind information. +// IF - The interface on which to create the NTE. +// NewAddr - The address of the new NTE. +// NewMask - The subnet mask for the new NTE. +// IsPrimary - TRUE if this NTE is the primary one on the interface +// IsDynamic - TRUE if this NTE is being created on an +// existing interface instead of a new one. +// +// Returns: A pointer to the new NTE if the operation succeeds. +// NULL if the operation fails. +// +NetTableEntry * +IPAddNTE(IFGeneralConfig *GConfigInfo, void * PNPContext, LLIPRegRtn RegRtn, + LLIPBindInfo *BindInfo, Interface *IF, IPAddr NewAddr, IPMask NewMask, + uint IsPrimary, uint IsDynamic) +{ + NetTableEntry *NTE, *PrevNTE; + CTELockHandle Handle; + + + // If the address is invalid we're done. Fail the request. + if (CLASSD_ADDR(NewAddr) || CLASSE_ADDR(NewAddr)) { + return NULL; + } + + // See if we have an inactive NTE on the NetTableList. If we do, we'll + // just recycle that. We will pull him out of the list. This is not + // strictly MP safe, since other people could be walking the list while + // we're doing this without holding a lock, but it should be harmless. + // The removed NTE is marked as invalid, and his next pointer will + // be nulled, so anyone walking the list might hit the end too soon, + // but that's all. The memory is never freed, and the next pointer is + // never pointed at freed memory. + + CTEGetLock(&RouteTableLock, &Handle); + + PrevNTE = STRUCT_OF(NetTableEntry, &NetTableList, nte_next); + for (NTE = NetTableList; NTE != NULL; PrevNTE = NTE, NTE = NTE->nte_next) + if (!(NTE->nte_flags & NTE_ACTIVE)) { + PrevNTE->nte_next = NTE->nte_next; + NTE->nte_next = NULL; + NumNTE--; + break; + } + + CTEFreeLock(&RouteTableLock, Handle); + + // See if we got one. + if (NTE == NULL) { + // Didn't get one. Try to allocate one. + NTE = CTEAllocMem(sizeof(NetTableEntry)); + if (NTE == NULL) + return NULL; + CTEMemSet(NTE, 0, sizeof(NetTableEntry)); + } + + // Initialize the address and mask stuff + NTE->nte_addr = NewAddr; + NTE->nte_mask = NewMask; + NTE->nte_mss = MAX(GConfigInfo->igc_mtu, 68); + NTE->nte_rtrdiscaddr = GConfigInfo->igc_rtrdiscaddr; + NTE->nte_rtrdiscstate = NTE_RTRDISC_UNINIT; + NTE->nte_rtrdisccount = 0; + NTE->nte_rtrdiscovery = (uchar)GConfigInfo->igc_rtrdiscovery; + NTE->nte_rtrlist = NULL; + NTE->nte_pnpcontext = PNPContext; + NTE->nte_if = IF; + NTE->nte_flags = NTE_ACTIVE; + + // + // If the new address is in the ATCache, flush it out, otherwise + // TdiOpenAddress may fail. + // + FlushATCache(NewAddr); + + if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + NTE->nte_flags |= NTE_VALID; + NTE->nte_rtrdisccount = MAX_SOLICITATION_DELAY; + NTE->nte_rtrdiscstate = NTE_RTRDISC_DELAYING; + } + + if (IsDynamic) { + NTE->nte_flags |= NTE_DYNAMIC; + } + + NTE->nte_ralist = NULL; + NTE->nte_echolist = NULL; + NTE->nte_icmpseq = 0; + NTE->nte_igmplist = NULL; + CTEInitLock(&NTE->nte_lock); + CTEInitTimer(&NTE->nte_timer); + + if (IsPrimary) { + // + // This is the first (primary) NTE on the interface. + // + NTE->nte_flags |= NTE_PRIMARY; + + // Pass our information to the underlying code. + if (!(*RegRtn)(&(IF->if_configname), NTE, IPRcv, IPSendComplete, + IPStatus, IPTDComplete, IPRcvComplete, BindInfo, + IF->if_index)) { + + // Couldn't register. + goto failure; + } + } + + // + // Link the NTE onto the global NTE list. + // + CTEGetLock(&RouteTableLock, &Handle); + + NTE->nte_next = NetTableList; + NetTableList = NTE; + NumNTE++; + + CTEFreeLock(&RouteTableLock, Handle); + + if (!InitInterface(NTE)) { + goto failure; + } + + if (!InitNTE(NTE)) { + goto failure; + } + + if (!InitNTERouting(NTE, GConfigInfo->igc_numgws, GConfigInfo->igc_gw)) { + // Couldn't add the routes for this NTE. Mark him as not valid. + // Probably should log an event here. + if (NTE->nte_flags & NTE_VALID) { + NTE->nte_flags &= ~NTE_VALID; + NTE->nte_if->if_ntecount--; + } + } + +#ifdef NT + + if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + SetPersistentRoutesForNTE( + net_long(NTE->nte_addr), + net_long(NTE->nte_mask), + NTE->nte_if->if_index + ); + } + +#endif // NT + + return(NTE); + +failure: + + // + // BUGBUG - what should we do with the NTE here???? + // + + return(NULL); +} + + +//* IPAddDynamicNTE - Add a new "dynamic" NTE to an existing interface +// +// Called to dynamically create a new network entry on an existing interface. +// This entry was not configured when the interaface was originally created +// and will not persist if the interface is unbound. +// +// Input: InterfaceContext - The context value which identifies the +// interface on which to create the NTE. +// NewAddr - The address of the new NTE. +// NewMask - The subnet mask for the new NTE. +// +// Output: NTEContext - The context identifying the new NTE. +// NTEInstance - The instance number which (reasonably) uniquely +// identifies this NTE in time. +// +// Returns: Nonzero if the operation succeeded. Zero if it failed. +// +uint +IPAddDynamicNTE(ushort InterfaceContext, IPAddr NewAddr, IPMask NewMask, + ushort *NTEContext, ulong *NTEInstance) +{ + IFGeneralConfig GConfigInfo; // General config info structure. + NDIS_HANDLE Handle; // Configuration handle. + NetTableEntry *NTE; + Interface *IF; + ushort MTU; + uint Flags = 0; + + +#ifdef NT + PAGED_CODE(); +#endif + + for (IF = IFList; IF != NULL; IF = IF->if_next) { + if (IF->if_index == InterfaceContext) { + break; + } + } + + //* Try to get the network configuration information. + if (!OpenIFConfig(&(IF->if_configname), &Handle)) + return FALSE; + + // Try to get our general config information. + if (!GetGeneralIFConfig(&GConfigInfo, Handle)) { + goto failure; + } + + NTE = IPAddNTE( + &GConfigInfo, + NULL, // PNPContext - BUGBUG needed? + NULL, // RegRtn - not needed if not primary + NULL, // BindInfo - not needed if not primary + IF, + NewAddr, + NewMask, + FALSE, // not primary + TRUE // is dynamic + ); + + if (NTE == NULL) { + goto failure; + } + + CloseIFConfig(Handle); + + // + // Notify upper layers of the new address. + // +#ifdef SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE); + +#endif // SECFLTR + + if (!IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + InitIGMPForNTE(NTE); + } + else { +#ifdef CHICAGO + // Call DHCP to get an address for this guy. + + // + // BUGBUG (mikemas 8/28/96) + // we may not always want to do this! + // + RequestDHCPAddr(NTE->nte_context); +#endif + } + + // + // Fill in the out parameter value. + // + *NTEContext = NTE->nte_context; + *NTEInstance = NTE->nte_instance; + + return(TRUE); + +failure: + + CloseIFConfig(Handle); + + return(IP_GENERAL_FAILURE); +} + + +//* IPAddInterface - Add an interface. +// +// Called when someone has an interface they want us to add. We read our +// configuration information, and see if we have it listed. If we do, +// we'll try to allocate memory for the structures we need. Then we'll +// call back to the guy who called us to get things going. Finally, we'll +// see if we have an address that needs to be DHCP'ed. +// +// Input: ConfigName - Name of config info we're to read. +// Context - Context to pass to i/f on calls. +// RegRtn - Routine to call to register. +// BindInfo - Pointer to bind information. +// +// Returns: Status of attempt to add the interface. +// +IP_STATUS +IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context, + LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo) +{ + IFGeneralConfig GConfigInfo; // General config info structure. + IFAddrList *AddrList; // List of addresses for this I/F. + uint NumAddr; // Number of IP addresses on this + // interface + NetTableEntry *NTE; // Current NTE being initialized. + uint i; // Index variable. + uint IndexMask; // Mask for searching IFBitMask. + Interface *IF; // Interface being added. + NDIS_HANDLE Handle; // Configuration handle. + NetTableEntry *PrimaryNTE; // The primary NTE for this I/F. + uint IFIndex; // Index to be assigned to this I/F. + NetTableEntry *LastNTE; // Last NTE created. + + + CTERefillMem(); + + PrimaryNTE = NULL; + AddrList = NULL; + IF = NULL; + LastNTE = NULL; + + //* First, try to get the network configuration information. + if (!OpenIFConfig(ConfigName, &Handle)) + return IP_GENERAL_FAILURE; // Couldn't get IFConfig. + + // Try to get our general config information. + if (!GetGeneralIFConfig(&GConfigInfo, Handle)) { + goto failure; + } + + // We got the general config info. Now allocate an interface. + IF = CTEAllocMem(InterfaceSize + ConfigName->MaximumLength); + + if (IF == NULL) { + goto failure; + } + + CTEMemSet(IF, 0, InterfaceSize); + CTEInitLock(&IF->if_lock); + + // Initialize the broadcast we'll use. + if (GConfigInfo.igc_zerobcast) + IF->if_bcast = IP_ZERO_BCST; + else + IF->if_bcast = IP_LOCAL_BCST; + + if (RouterConfigured) { + RouteInterface *RtIF = (RouteInterface *)IF; + + + RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_running = FALSE; + RtIF->ri_q.rsq_pending = 0; + RtIF->ri_q.rsq_maxpending = GConfigInfo.igc_maxpending; + RtIF->ri_q.rsq_qlength = 0; + CTEInitLock(&RtIF->ri_q.rsq_lock); + } + + IF->if_xmit = BindInfo->lip_transmit; + IF->if_transfer = BindInfo->lip_transfer; + IF->if_close = BindInfo->lip_close; + IF->if_invalidate = BindInfo->lip_invalidate; + IF->if_lcontext = BindInfo->lip_context; + IF->if_addaddr = BindInfo->lip_addaddr; + IF->if_deladdr = BindInfo->lip_deladdr; + IF->if_qinfo = BindInfo->lip_qinfo; + IF->if_setinfo = BindInfo->lip_setinfo; + IF->if_getelist = BindInfo->lip_getelist; + IF->if_tdpacket = NULL; + CTEAssert(BindInfo->lip_mss > sizeof(IPHeader)); + IF->if_mtu = BindInfo->lip_mss - sizeof(IPHeader); + IF->if_speed = BindInfo->lip_speed; + IF->if_flags = BindInfo->lip_flags & LIP_P2P_FLAG ? IF_FLAGS_P2P : 0; + IF->if_addrlen = BindInfo->lip_addrlen; + IF->if_addr = BindInfo->lip_addr; + IF->if_pnpcontext = PNPContext; + IF->if_llipflags = BindInfo->lip_flags; + + // Initialize the reference count to 1, for the open. + IF->if_refcount = 1; + +#ifdef IGMPV2 + IF->IgmpVersion = IGMPV2; +#else + IF->IgmpVersion = IGMPV1; +#endif + + + // + // No need to do the following since IF structure is inited to 0 through + // memset above + // + // IF->IgmpVer1Timeout = 0; + + // + // Copy the config string for use later when DHCP enables an address + // on this interface or when an NTE is added dynamically. + // + IF->if_configname.Buffer = (PVOID) (((uchar *)IF) + InterfaceSize); + IF->if_configname.Length = 0; + IF->if_configname.MaximumLength = ConfigName->MaximumLength; + + CTECopyString( + &(IF->if_configname), + ConfigName + ); + + // Find out how many addresses we have, and get the address list. + AddrList = GetIFAddrList(&NumAddr, Handle); + + if (AddrList == NULL) { + CTEFreeMem(IF); + goto failure; + } + + // + //Link this interface onto the global interface list + // + IF->if_next = IFList; + IFList = IF; + + if (FirstIF == NULL) + FirstIF = IF; + + NumIF++; + IndexMask = 1; + + for (i = 0; i < MAX_TDI_ENTITIES; i++) { + if ((IFBitMask[i/BITS_PER_WORD] & IndexMask) == 0) { + IFIndex = i+ 1; + IFBitMask[i/BITS_PER_WORD] |= IndexMask; + break; + } + if (((i+1) % BITS_PER_WORD) == 0) { + IndexMask = 1; + } else { + IndexMask = IndexMask << 1; + } + } + + if (i == MAX_TDI_ENTITIES) { + // Too many interfaces bound. + goto failure; + } + + IF->if_index = IFIndex; + + // Now loop through, initializing each NTE as we go. We don't hold any + // locks while we do this, since NDIS won't reenter us here and no one + // else manipulates the NetTableList. + + for (i = 0;i < NumAddr;i++) { + NetTableEntry *PrevNTE; + IPAddr NewAddr; + uint isPrimary; + + if (i == 0) { + isPrimary = TRUE; + } + else { + isPrimary = FALSE; + } + + NTE = IPAddNTE( + &GConfigInfo, + PNPContext, + RegRtn, + BindInfo, + IF, + net_long(AddrList[i].ial_addr), + net_long(AddrList[i].ial_mask), + isPrimary, + FALSE // not dynamic + ); + + if (NTE == NULL) { + goto failure; + } + + if (isPrimary) { + PrimaryNTE = NTE; + +#ifdef NT + + // + // Write the context of the first interface to the registry. + // + if (isPrimary) { + NTSTATUS writeStatus; + ulong context = (ulong) NTE->nte_context; + + writeStatus = SetRegDWORDValue( + Handle, + L"IPInterfaceContext", + &context + ); + + if (!NT_SUCCESS(writeStatus)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 2, + 1, + &(ConfigName->Buffer), + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to write IPInterfaceContext value for adapter %ws\n" + " (status %lx). DHCP will be unable to configure this \n" + " adapter.\n", + ConfigName->Buffer, + writeStatus + )); + } + } + +#endif // NT + + } + + LastNTE = NTE; + } + +#ifdef NT + + if (LastNTE != NULL) { + + NTSTATUS writeStatus; + ulong context = (ulong) LastNTE->nte_context; + + writeStatus = SetRegDWORDValue( + Handle, + L"IPInterfaceContextMax", + &context + ); + + if (!NT_SUCCESS(writeStatus)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 3, + 1, + &(ConfigName->Buffer), + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to write IPInterfaceContextMax value for adapter %ws\n" + " (status %lx). DHCP will be unable to configure this \n" + " adapter.\n", + ConfigName->Buffer, + writeStatus + )); + } + } + +#endif // NT + + CloseIFConfig(Handle); + + // We've initialized our NTEs. Now get the adapter open, and go through + // again, calling DHCP if we need to. + + (*(BindInfo->lip_open))(BindInfo->lip_context); + + if (PrimaryNTE != NULL) { +#ifdef CHICAGO + NotifyInterfaceChange(PrimaryNTE->nte_context, TRUE); +#endif + } + + // Now walk through the NTEs we've added, and get addresses for them (or + // tell clients about them). This code assumes that no one else has mucked + // with the list while we're here. + for (i = 0; i < NumAddr; i++, NTE = NTE->nte_next) { + +// +// BUGBUG - Doesn't this send up a notification of zero for a DHCP'd +// address on chicago??? (mikemas, 2/5/96) +// +#ifdef SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, &(IF->if_configname), TRUE); + +#else // SECFLTR + + NotifyAddrChange(NTE->nte_addr, NTE->nte_mask, NTE->nte_pnpcontext, + NTE->nte_context, &NTE->nte_addrhandle, NULL, TRUE); + +#endif // SECFLTR + + if (IP_ADDR_EQUAL(NTE->nte_addr, NULL_IP_ADDR)) { + // Call DHCP to get an address for this guy. +#ifdef CHICAGO + RequestDHCPAddr(NTE->nte_context); +#endif + } else { + InitIGMPForNTE(NTE); + } + } + + + CTEFreeMem(AddrList); + return IP_SUCCESS; + +failure: + CloseIFConfig(Handle); + + if (AddrList != NULL) + CTEFreeMem(AddrList); + + return IP_GENERAL_FAILURE; +} + +extern uint BCastMinMTU; + + +//* IPDelNTE - Delete an active NTE +// +// Called to delete an active NTE from the system. The RouteTableLock +// must be acquired before calling this routine. It will be freed upon +// return. +// +// Input: NTE - A pointer to the network entry to delete. +// RouteTableHandle - A pointer to the lock handle for the +// route table lock, which the caller has +// acquired. +// +// Returns: Nothing +// +void +IPDelNTE(NetTableEntry *NTE, CTELockHandle *RouteTableHandle) +{ + Interface *IF = NTE->nte_if; + ReassemblyHeader *RH, *RHNext; + EchoControl *EC, *ECNext; + EchoRtn Rtn; + CTELockHandle Handle; + PNDIS_PACKET Packet; + PNDIS_BUFFER Buffer; + uchar *TDBuffer; + + + if (NTE->nte_flags & NTE_VALID) { + (void) IPpSetNTEAddr(NTE, NULL_IP_ADDR, NULL_IP_ADDR, RouteTableHandle, NULL, NULL); + + } else { + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + NotifyAddrChange(NULL_IP_ADDR, NULL_IP_ADDR, + NTE->nte_pnpcontext, NTE->nte_context, + &NTE->nte_addrhandle, NULL, FALSE); + } + + CTEGetLock(&RouteTableLock, RouteTableHandle); + + if (DHCPNTE == NTE) + DHCPNTE = NULL; + + NTE->nte_flags = 0; + + CTEFreeLock(&RouteTableLock, *RouteTableHandle); + + CTEStopTimer(&NTE->nte_timer); + + CTEGetLock(&NTE->nte_lock, &Handle); + + RH = NTE->nte_ralist; + NTE->nte_ralist = NULL; + EC = NTE->nte_echolist; + NTE->nte_echolist = NULL; + + CTEFreeLock(&NTE->nte_lock, Handle); + + // Free any reassembly resources. + while (RH != NULL) { + RHNext = RH->rh_next; + FreeRH(RH); + RH = RHNext; + } + + // Now free any pending echo requests. + while (EC != NULL) { + ECNext= EC->ec_next; + Rtn = (EchoRtn)EC->ec_rtn; + (*Rtn)(EC, IP_ADDR_DELETED, NULL, 0, NULL); + EC = ECNext; + } + + // + // Free the TD resource allocated for this NTE. + // + CTEGetLock(&(IF->if_lock), &Handle); + + Packet = IF->if_tdpacket; + + if (Packet != NULL) { + + IF->if_tdpacket = + ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link; + + CTEFreeLock(&(IF->if_lock), Handle); + + Buffer = Packet->Private.Head; + TDBuffer = NdisBufferVirtualAddress(Buffer); + NdisFreePacketPool(Packet->Private.Pool); + +#ifdef CHICAGO + NdisFreeBufferPool(Buffer->Pool); +#endif + CTEFreeMem(TDBuffer); + } + else { + CTEFreeLock(&(IF->if_lock), Handle); + } + + return; +} + + +//* IPDeleteDynamicNTE - Deletes a "dynamic" NTE. +// +// Called to delete a network entry which was dynamically created on an +// existing interface. +// +// Input: NTEContext - The context value identifying the NTE to delete. +// +// Returns: Nonzero if the operation succeeded. Zero if it failed. +// +uint +IPDeleteDynamicNTE(ushort NTEContext) +{ + NetTableEntry *NTE; + Interface *IF; + CTELockHandle Handle; + + + CTEGetLock(&RouteTableLock, &Handle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ( (NTE->nte_context == NTEContext) && + (NTE->nte_flags & NTE_DYNAMIC) && + (NTE->nte_flags & NTE_ACTIVE) + ) + { + CTEAssert(NTE != LoopNTE); + CTEAssert(!(NTE->nte_flags & NTE_PRIMARY)); + + IPDelNTE(NTE, &Handle); + + // + // Route table lock was freed by IPDelNTE + // + + return(TRUE); + } + } + + CTEFreeLock(&RouteTableLock, Handle); + + return(FALSE); +} + + +//* IPGetNTEInfo - Retrieve information about a network entry. +// +// Called to retrieve context information about a network entry. +// +// Input: NTEContext - The context value which identifies the NTE to query. +// +// Output: NTEInstance - The instance number associated with the NTE. +// Address - The address assigned to the NTE. +// SubnetMask - The subnet mask assigned to the NTE. +// NTEFlags - The flag values associated with the NTE. +// +// Returns: Nonzero if the operation succeeded. Zero if it failed. +// +uint +IPGetNTEInfo(ushort NTEContext, ulong *NTEInstance, IPAddr *Address, + IPMask *SubnetMask, ushort *NTEFlags) +{ + NetTableEntry *NTE; + CTELockHandle Handle; + uint retval = FALSE; + + + CTEGetLock(&RouteTableLock, &Handle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ((NTE->nte_context == NTEContext) && + (NTE->nte_flags & NTE_ACTIVE) + ) + { + *NTEInstance = NTE->nte_instance; + + if (NTE->nte_flags & NTE_VALID) { + *Address = NTE->nte_addr; + *SubnetMask = NTE->nte_mask; + } + else { + *Address = NULL_IP_ADDR; + *SubnetMask = NULL_IP_ADDR; + } + + *NTEFlags = NTE->nte_flags; + retval = TRUE; + } + } + + CTEFreeLock(&RouteTableLock, Handle); + + return(retval); +} + + +//* IPDelInterface - Delete an interface. +// +// Called when we need to delete an interface that's gone away. We'll walk +// the NTE list, looking for NTEs that are on the interface that's going +// away. For each of those, we'll invalidate the NTE, delete routes on it, +// and notify the upper layers that it's gone. When that's done we'll pull +// the interface out of the list and free the memory. +// +// Note that this code probably isn't MP safe. We'll need to fix that for +// the port to NT. +// +// Input: Context - Pointer to primary NTE on the interface. +// +// Returns: Nothing. +// +void +IPDelInterface(void *Context) +{ + NetTableEntry *NTE = (NetTableEntry *)Context; + NetTableEntry *FoundNTE = NULL; + Interface *IF, *PrevIF; + CTELockHandle Handle; + PNDIS_PACKET Packet; + PNDIS_BUFFER Buffer; + uchar *TDBuffer; + ReassemblyHeader *RH; + EchoControl *EC; + EchoRtn Rtn; + CTEBlockStruc Block; + + IF = NTE->nte_if; + + CTEGetLock(&RouteTableLock, &Handle); + + IF->if_flags |= IF_FLAGS_DELETING; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if (NTE->nte_if == IF) { + + if (FoundNTE == NULL) { + FoundNTE = NTE; + } + + // This guy is on the interface, and needs to be deleted. + IPDelNTE(NTE, &Handle); + + CTEGetLock(&RouteTableLock, &Handle); + } + } + + CTEFreeLock(&RouteTableLock, Handle); + + // Clear this index from the IFBitMask. + CTEAssert(IFBitMask[(IF->if_index-1)/BITS_PER_WORD] & (1 << ((IF->if_index - 1)%BITS_PER_WORD))); + + IFBitMask[(IF->if_index-1)/BITS_PER_WORD] &= ~(1 << ((IF->if_index - 1)%BITS_PER_WORD)); + + if (FoundNTE != NULL) { +#ifdef CHICAGO + NotifyInterfaceChange(FoundNTE->nte_context, FALSE); +#endif + } + + // + // Free the TD resources on the IF. + // + + while ((Packet = IF->if_tdpacket) != NULL) { + + IF->if_tdpacket = + ((TDContext *)Packet->ProtocolReserved)->tdc_common.pc_link; + + Buffer = Packet->Private.Head; + TDBuffer = NdisBufferVirtualAddress(Buffer); + NdisFreePacketPool(Packet->Private.Pool); + +#ifdef CHICAGO + NdisFreeBufferPool(Buffer->Pool); +#endif + CTEFreeMem(TDBuffer); + } + + // If this was the 'first' IF, set that to NULL and delete the broadcast + // route that goes through him. + if (FirstIF == IF) { + DeleteRoute(IP_LOCAL_BCST, HOST_MASK, IPADDR_LOCAL, + FirstIF); + DeleteRoute(IP_ZERO_BCST, HOST_MASK, IPADDR_LOCAL, + FirstIF); + FirstIF = NULL; + BCastMinMTU = 0xffff; + } + + // OK, we've cleaned up all the routes through this guy. + // Get ready to block waiting for all reference to go + // away, then dereference our reference. After this, go + // ahead and try to block. Mostly likely our reference was + // the last one, so we won't block - we'll wake up immediately. + CTEInitBlockStruc(&Block); + IF->if_block = &Block; + + DerefIF(IF); + + (void)CTEBlock(&Block); + + // OK, we've cleaned up all references, so there shouldn't be + // any more transmits pending through this interface. Close the + // adapter to force synchronization with any receives in process. + + + (*(IF->if_close))(IF->if_lcontext); + + // Now walk the IFList, looking for this guy. When we find him, free him. + PrevIF = STRUCT_OF(Interface, &IFList, if_next); + while (PrevIF->if_next != IF && PrevIF->if_next != NULL) + PrevIF = PrevIF->if_next; + + if (PrevIF->if_next != NULL) { + PrevIF->if_next = IF->if_next; + NumIF--; + CTEFreeMem(IF); + } else + CTEAssert(FALSE); + + // If we've deleted the first interface but still have other valid + // interfaces, we need to create a new FirstIF and read broadcast routes + // through it. NumIF is always at least one because of the loopback + // interface. + if (FirstIF == NULL && NumIF != 1) { + + FirstIF = IFList; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + if ((NTE->nte_flags & NTE_VALID) && NTE != LoopNTE) { + BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss); + AddRoute(NTE->nte_if->if_bcast, HOST_MASK, IPADDR_LOCAL, + FirstIF, BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, + NULL); + } + } + } + +} + + +#else // _PNP_POWER + + +//* NotifyAddrChange - Notify clients of a change in addresses. +// +// Called when we want to notify registered clients that an address has come +// or gone. We call TDI to perform this function. +// +// Input: Addr - Addr that has changed. +// Mask - Ignored - Mask that has changed. +// Context - Ignored - PNP context for address +// IPContext - NTE context for NTE +// Handle - Pointer to where to get/set address registration +// handle +// ConfigName - Registry name to use to retrieve config info. +// Added - True if the addr is coming, False if it's going. +// +// Returns: Nothing. +// +void +NotifyAddrChange(IPAddr Addr, IPMask Mask, void *Context, ushort IPContext, + PVOID *Handle, PNDIS_STRING ConfigName, uint Added) + +{ + IP_STATUS StatusType; + NDIS_HANDLE ConfigHandle = NULL; + int i; + ULStatusProc StatProc; + + + if (Added) { + StatusType = IP_ADDR_ADDED; + +#ifdef SECFLTR + // + // Open a configuration key + // + if (!OpenIFConfig(ConfigName, &ConfigHandle)) { + // + // Not much we can do. The transports will have + // to handle this. + // + CTEAssert(ConfigHandle == NULL); + } +#endif // SECFLTR + + } + else { + StatusType = IP_ADDR_DELETED; + } + + for ( i = 0; i < NextPI; i++) { + StatProc = IPProtInfo[i].pi_status; + if (StatProc != NULL) + (*StatProc)(IP_HW_STATUS, StatusType, Addr, NULL_IP_ADDR, + NULL_IP_ADDR, 0, ConfigHandle ); + } + +#ifdef SECFLTR + + if (ConfigHandle != NULL) { + CloseIFConfig(ConfigHandle); + } + +#endif // SECFLTR + +} + + +#endif // _PNP_POWER + + +#pragma BEGIN_INIT + +//** ipinit - Initialize ourselves. +// +// This routine is called during initialization from the OS-specific +// init code. We need to check for the presence of the common xport +// environment first. +// +// +// Entry: Nothing. +// +// Returns: 0 if initialization failed, non-zero if it succeeds. +// +int +IPInit() +{ + IPConfigInfo *ci; // Pointer to our IP configuration info. + int numnets; // Number of nets active. + int i; + uint j; // Counter variables. + NetTableEntry *nt; // Pointer to current NTE. + LLIPBindInfo *ARPInfo; // Info. returned from ARP. + NDIS_STATUS Status; + Interface *NetInterface; // Interface for a particular net. + LLIPRegRtn RegPtr; + NetTableEntry *lastNTE; + + + if (!CTEInitialize()) + return IP_INIT_FAILURE; + + CTERefillMem(); + + if ((ci = IPGetConfig()) == NULL) + return IP_INIT_FAILURE; + +#ifndef _PNP_POWER + MaxIPNets = ci->ici_numnets + 1; +#endif // _PNP_POWER + + for (ATCIndex=0; ATCIndex < ATC_SIZE; ATCIndex++) { + ATCache[ATCIndex].atc_flags = 0; + } + ATCIndex = 0; + + // First, initalize our loopback stuff. + NetTableList = InitLoopback(ci); + if (NetTableList == NULL) + return IP_INIT_FAILURE; + + if (!ARPInit()) { + CTEFreeMem(NetTableList); + return IP_INIT_FAILURE; // Couldn't initialize ARP. + } + + CTERefillMem(); + if (!InitRouting(ci)) { + CTEFreeMem(NetTableList); + return IP_INIT_FAILURE; + } + + RATimeout = DEFAULT_RA_TIMEOUT; +#if 0 + CTEInitLock(&PILock); +#endif + LastPI = IPProtInfo; + + + if (!ci->ici_gateway) + InterfaceSize = sizeof(Interface); + else + InterfaceSize = sizeof(RouteInterface); + + DeadGWDetect = ci->ici_deadgwdetect; + PMTUDiscovery = ci->ici_pmtudiscovery; + IGMPLevel = ci->ici_igmplevel; + DefaultTTL = MIN(ci->ici_ttl, 255); + DefaultTOS = ci->ici_tos & 0xfc; + if (IGMPLevel > 2) + IGMPLevel = 0; + + InitTimestamp(); + +#ifndef _PNP_POWER + numnets = ci->ici_numnets; + + lastNTE = NetTableList; // loopback is only one on the list + CTEAssert(lastNTE != NULL); + CTEAssert(lastNTE->nte_next == NULL); + + // Loop through the config. info, copying the addresses and masks. + for (i = 0; i < numnets; i++) { + + CTERefillMem(); + nt = CTEAllocMem(sizeof(NetTableEntry)); + if (nt == NULL) + continue; + + CTEMemSet(nt, 0, sizeof(NetTableEntry)); + + nt->nte_addr = net_long(ci->ici_netinfo[i].nci_addr); + nt->nte_mask = net_long(ci->ici_netinfo[i].nci_mask); + nt->nte_mss = MAX(ci->ici_netinfo[i].nci_mtu, 68); + nt->nte_flags = (IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR) ? 0 : + NTE_VALID); + nt->nte_flags |= NTE_ACTIVE; + + CTEInitLock(&nt->nte_lock); + // If the address is invalid, skip it. + if (CLASSD_ADDR(nt->nte_addr) || CLASSE_ADDR(nt->nte_addr)) { + CTEFreeMem(nt); + continue; + } + + // See if we're already bound to this adapter. If we are, use the same + // interface. Otherwise assign a new one. We assume that the loopback + // interface is IF 1, so there is one less than NumIF in the table. + for (j = 0; j < NumIF - 1; j++) { + if (CTEEqualString(&(AdptNameTable[j].nm_name), + &(ci->ici_netinfo[i].nci_name))) { + + // Names match. Now check driver/types. + if (((ci->ici_netinfo[i].nci_type == NET_TYPE_LAN) && + (AdptNameTable[j].nm_driver.Buffer == NULL)) || + (CTEEqualString(&(AdptNameTable[j].nm_driver), + &(ci->ici_netinfo[i].nci_driver)))) + break; // Found a match + } + } + + if (j < (NumIF - 1)) { + + // Found a match above, so use that interface. + CTERefillMem(); + nt->nte_if = AdptNameTable[j].nm_interface; + ARPInfo = AdptNameTable[j].nm_arpinfo; + // If the Init of the interface or the NTE fails, we don't want to + // close the interface, because another net is using it. + + if (!InitInterface(nt)) { + CTEFreeMem(nt); + continue; + } + if (!InitNTE(nt)) { + CTEFreeMem(nt); + continue; + } + + } else { // No match, create a new interface + + CTEAssert(NumIF <= MaxIPNets); + + if (NumIF == MaxIPNets) { + continue; // too many adapters + } + + CTERefillMem(); + + ARPInfo = CTEAllocMem(sizeof(LLIPBindInfo)); + + if (ARPInfo == NULL) { + CTEFreeMem(nt); + continue; + } + + NetInterface = CTEAllocMem( + InterfaceSize + + ci->ici_netinfo[i].nci_configname.MaximumLength + ); + + if (!NetInterface) { + CTEFreeMem(ARPInfo); + CTEFreeMem(nt); + continue; + } + + CTEMemSet(NetInterface, 0, InterfaceSize); + + nt->nte_if = NetInterface; + nt->nte_flags |= NTE_PRIMARY; // He is the primary NTE. + + CTEInitLock(&NetInterface->if_lock); + + if (ci->ici_gateway) { + // Hack in the max pending value here. Probably should be + // done in iproute.c, but it's easier to do it here. + + RouteInterface *RtIF; + + RtIF = (RouteInterface *)NetInterface; + RtIF->ri_q.rsq_maxpending = ci->ici_netinfo[i].nci_maxpending; + } + + // If this is a LAN, register with ARP. + if (ci->ici_netinfo[i].nci_type == NET_TYPE_LAN) + RegPtr = ARPRegister; + else + RegPtr = FindRegPtr(&ci->ici_netinfo[i].nci_driver); + + if (RegPtr == NULL || !((*RegPtr)(&ci->ici_netinfo[i].nci_name, + nt, IPRcv, IPSendComplete, IPStatus, IPTDComplete, + IPRcvComplete, ARPInfo, NumIF))) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; // We're hosed, skip this net. + } + else { + + if (ci->ici_netinfo[i].nci_zerobcast) + NetInterface->if_bcast = IP_ZERO_BCST; + else + NetInterface->if_bcast = IP_LOCAL_BCST; + + NetInterface->if_xmit = ARPInfo->lip_transmit; + NetInterface->if_transfer = ARPInfo->lip_transfer; + NetInterface->if_close = ARPInfo->lip_close; + NetInterface->if_invalidate = ARPInfo->lip_invalidate; + NetInterface->if_lcontext = ARPInfo->lip_context; + NetInterface->if_addaddr = ARPInfo->lip_addaddr; + NetInterface->if_deladdr = ARPInfo->lip_deladdr; + NetInterface->if_qinfo = ARPInfo->lip_qinfo; + NetInterface->if_setinfo = ARPInfo->lip_setinfo; + NetInterface->if_getelist = ARPInfo->lip_getelist; + NetInterface->if_tdpacket = NULL; + NetInterface->if_index = ARPInfo->lip_index; + NetInterface->if_mtu = ARPInfo->lip_mss - sizeof(IPHeader); + NetInterface->if_speed = ARPInfo->lip_speed; + NetInterface->if_flags = ARPInfo->lip_flags & LIP_P2P_FLAG ? + IF_FLAGS_P2P : 0; + NetInterface->if_addrlen = ARPInfo->lip_addrlen; + NetInterface->if_addr = ARPInfo->lip_addr; + NetInterface->if_pnpcontext = PNPContext; + NetInterface->if_llipflags = ArpInfo->lip_flags + + NetInterface->if_configname.Buffer = + (PVOID) (((uchar *)NetInterface) + InterfaceSize); + + NetInterface->if_configname.Length = 0; + NetInterface->if_configname.MaximumLength = + ci->ici_netinfo[i].nci_configname.MaximumLength; + + CTECopyString( + &(NetInterface->if_configname), + &(ci->ici_netinfo[i].nci_configname) + ); + + CTERefillMem(); + + if (!InitInterface(nt)) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + + if (!InitNTE(nt)) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + + CTERefillMem(); + if (!CTEAllocateString(&AdptNameTable[j].nm_name, + CTELengthString(&ci->ici_netinfo[i].nci_name))) { + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + + if (ci->ici_netinfo[i].nci_type != NET_TYPE_LAN) { + if (!CTEAllocateString(&AdptNameTable[j].nm_driver, + CTELengthString(&ci->ici_netinfo[i].nci_driver))) { + CTEFreeString(&AdptNameTable[j].nm_name); + CTEFreeMem(ARPInfo); + CTEFreeMem(NetInterface); + CTEFreeMem(nt); + continue; + } + CTECopyString(&(AdptNameTable[j].nm_driver), + &(ci->ici_netinfo[i].nci_driver)); + } + + CTECopyString(&(AdptNameTable[j].nm_name), + &(ci->ici_netinfo[i].nci_name)); + AdptNameTable[j].nm_interface = NetInterface; + AdptNameTable[j].nm_arpinfo = ARPInfo; + NetInterface->if_next = IFList; + IFList = NetInterface; + if (FirstIF == NULL) + FirstIF = NetInterface; + NumIF++; + +#ifdef NT + // + // Write the interface context to the registry for DHCP et al + // + if (ci->ici_netinfo[i].nci_reghandle != NULL) { + NTSTATUS writeStatus; + ulong context = (ulong) nt->nte_context; + + writeStatus = SetRegDWORDValue( + ci->ici_netinfo[i].nci_reghandle, + L"IPInterfaceContext", + &context + ); + + if (!NT_SUCCESS(writeStatus)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 2, + 1, + &(ci->ici_netinfo[i].nci_name.Buffer), + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to write IPInterfaceContext value for adapter %ws\n" + " (status %lx). DHCP will be unable to configure this \n" + " adapter.\n", + ci->ici_netinfo[i].nci_name.Buffer, + writeStatus + )); + } + } +#endif // NT + } + } + + nt->nte_next = NULL; + lastNTE->nte_next = nt; + lastNTE = nt; + NumNTE++; + + if (!InitNTERouting(nt, ci->ici_netinfo[i].nci_numgws, + ci->ici_netinfo[i].nci_gw)) { + // Couldn't add the routes for this NTE. Mark has as not valid. + // Probably should log an event here. + if (nt->nte_flags & NTE_VALID) { + nt->nte_flags &= ~NTE_VALID; + nt->nte_if->if_ntecount--; + } + } + +#ifdef NT + if (!IP_ADDR_EQUAL(nt->nte_addr, NULL_IP_ADDR)) { + SetPersistentRoutesForNTE( + net_long(nt->nte_addr), + net_long(nt->nte_mask), + nt->nte_if->if_index + ); + } +#endif // NT + + } + +#endif // ndef PNP_POWER + + if (NumNTE != 0) { // We have an NTE, and loopback initialized. + PNDIS_PACKET Packet; + +#ifdef _PNP_POWER + for (i=0; iici_gateway ? IP_FORWARDING : + IP_NOT_FORWARDING); + IPSInfo.ipsi_defaultttl = DefaultTTL; + IPSInfo.ipsi_reasmtimeout = DEFAULT_RA_TIMEOUT; + + // Allocate our packet pools. + CTEInitLock(&HeaderLock); +#ifdef NT + ExInitializeSListHead(&PacketList); + ExInitializeSListHead(&HdrBufList); +#endif + + + Packet = GrowIPPacketList(); + + if (Packet == NULL) { + CloseNets(); + FreeNets(); + IPFreeConfig(ci); + return IP_INIT_FAILURE; + } + + (void)FreeIPPacket(Packet); + + NdisAllocateBufferPool(&Status, &BufferPool, NUM_IP_NONHDR_BUFFERS); + if (Status != NDIS_STATUS_SUCCESS) { +#ifdef DEBUG + DEBUGCHK; +#endif + } + + CTERefillMem(); + + ICMPInit(DEFAULT_ICMP_BUFFERS); + if (!IGMPInit()) + IGMPLevel = 1; + + // Should check error code, and log an event here if this fails. + CTERefillMem(); + InitGateway(ci); + + IPFreeConfig(ci); + CTERefillMem(); + +#ifndef _PNP_POWER + OpenAdapters(); + CleanAdaptTable(); // Clean up the adapter info we don't need. +#endif + + CTERefillMem(); + + // Loop through, initialize IGMP for each NTE. + for (nt = NetTableList; nt != NULL; nt = nt->nte_next) + InitIGMPForNTE(nt); + + return IP_INIT_SUCCESS; + } + else { + FreeNets(); + IPFreeConfig(ci); + return IP_INIT_FAILURE; // Couldn't initialize anything. + } +} + +#pragma END_INIT + + diff --git a/private/ntos/tdi/tcpip/ip/ipdef.h b/private/ntos/tdi/tcpip/ip/ipdef.h new file mode 100644 index 000000000..9d93d0616 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipdef.h @@ -0,0 +1,371 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ +#include "ipfilter.h" + +//** IPDEF.H - IP private definitions. +// +// This file contains all of the definitions for IP that +// are private to IP, i.e. not visible to outside layers. + +// Internal error codes, not seen by IP uses. +#define IP_OPTION_STRICT (MAX_IP_STATUS+1) + +#define CLASSA_ADDR(a) (( (*((uchar *)&(a))) & 0x80) == 0) +#define CLASSB_ADDR(a) (( (*((uchar *)&(a))) & 0xc0) == 0x80) +#define CLASSC_ADDR(a) (( (*((uchar *)&(a))) & 0xe0) == 0xc0) +#define CLASSE_ADDR(a) ((( (*((uchar *)&(a))) & 0xf0) == 0xf0) && \ + ((a) != 0xffffffff)) + +#define CLASSA_MASK 0x000000ff +#define CLASSB_MASK 0x0000ffff +#define CLASSC_MASK 0x00ffffff +#define CLASSD_MASK 0x000000e0 +#define CLASSE_MASK 0xffffffff + +#define IP_OPT_COPIED 0x80 // Bit indicating options is to be copied. +#define IP_OPT_TYPE 0 +#define IP_OPT_LENGTH 1 +#define IP_OPT_DATA 2 +#define IP_OPT_PTR 2 // Pointer offset, for those options that have it. +#define IP_TS_OVFLAGS 3 // Offset for overflow and flags. +#define IP_TS_FLMASK 0xf // Mask for flags +#define IP_TS_OVMASK 0xf0 // Mask for overflow field. +#define IP_TS_MAXOV 0xf0 // Maximum value for the overflow field. +#define IP_TS_INC 0x10 // Increment used on overflow field. + +#define MIN_RT_PTR 4 +#define MIN_TS_PTR 5 + +#define TS_REC_TS 0 // Record TS option. +#define TS_REC_ADDR 1 // Record TS and address. +#define TS_REC_SPEC 3 // Only specified addresses record. + +#define OPT_SSRR 1 // We've seen a SSRR in this option buffer +#define OPT_LSRR 2 // We've seen a LSRR in this option buffer +#define OPT_RR 4 // We've seen a RR +#define OPT_TS 8 // We've seen a TS. + +#define MAX_OPT_SIZE 40 + +#define ALL_ROUTER_MCAST 0x020000E0 + +// Received option index structure. +struct OptIndex { + uchar oi_srindex; + uchar oi_rrindex; + uchar oi_tsindex; + uchar oi_srtype; +}; /* OptIndex */ + +typedef struct OptIndex OptIndex; + +#define MAX_HDR_SIZE (sizeof(IPHeader) + MAX_OPT_SIZE) + +#define DEFAULT_VERLEN 0x45 // Default version and length. + +#define IP_VERSION 0x40 +#define IP_VER_FLAG 0xF0 + +#define IP_RSVD_FLAG 0x0080 // Reserved. +#define IP_DF_FLAG 0x0040 // 'Don't fragment' flag +#define IP_MF_FLAG 0x0020 // 'More fragments flag' + + +#define IP_OFFSET_MASK ~0x00E0 // Mask for extracting offset field. + +typedef IP_STATUS (*ULRcvProc)(void *, IPAddr, IPAddr, IPAddr, IPAddr, + IPHeader UNALIGNED *, uint, IPRcvBuf *, uint, + uchar, uchar, IPOptInfo *); + +typedef uint (*ULStatusProc)(uchar, IP_STATUS, IPAddr, IPAddr, IPAddr, ulong, void *); + +//* Protocol information structure. These is one of there for each protocol bound +// to an NTE. +struct ProtInfo { + void (*pi_xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine. + ULRcvProc pi_rcv; // Pointer to receive routine. + ULStatusProc pi_status; // Pointer to status handler. + void (*pi_rcvcmplt)(void); // Pointer to recv. cmplt handler. + uchar pi_protocol; // Protocol type. + uchar pi_pad[3]; // Pad to dword +}; /* ProtInfo */ + +typedef struct ProtInfo ProtInfo; + +//* Per-net information. We keep a variety of information for +// each net, including the IP address, subnet mask, and reassembly +// information. + +#define MAX_IP_PROT 5 // ICMP, IGMP, TCP, UDP, & Raw + +struct IPRtrEntry { + struct IPRtrEntry *ire_next; + IPAddr ire_addr; + long ire_preference; + ushort ire_lifetime; + ushort ire_pad; +}; /* IPRtrEntry */ + +typedef struct IPRtrEntry IPRtrEntry; + +struct NetTableEntry { + struct NetTableEntry *nte_next; // Next NTE of I/F. + IPAddr nte_addr; // IP address for this net. + IPMask nte_mask; // Subnet mask for this net. + struct Interface *nte_if; // Pointer to interface for this net. + struct NetTableEntry *nte_ifnext; // Linkage on if chain. + ushort nte_flags; // Flags for NTE. + ushort nte_context; // Context passed to upper layers. + ulong nte_instance; // Unique instance ID for this net + void *nte_pnpcontext; // PNP context. + DEFINE_LOCK_STRUCTURE(nte_lock) + struct ReassemblyHeader *nte_ralist; // Reassembly list. + struct EchoControl *nte_echolist; // List of pending echo control blocks + CTETimer nte_timer; // Timer for this net. + ushort nte_mss; + ushort nte_icmpseq; // ICMP seq. # + struct IGMPAddr *nte_igmplist; // List of mcast addresses. +#ifdef _PNP_POWER + void *nte_addrhandle; // Handle for address registration. +#endif + IPAddr nte_rtrdiscaddr; // Address used for Router Discovery + uchar nte_rtrdiscstate; // state of router solicitations + uchar nte_rtrdisccount; // router solicitation count + uchar nte_rtrdiscovery; + IPRtrEntry *nte_rtrlist; + +}; /* NetTableEntry */ + +typedef struct NetTableEntry NetTableEntry; + +#define NTE_VALID 0x0001 // NTE is valid. +#define NTE_COPY 0x0002 // For NDIS copy lookahead stuff. +#define NTE_PRIMARY 0x0004 // This is the 'primary' NTE on the I/F. +#define NTE_ACTIVE 0x0008 // NTE is active, i.e. interface is valid. +#define NTE_DYNAMIC 0x0010 // NTE is was created dynamically + +#define IP_TIMEOUT 500 + +#define NTE_RTRDISC_UNINIT 0 +#define NTE_RTRDISC_DELAYING 1 +#define NTE_RTRDISC_SOLICITING 2 + +#define MAX_SOLICITATION_DELAY 2 // ticks to delay +#define SOLICITATION_INTERVAL 6 // ticks between solicitations +#define MAX_SOLICITATIONS 3 // number of solicitations + +struct AddrTypeCache { + IPAddr atc_addr; // IP Addr of cache entry + uchar atc_flags; // Valid flag + uchar atc_type; // Addr Type +}; + +typedef struct AddrTypeCache AddrTypeCache; + +#define ATC_SIZE 8 +#define ATC_MASK 7 // mask used to make sure indexes are less than ATC_SIZE + +//* Buffer reference structure. Used by broadcast and fragmentation code to +// track multiple references to a single user buffer. +struct BufferReference { + PNDIS_BUFFER br_buffer; // Pointer to uses buffer. + DEFINE_LOCK_STRUCTURE(br_lock) + int br_refcount; // Count of references to user's buffer. +}; /* BufferReference */ + +typedef struct BufferReference BufferReference; + +// Definitions of flags in pc_flags field +#define PACKET_FLAG_OPTIONS 1 // Set if packet has an options buffer. +#define PACKET_FLAG_IPBUF 2 // Set if packet is composed of IP buffers. +#define PACKET_FLAG_RA 4 // Set if packet is being used for reassembly. +#define PACKET_FLAG_FW 8 // Set if packet is a forwarding packet. +#define PACKET_FLAG_IPHDR 0x10 // Packet uses an IP hdr buffer. + +//* Transfer data packet context. Used when TD'ing a packet - we store information for the +// callback here. +struct TDContext { + struct PCCommon tdc_common; + void *tdc_buffer; // Pointer to buffer containing data. + NetTableEntry *tdc_nte; // NTE to receive this on. + struct RABufDesc *tdc_rbd; // Pointer to RBD, if any. + uchar tdc_dtype; // Destination type of original address. + uchar tdc_hlength; // Length in bytes of header. + uchar tdc_pad[2]; + uchar tdc_header[MAX_HDR_SIZE + 8]; +}; /* TDContext */ + +typedef struct TDContext TDContext; + +//* Information about net interfaces. There can be multiple nets for each interface, +// but there is exactly one interface per net. + +struct Interface { + struct Interface *if_next; // Next interface in chain. + void *if_lcontext; // Link layer context. + NDIS_STATUS (*if_xmit)(void *, PNDIS_PACKET, IPAddr, RouteCacheEntry *); + NDIS_STATUS (*if_transfer)(void *, NDIS_HANDLE, uint, uint, uint, PNDIS_PACKET, + uint *); + void (*if_close)(void *); + void (*if_invalidate)(void *, RouteCacheEntry *); + uint (*if_addaddr)(void *, uint, IPAddr, IPMask, void *); + void (*if_deladdr)(void *, uint, IPAddr, IPMask); + int (*if_qinfo)(void *, struct TDIObjectID *, + PNDIS_BUFFER, uint *, void *); + int (*if_setinfo)(void *, struct TDIObjectID *, void *, + uint); + int (*if_getelist)(void *, void *, uint *); + PNDIS_PACKET if_tdpacket; // Packet used for transferring data. + uint if_index; // Index of this interface. + uint if_ntecount; // Valid NTEs on this interface. + NetTableEntry *if_nte; // Pointer to list of NTE on interface. + IPAddr if_bcast; // Broadcast address for this interface. + uint if_mtu; // True maximum MTU for the interface. + uint if_speed; // Speed in bits/sec of this interface. + uint if_flags; // Flags for this interface. + INTERFACE_CONTEXT if_filtercontext; // Filter context for this i/f. + uint if_addrlen; // Length of i/f addr. + uchar *if_addr; // Pointer to addr. + + uint IgmpVersion; //igmp version active on this interface + uint IgmpVer1Timeout; //Version 1 router present timeout +#ifdef _PNP_POWER + uint if_refcount; // Reference count for this i/f. + CTEBlockStruc *if_block; // Block structure for PnP. + void *if_pnpcontext; // Context to pass to upper layers. +#endif // _PNP_POWER + + uint if_llipflags; // Lower layer flags +#ifdef SECFLTR + NDIS_STRING if_configname; // Name of the i/f config section +#endif //SECFLTR + DEFINE_LOCK_STRUCTURE(if_lock) +}; /* Interface */ + +typedef struct Interface Interface; + +/*NOINC*/ +extern void DerefIF(Interface *IF); +/*INC*/ + +#define IF_FLAGS_P2P 1 // Point to point interface +#define IF_FLAGS_DELETING 2 // Interface is in the process of going + // away. + +// Structure of a reassembly buffer descriptor. Each RBD describes a fragment of the total +// datagram +struct RABufDesc { + IPRcvBuf rbd_buf; // IP receive buffer for this fragment. + ushort rbd_start; // Offset of first byte of this fragment. + ushort rbd_end; // Offset of last byte of this fragment. +}; /* RABufDesc */ + +typedef struct RABufDesc RABufDesc; + +// Reassembly header. The includes the information needed for the lookup, as well as space +// for the received header and a chain of reassembly buffer descriptors. +struct ReassemblyHeader { + struct ReassemblyHeader *rh_next; // Next header in chain. + IPAddr rh_dest; // Destination address of fragment. + IPAddr rh_src; // Source address of fragment. + ushort rh_id; // ID of datagram. + uchar rh_protocol; // Protocol of datagram. + uchar rh_ttl; // Remaining time of datagram. + RABufDesc *rh_rbd; // Chain of RBDs for this datagram. + ushort rh_datasize; // Total size of data. + ushort rh_datarcvd; // Amount of data received so far. + uint rh_headersize; // Size in bytes of header. + uchar rh_header[MAX_HDR_SIZE+8]; // Saved IP header of first fragment. +}; /* ReassemblyHeader */ + +typedef struct ReassemblyHeader ReassemblyHeader; + +// ICMP type and code definitions +#define IP_DEST_UNREACH_BASE IP_DEST_NET_UNREACHABLE + +#define ICMP_REDIRECT 5 // Redirect +#define ADDR_MASK_REQUEST 17 // Address mask request +#define ADDR_MASK_REPLY 18 +#define ICMP_DEST_UNREACH 3 // Destination unreachable +#define ICMP_TIME_EXCEED 11 // Time exceeded during reassembly +#define ICMP_PARAM_PROBLEM 12 // Parameter problem +#define ICMP_SOURCE_QUENCH 4 // Source quench +#define ICMP_ROUTER_ADVERTISEMENT 9 // Router Advertisement +#define ICMP_ROUTER_SOLICITATION 10 // Router Solicitation + +#define NET_UNREACH 0 +#define HOST_UNREACH 1 +#define PROT_UNREACH 2 +#define PORT_UNREACH 3 +#define FRAG_NEEDED 4 +#define SR_FAILED 5 +#define DEST_NET_UNKNOWN 6 +#define DEST_HOST_UNKNOWN 7 +#define SRC_ISOLATED 8 +#define DEST_NET_ADMIN 9 +#define DEST_HOST_ADMIN 10 +#define NET_UNREACH_TOS 11 +#define HOST_UNREACH_TOS 12 + +#define TTL_IN_TRANSIT 0 // TTL expired in transit +#define TTL_IN_REASSEM 1 // Time exceeded in reassembly + + +#define PTR_VALID 0 +#define REQ_OPTION_MISSING 1 + +#define REDIRECT_NET 0 +#define REDIRECT_HOST 1 +#define REDIRECT_NET_TOS 2 +#define REDIRECT_HOST_TOS 3 + +extern uint DHCPActivityCount; + +extern IP_STATUS SetIFContext(uint Index, INTERFACE_CONTEXT *Context); + +extern IP_STATUS SetFilterPtr(IPPacketFilterPtr FilterPtr); + +extern IP_STATUS SetMapRoutePtr(IPMapRouteToInterfacePtr MapRoutePtr); + +#ifdef NT + +#ifdef POOL_TAGGING + +#ifdef ExAllocatePool +#undef ExAllocatePool +#endif + +#define ExAllocatePool(type, size) ExAllocatePoolWithTag(type, size, 'iPCT') + +#ifndef CTEAllocMem +#error "CTEAllocMem is not already defined - will override tagging" +#else +#undef CTEAllocMem +#endif + +#define CTEAllocMem(size) ExAllocatePoolWithTag(NonPagedPool, size, 'iPCT') + +#endif // POOL_TAGGING + +// +// Use the TCP core checksum routine. +// + +ULONG +tcpxsum ( + IN ULONG Checksum, + IN PUCHAR Source, + IN ULONG Length + ); + +#define xsum(Buffer, Length) ((ushort) tcpxsum(0, (PUCHAR) (Buffer), (Length))) + +#else // NT + +extern ushort xsum(void *, int); + +#endif // NT + diff --git a/private/ntos/tdi/tcpip/ip/ipinit.h b/private/ntos/tdi/tcpip/ip/ipinit.h new file mode 100644 index 000000000..d1d730915 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipinit.h @@ -0,0 +1,155 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IPINIT.H - IP initialization definitions. +// +// This file contains all of the definitions for IP that are +// init. time specific. + +#define IP_INIT_FAILURE 0 // If we fail. +#define IP_INIT_SUCCESS 1 +#define CFG_REQUIRED 1 +#define CFG_OPTIONAL 0 + + +#define NET_TYPE_LAN 0 // The local net interface is a LAN. +#define NET_TYPE_WAN 1 // Point to point or other non-LAN network. +#define DEFAULT_TTL 128 +#define DEFAULT_TOS 0 + +#define MAX_DEFAULT_GWS 5 // Maximum number of default gateways per net. +#define MAX_NAME_SIZE 32 // Maximum length of an adapter name. + +#define DEFAULT_FW_PACKETS 50 // Default number of packets for forwarding. +#define DEFAULT_FW_BUFSIZE 74240 // Enough for 50 1480-byte Ethernet packets, + // rounded up to a multiple of 256. + +#define DEFAULT_MAX_FW_PACKETS 0xffffffff +#define DEFAULT_MAX_FW_BUFSIZE 0xffffffff + +#define DEFAULT_MAX_PENDING 5000 + +#define TR_RII_ALL 0x80 +#define TR_RII_SINGLE 0xC0 + +#define DEFAULT_ARP_CACHE_LIFE (2L*60L) // 2 minutes + +#ifndef _PNP_POWER + +//* Per net config. information. +struct NetConfigInfo { + IPAddr nci_addr; // IPAddr for this net. + IPMask nci_mask; // Net mask for this net. + uint nci_type; // Type of this net - Enet, TR, SLIP., etc. + NDIS_STRING nci_driver; // Device name for lower layer driver. + // Unused for NET_TYPE_LAN. + NDIS_STRING nci_name; // Name of adapter for this net. + +#ifdef SECFLTR + NDIS_STRING nci_configname; // Name of config section in registry. +#endif // SECFLTR + + uint nci_zerobcast; // Type of broadcast to be used on this net. + +#ifdef NT + HANDLE nci_reghandle; // Open handle to the registry key for + // this adapter. +#endif // NT + + uint nci_mtu; // Max MSS for this net. + uint nci_maxpending; // Max routing packets pending. + uint nci_numgws; // Number of default gateways for this interface. + IPAddr nci_gw[MAX_DEFAULT_GWS]; // Array of IPaddresses for gateways + uint nci_rtrdiscovery; // Router discovery enabled + IPAddr nci_rtrdiscaddr; // Multicast or BCast? +}; /* NetConfigInfo */ + +typedef struct NetConfigInfo NetConfigInfo; + + +#else // _PNP_POWER + +/*NOINC*/ + + +// Per-net config structures for Chicago. +typedef struct IFGeneralConfig { + uint igc_zerobcast; // Type of broadcast to be used on this net. + uint igc_mtu; // Max MSS for this net. + uint igc_maxpending; // Max FW pending on this IF. + uint igc_numgws; // Number of default gateways for this + // interface. + IPAddr igc_gw[MAX_DEFAULT_GWS]; // Array of IPaddresses for gateways + uint igc_rtrdiscovery; // Router discovery enabled + IPAddr igc_rtrdiscaddr; // Multicast or BCast? +} IFGeneralConfig; + +typedef struct IFAddrList { + IPAddr ial_addr; // Address for this interface. + IPMask ial_mask; // Mask to go with this. +} IFAddrList; + + +/*INC*/ + +#endif // _PNP_POWER + +//* Structure of configuration information. A pointer to this information +// is returned from a system-specific config. information routine. +struct IPConfigInfo { + uint ici_gateway; // 1 if we are a gateway, 0 otherwise + uint ici_fwbcast; // 1 if bcasts should be forwarded. Else 0. + uint ici_fwbufsize; // Total size of FW buf size. + uint ici_fwpackets; // Total number of FW packets to have. + uint ici_maxfwbufsize; // Maximum size of FW buffer. + uint ici_maxfwpackets; // Maximum number of FW packets. + uint ici_deadgwdetect; // True if we're doing dead GW detection. + uint ici_pmtudiscovery; // True if we're doing Path MTU discovery. + uint ici_igmplevel; // Level of IGMP we're doing. + uint ici_ttl; // Default TTL. + uint ici_tos; // Default TOS; + +#ifndef _PNP_POWER + int ici_numnets; // Number of nets present. + struct NetConfigInfo *ici_netinfo; // Per net config. info +#endif // _PNP_POWER + +}; /* IPConfigInfo */ + +typedef struct IPConfigInfo IPConfigInfo; + + +#ifndef _PNP_POWER + +struct NameMapping { + NDIS_STRING nm_driver; + NDIS_STRING nm_name; + void *nm_interface; + void *nm_arpinfo; +}; /* NameMapping */ + +typedef struct NameMapping NameMapping; + +struct DriverRegMapping { + NDIS_STRING drm_driver; + void *drm_regptr; +}; /* DriverRegMapping */ + +typedef struct DriverRegMapping DriverRegMapping; + +#endif // _PNP_POWER +extern uchar TrRii; + + +struct SetAddrControl { + void *sac_rtn; // Pointer to routine to call when completing request. +}; /* SetAddrControl */ + +/*NOINC*/ +typedef struct SetAddrControl SetAddrControl; +typedef void (*SetAddrRtn)(void *, IP_STATUS); +/*INC*/ + diff --git a/private/ntos/tdi/tcpip/ip/iploop.c b/private/ntos/tdi/tcpip/ip/iploop.c new file mode 100644 index 000000000..b47676876 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iploop.c @@ -0,0 +1,665 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** iploop.c - IP loopback routines. +// +// This file contains all the routines related to loopback + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "tdistat.h" +#include "ipdef.h" +#include "ipinit.h" +#include "llipif.h" +#include "iprtdef.h" +#include "iproute.h" +#include "tdiinfo.h" +#include "llinfo.h" + +#define LOOP_LOOKAHEAD MAX_HDR_SIZE + 8 + +extern int NumNTE; + +extern Interface *IFList; +extern uint NumIF; + +extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint); +extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS); +extern void IPRcvComplete(void); +extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size, + uint *StartOffset); + +DEFINE_LOCK_STRUCTURE(LoopLock) +PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL; +PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL; +CTEEvent LoopXmitEvent; +RouteInterface LoopInterface; // Loopback interface. +uint LoopXmitRtnRunning = 0; + + +#ifdef NT +#ifdef ALLOC_PRAGMA +// +// Make init code disposable. +// +NetTableEntry *InitLoopback(IPConfigInfo *ConfigInfo); + +#pragma alloc_text(INIT, InitLoopback) + +#endif // ALLOC_PRAGMA +#endif // NT + + +uint LoopIndex; // Index of loop I/F. +uint LoopInstance; // I/F instance of loopback I/F. +NetTableEntry *LoopNTE; // Pointer to loopback NTE. + +IFEntry LoopIFE; // Loopback IF Entry. + +uchar LoopName[] = "MS TCP Loopback interface"; + +uint LoopEntityType = IF_MIB; + +//* LoopXmitRtn - Loopback xmit event routine. +// +// This is the delayed event routine called for a loopback transmit. +// +// Entry: Event - Pointer to event structure. +// Context - Pointer to loopback NTE +// +// Returns: Nothing. +// +void +LoopXmitRtn(CTEEvent *Event, void *Context) +{ + PNDIS_PACKET Packet; // Pointer to packet being transmitted + CTELockHandle Handle; + PNDIS_BUFFER Buffer; // Current NDIS buffer being processed. + uint TotalLength; // Total length of send. + uint LookaheadLength; // Bytes in lookahead. + uint Copied; // Bytes copied so far. + uchar *CopyPtr; // Pointer to buffer being copied into. + uchar *SrcPtr; // Pointer to buffer being copied from. + uint SrcLength; // Length of src buffer. + uchar LookaheadBuffer[LOOP_LOOKAHEAD]; + uchar Rcvd = FALSE; +#ifdef NT + KIRQL OldIrql; + + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + + // + // Raise IRQL so we can acquire locks at DPC level in the receive code. + // + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); +#endif // NT + + CTEGetLock(&LoopLock, &Handle); + + if (LoopXmitRtnRunning) { + CTEFreeLock(&LoopLock, Handle); +#ifdef NT + KeLowerIrql(OldIrql); +#endif // NT + return; + } + + LoopXmitRtnRunning = 1; + + for (;;) { + + Packet = LoopXmitHead; // Get the next packet from the list. + if (Packet != (PNDIS_PACKET)NULL) { + LoopXmitHead = *(PNDIS_PACKET *)Packet->MacReserved; + LoopIFE.if_outqlen--; + CTEFreeLock(&LoopLock, Handle); + } else { // Nothing left to do. + LoopXmitRtnRunning = 0; + CTEFreeLock(&LoopLock, Handle); + break; + } + + NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength); + LoopIFE.if_outoctets += TotalLength; + LoopIFE.if_inoctets += TotalLength; + + // See if the interface is up. If it's not, we can't deliver it. + if (LoopIFE.if_adminstatus == IF_STATUS_UP) { + LookaheadLength = MIN(LOOP_LOOKAHEAD, TotalLength); + Copied = 0; + CopyPtr = LookaheadBuffer; + while (Copied < LookaheadLength) { + uint ThisCopy; // Bytes to copy this time. + +#ifdef DEBUG + if (!Buffer) { + DEBUGCHK; + CTEGetLock(&LoopLock, &Handle); + LoopXmitRtnRunning = 0; + CTEFreeLock(&LoopLock, Handle); +#ifdef NT + KeLowerIrql(OldIrql); +#endif // NT + return; + } +#endif + + NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength); + ThisCopy = MIN(SrcLength, LookaheadLength - Copied); + CTEMemCopy(CopyPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + CopyPtr += ThisCopy; + NdisGetNextBuffer(Buffer, &Buffer); + } + + Rcvd = TRUE; + LoopIFE.if_inucastpkts++; + + IPRcv(Context, LookaheadBuffer, LookaheadLength, TotalLength, + (NDIS_HANDLE) Packet, 0, FALSE); + + } else { + LoopIFE.if_indiscards++; + } + + IPSendComplete(Context, Packet, NDIS_STATUS_SUCCESS); + +#ifdef NT + // + // Give other threads a chance to run. + // + KeLowerIrql(OldIrql); + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); +#endif // NT + + CTEGetLock(&LoopLock, &Handle); + } + + if (Rcvd) { + IPRcvComplete(); + } + +#ifdef NT + KeLowerIrql(OldIrql); +#endif // NT + +} + +//** LoopXmit - Transmit a loopback packet. +// +// This is the routine called when we need to transmit a packet to ourselves. We put +// the packet on our loopback list, and schedule an event to deal with it. +// +// Entry: Context - Pointer to the loopback NTE. +// Packet - Pointer to packet to be transmitted. +// Dest - Destination addres of packet. +// RCE - Pointer to RCE (should be NULL). +// +// Returns: NDIS_STATUS_PENDING +// +NDIS_STATUS +LoopXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE) +{ + PNDIS_PACKET *PacketPtr; + CTELockHandle Handle; + + LoopIFE.if_outucastpkts++; + + if (LoopIFE.if_adminstatus == IF_STATUS_UP) { + PacketPtr = (PNDIS_PACKET *)Packet->MacReserved; + *PacketPtr = (PNDIS_PACKET)NULL; + + + CTEGetLock(&LoopLock, &Handle); + if (LoopXmitHead == (PNDIS_PACKET)NULL) { // Xmit. Q is empty + LoopXmitHead = Packet; + } else { // Xmit. Q is not empty + PacketPtr = (PNDIS_PACKET *)LoopXmitTail->MacReserved; + *PacketPtr = Packet; + } + LoopXmitTail = Packet; + LoopIFE.if_outqlen++; + if (!LoopXmitRtnRunning) { + CTEScheduleEvent(&LoopXmitEvent, Context); + } + CTEFreeLock(&LoopLock, Handle); + return NDIS_STATUS_PENDING; + } else { + LoopIFE.if_outdiscards++; + return NDIS_STATUS_SUCCESS; + } +} + +//* LoopXfer - Loopback transfer data routine. +// +// Called when we need to transfer data for the loopback net. The input TDContext is +// the original packet. +// +// Entry: Context - Pointer to loopback NTE. +// TDContext - Original packet that was sent. +// Dummy - Unused +// Offset - Offset in frame from which to start copying. +// BytesToCopy - Number of bytes to copy. +// DestPacket - Packet describing buffer to copy into. +// BytesCopied - Place to return bytes copied. +// +// Returns: NDIS_STATUS_SUCCESS +// +NDIS_STATUS +LoopXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy, + PNDIS_PACKET DestPacket, uint *BytesCopied) +{ + PNDIS_BUFFER SrcBuffer; // Current buffer we're copying from. + PNDIS_PACKET SrcPacket = (PNDIS_PACKET)TDContext; + uchar *SrcPtr; // Where we're copying from. + uint SrcLength; // Length of current src buffer. + PNDIS_BUFFER DestBuffer; // Buffer we're copying to. + uchar *DestPtr; // Where we're copying to. + uint DestLength; // Length of current dest. buffer. + uint Copied; // Length we've copied so far. + + // First, skip over Offset bytes in the packet. + NdisQueryPacket(SrcPacket, NULL, NULL, &SrcBuffer, NULL); +#ifdef DEBUG + if (!SrcBuffer) + DEBUGCHK; +#endif + NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength); + while (Offset >= SrcLength) { + Offset -= SrcLength; + NdisGetNextBuffer(SrcBuffer, &SrcBuffer); +#ifdef DEBUG + if (!SrcBuffer) + DEBUGCHK; +#endif + NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength); + } + // Update Src pointer and length. + SrcPtr += Offset; + SrcLength -= Offset; + + // Set up the destination pointers and lengths. + NdisQueryPacket(DestPacket, NULL, NULL, &DestBuffer, NULL); + NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength); + Copied = 0; + + while (BytesToCopy) { + uint ThisCopy; // What we're copying this time. + + ThisCopy = MIN(SrcLength, DestLength); + CTEMemCopy(DestPtr, SrcPtr, ThisCopy); + Copied += ThisCopy; + DestPtr += ThisCopy; + SrcPtr += ThisCopy; + BytesToCopy -= ThisCopy; + SrcLength -= ThisCopy; + DestLength -= ThisCopy; + if (!SrcLength) { // We've exhausted the source buffer. + NdisGetNextBuffer(SrcBuffer, &SrcBuffer); + if (!SrcBuffer) { +#ifdef DEBUG + if (BytesToCopy) + DEBUGCHK; +#endif + break; // Copy is done. + } + NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength); + } + if (!DestLength) { // We've exhausted the destination buffer. + NdisGetNextBuffer(DestBuffer, &DestBuffer); + if (!DestBuffer) { +#ifdef DEBUG + if (BytesToCopy) + DEBUGCHK; +#endif + break; // Copy is done. + } + NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength); + } + } + + *BytesCopied = Copied; + return NDIS_STATUS_SUCCESS; + + +} + +//* LoopClose - Loopback close routine. +// +// This is the loopback close routine. It does nothing but return. +// +// Entry: Context - Unused. +// +// Returns: Nothing. +// +void +LoopClose(void *Context) +{ + +} + +//* LoopInvalidate - Invalidate an RCE. +// +// The loopback invalidate RCE routine. It also does nothing. +// +// Entry: Context - Unused. +// RCE - Pointer to RCE to be invalidated. +// +// Returns: Nothing. +// +void +LoopInvalidate(void *Context, RouteCacheEntry *RCE) +{ + +} + +//* LoopQInfo - Loopback query information handler. +// +// Called when the upper layer wants to query information about the loopback +// interface. +// +// Input: IFContext - Interface context (unused). +// 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 +LoopQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, + void *Context) +{ + uint Offset = 0; + uint BufferSize = *Size; + uint Entity; + uint Instance; + + + Entity = ID->toi_entity.tei_entity; + Instance = ID->toi_entity.tei_instance; + + // First, make sure it's possibly an ID we can handle. + if (Entity != IF_ENTITY || Instance != LoopInstance) { + 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)) { + (void)CopyToNdis(Buffer, (uchar *)&LoopEntityType, sizeof(uint), + &Offset); + return TDI_SUCCESS; + } else + return TDI_BUFFER_TOO_SMALL; + } + return TDI_INVALID_PARAMETER; + } else + if (ID->toi_class != INFO_CLASS_PROTOCOL) + return TDI_INVALID_PARAMETER; + + // If he's asking for MIB statistics, then return them, otherwise fail + // the request. + + if (ID->toi_id == IF_MIB_STATS_ID) { + + + // 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. Copy our IFE structure + // into his buffer. + Buffer = CopyToNdis(Buffer, (uchar *)&LoopIFE, IFE_FIXED_SIZE, &Offset); + + // See if he has room for the descriptor string. + if (BufferSize >= (IFE_FIXED_SIZE + sizeof(LoopName))) { + // He has room. Copy it. + (void)CopyToNdis(Buffer, LoopName, sizeof(LoopName), &Offset); + *Size = IFE_FIXED_SIZE + sizeof(LoopName); + return TDI_SUCCESS; + } else { + // Not enough room to copy the desc. string. + *Size = IFE_FIXED_SIZE; + return TDI_BUFFER_OVERFLOW; + } + + } + + return TDI_INVALID_PARAMETER; + +} + +//* LoopSetInfo - Loopback set information handler. +// +// The loopback set information handler. We support setting of an I/F admin +// status. +// +// 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 +LoopSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size) +{ + IFEntry *IFE = (IFEntry *)Buffer; + uint Entity, Instance, Status; + + 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 != IF_ENTITY || Instance != LoopInstance) { + return TDI_INVALID_REQUEST; + } + + if (ID->toi_class != INFO_CLASS_PROTOCOL || + ID->toi_type != INFO_TYPE_PROVIDER) { + return TDI_INVALID_PARAMETER; + } + + // It's for the I/F level, see if it's for the statistics. + 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. + Status = IFE->if_adminstatus; + if (Status == IF_STATUS_UP || Status == IF_STATUS_DOWN) + LoopIFE.if_adminstatus = Status; + else + if (Status != IF_STATUS_TESTING) + return TDI_INVALID_PARAMETER; + + return TDI_SUCCESS; + + } else + return TDI_INVALID_PARAMETER; + } + + return TDI_INVALID_PARAMETER; +} + +//* LoopAddAddr - Dummy loopback add address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +LoopAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) +{ + + return TRUE; +} + +//* LoopDelAddr - Dummy loopback del address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +LoopDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) +{ + + return TRUE; +} + +#pragma BEGIN_INIT + +extern int InitNTE(NetTableEntry *); +extern int InitInterface(NetTableEntry *); + +#ifdef CHICAGO +#pragma END_INIT +#pragma code_seg("_LTEXT", "LCODE") +#endif + +//* LoopGetEList - Get the entity list. +// +// Called at init time to get an entity list. We fill our stuff in and return. +// +// Input: Context - Unused. +// 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 +LoopGetEList(void *Context, TDIEntityID *EntityList, uint *Count) +{ + uint ECount; + uint MyIFBase; + uint i; + + ECount = *Count; + + // Walk down the list, looking for existing IF entities, and + // adjust our base instance accordingly. + + MyIFBase = 0; + for (i = 0; i < ECount; i++, EntityList++) { + if (EntityList->tei_entity == IF_ENTITY) + MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1); + } + + // EntityList points to the start of where we want to begin filling in. + // Make sure we have enough room. + + if ((ECount + 1) > MAX_TDI_ENTITIES) + return FALSE; + + // At this point we've figure out our base instance. Save for later use. + LoopInstance = MyIFBase; + + // Now fill it in. + EntityList->tei_entity = IF_ENTITY; + EntityList->tei_instance = MyIFBase; + (*Count)++; + + return TRUE; +} + +#ifdef CHICAGO +#pragma BEGIN_INIT +#endif + + +//** InitLoopback - Initialize the loopback NTE. +// +// This function initialized the loopback NTE. We set up the the MSS and pointer to +// the various pseudo-link routines, then call InitNTE and return. +// +// Entry: ConfigInfo - Pointer to config. info structure. +// +// Returns: TRUE if we initialized, FALSE if we didn't. +// +NetTableEntry * +InitLoopback(IPConfigInfo *ConfigInfo) +{ + LLIPBindInfo ARPInfo; + + CTERefillMem(); + LoopNTE = CTEAllocMem(sizeof(NetTableEntry)); + if (LoopNTE == NULL) + return LoopNTE; + + CTEMemSet(LoopNTE, 0, sizeof(NetTableEntry)); + + LoopNTE->nte_addr = LOOPBACK_ADDR; + LoopNTE->nte_mask = CLASSA_MASK; + LoopNTE->nte_icmpseq = 1; + LoopNTE->nte_flags = NTE_VALID | NTE_ACTIVE | NTE_PRIMARY; + + CTEInitLock(&LoopNTE->nte_lock); + CTEInitLock(&LoopInterface.ri_if.if_lock); + LoopNTE->nte_mss = LOOPBACK_MSS; + LoopNTE->nte_if = (Interface *)&LoopInterface; + LoopInterface.ri_if.if_lcontext = LoopNTE; + LoopInterface.ri_if.if_xmit = LoopXmit; + LoopInterface.ri_if.if_transfer = LoopXfer; + LoopInterface.ri_if.if_close = LoopClose; + LoopInterface.ri_if.if_invalidate = LoopInvalidate; + LoopInterface.ri_if.if_qinfo = LoopQInfo; + LoopInterface.ri_if.if_setinfo = LoopSetInfo; + LoopInterface.ri_if.if_getelist = LoopGetEList; + LoopInterface.ri_if.if_addaddr = LoopAddAddr; + LoopInterface.ri_if.if_deladdr = LoopDelAddr; + LoopInterface.ri_if.if_bcast = IP_LOCAL_BCST; + LoopInterface.ri_if.if_speed = 10000000; + LoopInterface.ri_if.if_mtu = LOOPBACK_MSS; + LoopInterface.ri_if.if_llipflags = LIP_COPY_FLAG; +#ifdef _PNP_POWER + LoopInterface.ri_if.if_refcount = 1; + LoopInterface.ri_if.if_pnpcontext = 0; +#endif + + ARPInfo.lip_mss = LOOPBACK_MSS + sizeof(IPHeader); + ARPInfo.lip_index = LoopIndex; + ARPInfo.lip_close = LoopClose; + ARPInfo.lip_addaddr = LoopAddAddr; + ARPInfo.lip_deladdr = LoopDelAddr; + ARPInfo.lip_flags = LIP_COPY_FLAG; + LoopIndex = NumIF + 1; + LoopInterface.ri_if.if_index = LoopIndex; + CTEInitEvent(&LoopXmitEvent, LoopXmitRtn); + CTEInitLock(&LoopLock); + LoopIFE.if_index = LoopIndex; + LoopIFE.if_type = IF_TYPE_LOOPBACK; + LoopIFE.if_mtu = ARPInfo.lip_mss; + LoopIFE.if_speed = 10000000; + LoopIFE.if_adminstatus = IF_STATUS_UP; + LoopIFE.if_operstatus = IF_STATUS_UP; + LoopIFE.if_descrlen = sizeof(LoopName); + + IFList = (Interface *)&LoopInterface; + NumIF++; + + NumNTE++; + + if (!InitInterface(LoopNTE)) + return NULL; + + if (!InitNTE(LoopNTE)) + return NULL; + + return LoopNTE; +} + +#pragma END_INIT + diff --git a/private/ntos/tdi/tcpip/ip/iprcv.c b/private/ntos/tdi/tcpip/ip/iprcv.c new file mode 100644 index 000000000..791bc58f6 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iprcv.c @@ -0,0 +1,1145 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** iprcv.c - IP receive routines. +// +// This module contains all receive related IP routines. +// + + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "info.h" +#include "iproute.h" +#include "ipfilter.h" + +extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong); + +extern uchar RATimeout; +extern NDIS_HANDLE BufferPool; +#if 0 +EXTERNAL_LOCK(PILock) +#endif +extern ProtInfo IPProtInfo[]; // Protocol information table. +extern ProtInfo *LastPI; // Last protinfo structure looked at. +extern int NextPI; // Next PI field to be used. +extern ProtInfo *RawPI; // Raw IP protinfo +extern NetTableEntry *NetTableList; // Pointer to the net table. + +DEBUGSTRING(RcvFile, "iprcv.c"); + +#ifdef CHICAGO +extern void RefillReasmMem(void); +#endif + +//* FindUserRcv - Find the receive handler to be called for a particular protocol. +// +// This functions takes as input a protocol value, and returns a pointer to +// the receive routine for that protocol. +// +// Input: NTE - Pointer to NetTableEntry to be searched +// Protocol - Protocol to be searched for. +// UContext - Place to returns UL Context value. +// +// Returns: Pointer to the receive routine. +// +ULRcvProc +FindUserRcv(uchar Protocol) +{ + ULRcvProc RcvProc; + int i; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + + if (LastPI->pi_protocol == Protocol) { + RcvProc = LastPI->pi_rcv; +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return RcvProc; + } + + RcvProc = (ULRcvProc)NULL; + for ( i = 0; i < NextPI; i++) { + if (IPProtInfo[i].pi_protocol == Protocol) { + LastPI = &IPProtInfo[i]; + RcvProc = IPProtInfo[i].pi_rcv; +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return RcvProc; + } + } + + // + // Didn't find a match. Use the raw protocol if it is registered. + // + if (RawPI != NULL) { + RcvProc = RawPI->pi_rcv; + } + +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return RcvProc; + +} + +//* IPRcvComplete - Handle a receive complete. +// +// Called by the lower layer when receives are temporarily done. +// +// Entry: Nothing. +// +// Returns: Nothing. +// +void +IPRcvComplete(void) +{ + void (*ULRcvCmpltProc)(void); + int i; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + for (i = 0; i < NextPI; i++) { + if ((ULRcvCmpltProc = IPProtInfo[i].pi_rcvcmplt) != NULL) { +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + (*ULRcvCmpltProc)(); +#if 0 + CTEGetLock(&PILock, &Handle); +#endif + } + } +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + +} +//* FindRH - Look up a reassembly header on an NTE. +// +// A utility function to look up a reassembly header. We assume the lock on the NTE +// is taken when we are called. If we find a matching RH we'll take the lock on it. +// We also return the predeccessor of the RH, for use in insertion or deletion. +// +// Input: PrevRH - Place to return pointer to previous RH +// NTE - NTE to be searched. +// Dest - Destination IP address +// Src - Src IP address +// ID - ID of RH +// Protocol - Protocol of RH +// +// Returns: Pointer to RH, or NULL if none. +// +ReassemblyHeader * +FindRH(ReassemblyHeader **PrevRH, NetTableEntry *NTE, IPAddr Dest, IPAddr Src, ushort Id, + uchar Protocol) +{ + ReassemblyHeader *TempPrev, *Current; + + TempPrev = STRUCT_OF(ReassemblyHeader, &NTE->nte_ralist, rh_next); + Current = NTE->nte_ralist; + while (Current != (ReassemblyHeader *)NULL) { + if (Current->rh_dest == Dest && Current->rh_src == Src && Current->rh_id == Id && + Current->rh_protocol == Protocol) + break; + TempPrev = Current; + Current = Current->rh_next; + } + + *PrevRH = TempPrev; + return Current; + +} + +//* ParseRcvdOptions - Validate incoming options. +// +// Called during reception handling to validate incoming options. We make sure that everything +// is OK as best we can, and find indices for any source route option. +// +// Input: OptInfo - Pointer to option info. structure. +// Index - Pointer to optindex struct to be filled in. +// +// +// Returns: Index of error if any, MAX_OPT_SIZE if no errors. +// +uchar +ParseRcvdOptions(IPOptInfo *OptInfo, OptIndex *Index) +{ + uint i= 0; // Index variable. + uchar *Options = OptInfo->ioi_options; + uint OptLength = (uint)OptInfo->ioi_optlength; + uchar Length; // Length of option. + uchar Pointer; // Pointer field, for options that use it. + + while(i < OptLength && *Options != IP_OPT_EOL) { + if (*Options == IP_OPT_NOP) { + i++; + Options++; + continue; + } + if (((Length = Options[IP_OPT_LENGTH]) + i) > OptLength) { + return (uchar)i + (uchar)IP_OPT_LENGTH; // Length exceeds options length. + } + + Pointer = Options[IP_OPT_DATA] - 1; + + if (*Options == IP_OPT_TS) { + if (Length < (MIN_TS_PTR - 1)) + return (uchar)i + (uchar)IP_OPT_LENGTH; + Index->oi_tsindex = (uchar)i; + } else { + if (Length < (MIN_RT_PTR - 1)) + return (uchar)i + (uchar)IP_OPT_LENGTH; + + if (*Options == IP_OPT_LSRR || *Options == IP_OPT_SSRR) { + // A source route option + if (Pointer < Length) { // Route not complete + + if ((Length - Pointer) < sizeof(IPAddr)) + return (uchar)i + (uchar)IP_OPT_LENGTH; + + Index->oi_srtype = *Options; + Index->oi_srindex = (uchar)i; + } + } else { + if (*Options == IP_OPT_RR) { + if (Pointer < Length) + Index->oi_rrindex = (uchar)i; + } + } + } + + i += Length; + Options += Length; + } + + return MAX_OPT_SIZE; +} + +//* BCastRcv - Receive a broadcast or multicast packet. +// +// Called when we have to receive a broadcast packet. We loop through the NTE table, +// calling the upper layer receive protocol for each net which matches the receive I/F +// and for which the destination address is a broadcast. +// +// Input: RcvProc - The receive procedure to be called. +// SrcNTE - NTE on which the packet was originally received. +// DestAddr - Destination address. +// SrcAddr - Source address of packet. +// Data - Pointer to received data. +// DataLength - Size in bytes of data +// Protocol - Upper layer protocol being called. +// OptInfo - Pointer to received IP option info. +// +// Returns: Nothing. +// +void +BCastRcv(ULRcvProc RcvProc, NetTableEntry *SrcNTE, IPAddr DestAddr, + IPAddr SrcAddr, IPHeader UNALIGNED *Header, uint HeaderLength, + IPRcvBuf *Data, uint DataLength, uchar Protocol, IPOptInfo *OptInfo) +{ + NetTableEntry *CurrentNTE; + const Interface *SrcIF = SrcNTE->nte_if; + ulong Delivered = 0; + + + for (CurrentNTE = NetTableList; + CurrentNTE != NULL; + CurrentNTE = CurrentNTE->nte_next) + { + if ((CurrentNTE->nte_flags & NTE_ACTIVE) && + (CurrentNTE->nte_if == SrcIF) && + IS_BCAST_DEST(IsBCastOnNTE(DestAddr, CurrentNTE))) + { + Delivered = 1; + + (*RcvProc)(CurrentNTE, DestAddr, SrcAddr, CurrentNTE->nte_addr, + SrcNTE->nte_addr, Header, HeaderLength, Data, DataLength, + TRUE, Protocol, OptInfo); + } + } + + if (Delivered) { + IPSInfo.ipsi_indelivers++; + } +} + +//* DeliverToUser - Deliver data to a user protocol. +// +// This procedure is called when we have determined that an incoming packet belongs +// here, and any options have been processed. We accept it for upper layer processing, +// which means looking up the receive procedure and calling it, or passing it to BCastRcv +// if neccessary. +// +// Input: SrcNTE - Pointer to NTE on which packet arrived. +// DestNTE - Pointer to NTE that is accepting packet. +// Header - Pointer to IP header of packet. +// HeaderLength - Length of Header in bytes. +// Data - Pointer to IPRcvBuf chain. +// DataLength - Length in bytes of upper layer data. +// OptInfo - Pointer to Option information for this receive. +// DestType - Type of destination - LOCAL, BCAST. +// +// Returns: Nothing. +void +DeliverToUser(NetTableEntry *SrcNTE, NetTableEntry *DestNTE, + IPHeader UNALIGNED *Header, uint HeaderLength, IPRcvBuf *Data, + uint DataLength, IPOptInfo *OptInfo, uchar DestType) +{ + ULRcvProc rcv; + +#ifdef DEBUG + if (DestType >= DEST_REMOTE) + DEBUGCHK; +#endif + + // Process this request right now. Look up the protocol. If we + // find it, copy the data if we need to, and call the protocol's + // receive handler. If we don't find it, send an ICMP + // 'protocol unreachable' message. + rcv = FindUserRcv(Header->iph_protocol); + if (rcv != NULL) { + IP_STATUS Status; + + if (DestType == DEST_LOCAL) { + Status = (*rcv)(SrcNTE,Header->iph_dest, Header->iph_src, + DestNTE->nte_addr, SrcNTE->nte_addr, Header, + HeaderLength, Data, DataLength, FALSE, + Header->iph_protocol, OptInfo); + + if (Status == IP_SUCCESS) { + IPSInfo.ipsi_indelivers++; + return; + } + + if (Status == IP_DEST_PROT_UNREACHABLE) { + IPSInfo.ipsi_inunknownprotos++; + SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, + PROT_UNREACH, 0); + } + else { + IPSInfo.ipsi_indelivers++; + SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, + PORT_UNREACH, 0); + } + + return; // Just return out of here now. + } + else { + BCastRcv(rcv, SrcNTE, Header->iph_dest, Header->iph_src, + Header, HeaderLength, Data, DataLength, + Header->iph_protocol, OptInfo); + } + + } else { + IPSInfo.ipsi_inunknownprotos++; + // If we get here, we didn't find a matching protocol. Send an ICMP message. + SendICMPErr(DestNTE->nte_addr, Header, ICMP_DEST_UNREACH, PROT_UNREACH, 0); + } + +} + +//* FreeRH - Free a reassembly header. +// +// Called when we need to free a reassembly header, either because of a timeout or because +// we're done with it. +// +// Input: RH - RH to be freed. +// +// Returns: Nothing. +// +void +FreeRH(ReassemblyHeader *RH) +{ + RABufDesc *RBD, *TempRBD; + + RBD = RH->rh_rbd; + while (RBD != NULL) { + TempRBD = RBD; + RBD = (RABufDesc *)RBD->rbd_buf.ipr_next; + CTEFreeMem(TempRBD); + } + CTEFreeMem(RH); + +} + +//* ReassembleFragment - Put a fragment into the reassembly list. +// +// This routine is called once we've put a fragment into the proper buffer. We look for +// a reassembly header for the fragment. If we don't find one, we create one. Otherwise +// we search the reassembly list, and insert the datagram in it's proper place. +// +// Input: NTE - NTE to reassemble on. +// SrcNTE - NTE datagram arrived on. +// NewRBD - New RBD to be inserted. +// IPH - Pointer to header of datagram. +// HeaderSize - Size in bytes of header. +// DestType - Type of destination address. +// +// Returns: Nothing. +// +void +ReassembleFragment(NetTableEntry *NTE, NetTableEntry *SrcNTE, RABufDesc *NewRBD, + IPHeader UNALIGNED *IPH, uint HeaderSize, uchar DestType) +{ + CTELockHandle NTEHandle; // Lock handle used for NTE + ReassemblyHeader *RH, *PrevRH; // Current and previous reassembly headers. + RABufDesc *PrevRBD; // Previous RBD in reassembly header list. + RABufDesc *CurrentRBD; + ushort DataLength = (ushort)NewRBD->rbd_buf.ipr_size, DataOffset; + ushort Offset; // Offset of this fragment. + ushort NewOffset; // Offset we'll copy from after checking RBD list. + ushort NewEnd; // End offset of fragment, after trimming (if any). + + // If this is a broadcast, go ahead and forward it now. + if (IS_BCAST_DEST(DestType)) + IPForward(SrcNTE, IPH, HeaderSize, NewRBD->rbd_buf.ipr_buffer, + NewRBD->rbd_buf.ipr_size, NULL, 0, DestType); + + + // We've got the buffer we need. Now get the reassembly header, if there is one. If + // there isn't, create one. + CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle); + RH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol); + if (RH == (ReassemblyHeader *)NULL) { // Didn't find one, so create one. + ReassemblyHeader *NewRH; + + CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle); + RH = CTEAllocMem(sizeof(ReassemblyHeader)); + if (RH == (ReassemblyHeader *)NULL) { // Couldn't get a buffer. +#ifdef CHICAGO + RefillReasmMem(); +#endif + IPSInfo.ipsi_reasmfails++; + CTEFreeMem(NewRBD); + return; + } + + CTEGetLockAtDPC(&NTE->nte_lock, &NTEHandle); + // Need to look it up again - it could have changed during above call. + NewRH = FindRH(&PrevRH, NTE, IPH->iph_dest, IPH->iph_src, IPH->iph_id, IPH->iph_protocol); + if (NewRH != (ReassemblyHeader *)NULL) { + CTEFreeMem(RH); + RH = NewRH; + } else { + + RH->rh_next = PrevRH->rh_next; + PrevRH->rh_next = RH; + + + // Initialize our new reassembly header. + RH->rh_dest = IPH->iph_dest; + RH->rh_src = IPH->iph_src; + RH->rh_id = IPH->iph_id; + RH->rh_protocol = IPH->iph_protocol; + RH->rh_ttl = RATimeout; + RH->rh_datasize = 0xffff; // Default datasize to maximum. + RH->rh_rbd = (RABufDesc *)NULL; // And nothing on chain. + RH->rh_datarcvd = 0; // Haven't received any data yet. + RH->rh_headersize = 0; + + } + } + + // When we reach here RH points to the reassembly header we want to use. + // and we hold locks on the NTE and the RH. If this is the first fragment we'll save + // the options and header information here. + + Offset = IPH->iph_offset & IP_OFFSET_MASK; + Offset = net_short(Offset) * 8; + + if (Offset == 0) { // First fragment. + RH->rh_headersize = HeaderSize; + CTEMemCopy(RH->rh_header, IPH, HeaderSize + 8); + } + + // If this is the last fragment, update the amount of data we expect to received. + if (!(IPH->iph_offset & IP_MF_FLAG)) + RH->rh_datasize = Offset + DataLength; + + // Update the TTL value with the maximum of the current TTL and the incoming + // TTL (+1, to deal with rounding errors). + RH->rh_ttl = MAX(RH->rh_ttl, MIN(254, IPH->iph_ttl) + 1); + + // Now we need to see where in the RBD list to put this. + // + // The idea is to go through the list of RBDs one at a time. The RBD currently + // being examined is CurrentRBD. If the start offset of the new fragment is less + // than (i.e. in front of) the offset of CurrentRBD, we need to insert the NewRBD + // in front of the CurrentRBD. If this is the case we need to check and see if the + // end of the new fragment overlaps some or all of the fragment described by + // CurrentRBD, and possibly subsequent fragment. If it overlaps part of a fragment + // we'll adjust our end down to be in front of the existing fragment. If it overlaps + // all of the fragment we'll free the old fragment. + // + // If the new fragment does not start in front of the current fragment we'll check + // to see if it starts somewhere in the middle of the current fragment. If this + // isn't the case, we move on the the next fragment. If this is the case, we check + // to see if the current fragment completely covers the new fragment. If not we + // move our start up and continue with the next fragment. + + NewOffset = Offset; + NewEnd = Offset + DataLength - 1; + PrevRBD = STRUCT_OF(RABufDesc, STRUCT_OF(IPRcvBuf, &RH->rh_rbd, ipr_next), rbd_buf); + CurrentRBD = RH->rh_rbd; + for (; CurrentRBD != NULL; PrevRBD = CurrentRBD, CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next) { + + // See if it starts in front of this fragment. + if (NewOffset < CurrentRBD->rbd_start) { + // It does start in front. Check to see if there's any overlap. + + if (NewEnd < CurrentRBD->rbd_start) + break; // No overlap, so get out. + else { + // It does overlap. While we have overlap, walk down the list + // looking for RBDs we overlap completely. If we find one, put it + // on our deletion list. If we have overlap but not complete overlap, + // move our end down if front of the fragment we overlap. + do { + if (NewEnd > CurrentRBD->rbd_end) { // This overlaps completely. + RABufDesc *TempRBD; + + RH->rh_datarcvd -= CurrentRBD->rbd_buf.ipr_size; + TempRBD = CurrentRBD; + CurrentRBD = (RABufDesc *)CurrentRBD->rbd_buf.ipr_next; + CTEFreeMem(TempRBD); + } else // Only partial ovelap. + NewEnd = CurrentRBD->rbd_start - 1; + // Update of NewEnd will force us out of loop. + + } while (CurrentRBD != NULL && NewEnd >= CurrentRBD->rbd_start); + break; + } + } else { + // This fragment doesn't go in front of the current RBD. See if it is + // entirely beyond the end of the current fragment. If it is, just + // continue. Otherwise see if the current fragment completely subsumes + // us. If it does, get out, otherwise update our start offset and + // continue. + + if (NewOffset > CurrentRBD->rbd_end) + continue; // No overlap at all. + else { + if (NewEnd <= CurrentRBD->rbd_end) { + // The current fragment overlaps the new fragment totally. Set + // our offsets so that we'll skip the copy below. + NewEnd = NewOffset - 1; + break; + } else // Only partial overlap. + NewOffset = CurrentRBD->rbd_end + 1; + } + } + } // End of for loop. + + // Adjust the length and offset fields in the new RBD. + DataLength = NewEnd - NewOffset + 1; + DataOffset = NewOffset - Offset; + // Link him in chain. + NewRBD->rbd_buf.ipr_size = (uint)DataLength; + NewRBD->rbd_end = NewEnd; + NewRBD->rbd_start = NewOffset; + RH->rh_datarcvd += DataLength; + NewRBD->rbd_buf.ipr_buffer += DataOffset; + NewRBD->rbd_buf.ipr_next = (IPRcvBuf *)CurrentRBD; + PrevRBD->rbd_buf.ipr_next = &NewRBD->rbd_buf; + + // If we've received all the data, deliver it to the user. + if (RH->rh_datarcvd == RH->rh_datasize) { // We have it all. + IPOptInfo OptInfo; + IPHeader *Header; + IPRcvBuf *FirstBuf; + + PrevRH->rh_next = RH->rh_next; + CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle); + Header = (IPHeader *)RH->rh_header; + OptInfo.ioi_ttl = Header->iph_ttl; + OptInfo.ioi_tos = Header->iph_tos; + OptInfo.ioi_flags = 0; // Flags must be 0 - DF can't be set, this was reassembled. + + if (RH->rh_headersize != sizeof(IPHeader)) { // We had options. + OptInfo.ioi_options = (uchar *)(Header + 1); + OptInfo.ioi_optlength = RH->rh_headersize - sizeof(IPHeader); + } else { + OptInfo.ioi_options = (uchar *)NULL; + OptInfo.ioi_optlength = 0; + } + + // Make sure that the first buffer contains enough data. + FirstBuf = (IPRcvBuf *)RH->rh_rbd; + while (FirstBuf->ipr_size < MIN_FIRST_SIZE) { + IPRcvBuf *NextBuf = FirstBuf->ipr_next; + uint CopyLength; + + if (NextBuf == NULL) + break; + + CopyLength = MIN(MIN_FIRST_SIZE - FirstBuf->ipr_size, + NextBuf->ipr_size); + CTEMemCopy(FirstBuf->ipr_buffer + FirstBuf->ipr_size, + NextBuf->ipr_buffer, CopyLength); + FirstBuf->ipr_size += CopyLength; + NextBuf->ipr_buffer += CopyLength; + NextBuf->ipr_size -= CopyLength; + if (NextBuf->ipr_size == 0) { + FirstBuf->ipr_next = NextBuf->ipr_next; + CTEFreeMem(NextBuf); + } + } + + IPSInfo.ipsi_reasmoks++; + DeliverToUser(SrcNTE, NTE, Header, RH->rh_headersize, FirstBuf, + RH->rh_datasize, &OptInfo, DestType); + FreeRH(RH); + } else + CTEFreeLockFromDPC(&NTE->nte_lock, NTEHandle); + + +} + +//* RATDComplete - Completion routing for a reassembly transfer data. +// +// This is the completion handle for TDs invoked because we are reassembling a fragment. +// +// Input: NetContext - Pointer to the net table entry on which we received this. +// Packet - Packet we received into. +// Status - Final status of copy. +// DataSize - Size in bytes of data transferred. +// +// Returns: Nothing +// +void +RATDComplete(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataSize) +{ + NetTableEntry *NTE = (NetTableEntry *)NetContext; + Interface *SrcIF; + TDContext *Context = (TDContext *)Packet->ProtocolReserved; + CTELockHandle Handle; + PNDIS_BUFFER Buffer; + + if (Status == NDIS_STATUS_SUCCESS) { + Context->tdc_rbd->rbd_buf.ipr_size = DataSize; + ReassembleFragment(Context->tdc_nte, NTE, Context->tdc_rbd, + (IPHeader *)Context->tdc_header, Context->tdc_hlength, Context->tdc_dtype); + } + + NdisUnchainBufferAtFront(Packet, &Buffer); + NdisFreeBuffer(Buffer); + Context->tdc_common.pc_flags &= ~PACKET_FLAG_RA; + SrcIF = NTE->nte_if; + CTEGetLockAtDPC(&SrcIF->if_lock, &Handle); + + Context->tdc_common.pc_link = SrcIF->if_tdpacket; + SrcIF->if_tdpacket = Packet; + CTEFreeLockFromDPC(&SrcIF->if_lock, Handle); + + return; + +} + +//* IPReassemble - Reassemble an incoming datagram. +// +// Called when we receive an incoming fragment. The first thing we do is get a buffer +// to put the fragment in. If we can't we'll exit. Then we copy the data, either via +// transfer data or directly if it all fits. +// +// Input: SrcNTE - Pointer to NTE that received the datagram. +// NTE - Pointer to NTE on which to reassemble. +// IPH - Pointer to header of packet. +// HeaderSize - Size in bytes of header. +// Data - Pointer to data part of fragment. +// BufferLength - Length in bytes of user data available in the buffer. +// DataLength - Length in bytes of the (upper-layer) data. +// DestType - Type of destination +// LContext1, LContext2 - Link layer context values. +// +// Returns: Nothing. +// +void +IPReassemble(NetTableEntry *SrcNTE, NetTableEntry *NTE, IPHeader UNALIGNED *IPH, + uint HeaderSize, + uchar *Data, uint BufferLength, uint DataLength, uchar DestType, NDIS_HANDLE LContext1, + uint LContext2) +{ + Interface *RcvIF; + PNDIS_PACKET TDPacket; // NDIS packet used for TD. + TDContext *TDC = (TDContext *)NULL; // Transfer data context. + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; + RABufDesc *NewRBD; // Pointer to new RBD to hold arriving fragment. + CTELockHandle Handle; + uint AllocSize; + + IPSInfo.ipsi_reasmreqds++; + + // First, get a new RBD to hold the arriving fragment. If we can't, then just skip + // the rest. The RBD has the buffer implicitly at the end of it. The buffer for the + // first fragment must be at least MIN_FIRST_SIZE bytes. + if ((IPH->iph_offset & IP_OFFSET_MASK) == 0) + AllocSize = MAX(MIN_FIRST_SIZE, DataLength); + else + AllocSize = DataLength; + + NewRBD = CTEAllocMem(sizeof(RABufDesc) + AllocSize); + + if (NewRBD != (RABufDesc *)NULL) { + + NewRBD->rbd_buf.ipr_buffer = (uchar *)(NewRBD + 1); + NewRBD->rbd_buf.ipr_size = DataLength; + NewRBD->rbd_buf.ipr_owner = IPR_OWNER_IP; + + // Copy the data into the buffer. If we need to call transfer data do so now. + if (DataLength > BufferLength) { // Need to call transfer data. + NdisAllocateBuffer(&Status, &Buffer, BufferPool, NewRBD + 1, DataLength); + if (Status != NDIS_STATUS_SUCCESS) { + IPSInfo.ipsi_reasmfails++; + CTEFreeMem(NewRBD); + return; + } + + // Now get a packet for transferring the frame. + RcvIF = SrcNTE->nte_if; + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDPacket = RcvIF->if_tdpacket; + + if (TDPacket != (PNDIS_PACKET)NULL) { + + TDC = (TDContext *)TDPacket->ProtocolReserved; + RcvIF->if_tdpacket = TDC->tdc_common.pc_link; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + + TDC->tdc_common.pc_flags |= PACKET_FLAG_RA; + TDC->tdc_nte = NTE; + TDC->tdc_dtype = DestType; + TDC->tdc_hlength = (uchar)HeaderSize; + TDC->tdc_rbd = NewRBD; + CTEMemCopy(TDC->tdc_header, IPH, HeaderSize + 8); + NdisChainBufferAtFront(TDPacket, Buffer); + Status = (*(RcvIF->if_transfer))(RcvIF->if_lcontext, LContext1, LContext2, + HeaderSize, DataLength, TDPacket, &DataLength); + if (Status != NDIS_STATUS_PENDING) + RATDComplete(SrcNTE, TDPacket, Status, DataLength); + else + return; + } else { // Couldn't get a TD packet. + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + CTEFreeMem(NewRBD); + IPSInfo.ipsi_reasmfails++; + return; + } + } else { // It all fits, copy it. + CTEMemCopy(NewRBD + 1, Data, DataLength); + ReassembleFragment(NTE, SrcNTE, NewRBD, IPH, HeaderSize, DestType); + } + } else { +#ifdef CHICAGO + RefillReasmMem(); +#endif + + IPSInfo.ipsi_reasmfails++; + } + + return; +} + + + +//* CheckLocalOptions - Check the options received with a packet. +// +// A routine called when we've received a packet for this host and want to examine +// it for options. We process the options, and return TRUE or FALSE depending on whether +// or not it's for us. +// +// Input: SrcNTE - Pointer to NTE this came in on. +// Header - Pointer to incoming header. +// OptInfo - Place to put opt info. +// DestType - Type of incoming packet. +// +// Returns: DestType - Local or remote. +// +uchar +CheckLocalOptions(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, + IPOptInfo *OptInfo, uchar DestType) +{ + uint HeaderLength; // Length in bytes of header. + OptIndex Index; + uchar ErrIndex; + +#ifdef DEBUG + if (DestType >= DEST_REMOTE) + DEBUGCHK; +#endif + + + HeaderLength = (Header->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + +#ifdef DEBUG + if (HeaderLength <= sizeof(IPHeader)) + DEBUGCHK; +#endif + + OptInfo->ioi_options = (uchar *)(Header + 1); + OptInfo->ioi_optlength = HeaderLength - sizeof(IPHeader); + + + // We have options of some sort. The packet may or may not be bound for us. + Index.oi_srindex = MAX_OPT_SIZE; + if ((ErrIndex = ParseRcvdOptions(OptInfo, &Index)) < MAX_OPT_SIZE) { + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, PTR_VALID, + ((ulong)ErrIndex + sizeof(IPHeader))); + return DEST_INVALID; // Parameter error. + } + + // If there's no source route, or if the destination is a broadcast, we'll take + // it. If it is a broadcast DeliverToUser will forward it when it's done, and + // the forwarding code will reprocess the options. + if (Index.oi_srindex == MAX_OPT_SIZE || IS_BCAST_DEST(DestType)) + return DEST_LOCAL; + else + return DEST_REMOTE; + +} + +//* TDUserRcv - Completion routing for a user transfer data. +// +// This is the completion handle for TDs invoked because we need to give data to a +// upper layer client. All we really do is call the upper layer handler with +// the data. +// +// Input: NetContext - Pointer to the net table entry on which we received this. +// Packet - Packet we received into. +// Status - Final status of copy. +// DataSize - Size in bytes of data transferred. +// +// Returns: Nothing +// +void +TDUserRcv(void *NetContext, PNDIS_PACKET Packet, NDIS_STATUS Status, + uint DataSize) +{ + NetTableEntry *NTE = (NetTableEntry *)NetContext; + Interface *SrcIF; + TDContext *Context = (TDContext *)Packet->ProtocolReserved; + CTELockHandle Handle; + uchar DestType; + IPRcvBuf RcvBuf; + IPOptInfo OptInfo; + IPHeader *Header; + + if (Status == NDIS_STATUS_SUCCESS) { + Header = (IPHeader *)Context->tdc_header; + OptInfo.ioi_ttl = Header->iph_ttl; + OptInfo.ioi_tos = Header->iph_tos; + OptInfo.ioi_flags = (net_short(Header->iph_offset) >> 13) & IP_FLAG_DF; + if (Context->tdc_hlength != sizeof(IPHeader)) { + OptInfo.ioi_options = (uchar *)(Header + 1); + OptInfo.ioi_optlength = Context->tdc_hlength - sizeof(IPHeader); + } else { + OptInfo.ioi_options = (uchar *)NULL; + OptInfo.ioi_optlength = 0; + } + + DestType = Context->tdc_dtype; + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_owner = IPR_OWNER_IP; + RcvBuf.ipr_buffer = (uchar *)Context->tdc_buffer; + RcvBuf.ipr_size = DataSize; + + DeliverToUser(NTE, Context->tdc_nte, Header, Context->tdc_hlength, + &RcvBuf, DataSize, &OptInfo, DestType); + // If it's a broadcast packet forward it on. + if (IS_BCAST_DEST(DestType)) + IPForward(NTE, Header, Context->tdc_hlength, RcvBuf.ipr_buffer, DataSize, + NULL, 0, DestType); + } + + SrcIF = NTE->nte_if; + CTEGetLockAtDPC(&SrcIF->if_lock, &Handle); + + Context->tdc_common.pc_link = SrcIF->if_tdpacket; + SrcIF->if_tdpacket = Packet; + CTEFreeLockFromDPC(&SrcIF->if_lock, Handle); + + return; + +} + + +//* IPRcv - Receive an incoming IP datagram. +// +// This is the routine called by the link layer module when an incoming IP +// datagram is to be processed. We validate the datagram (including doing +// the xsum), copy and process incoming options, and decide what to do with it. +// +// Entry: MyContext - The context valued we gave to the link layer. +// Data - Pointer to the data buffer. +// DataSize - Size in bytes of the data buffer. +// TotalSize - Total size in bytes available. +// LContext1 - 1st link context. +// LContext2 - 2nd link context. +// BCast - Indicates whether or not packet was received on bcast address. +// +// Returns: Nothing. +// +void +IPRcv(void *MyContext, void *Data, uint DataSize, uint TotalSize, NDIS_HANDLE LContext1, + uint LContext2, uint BCast) +{ + IPHeader UNALIGNED *IPH = (IPHeader UNALIGNED *)Data; + NetTableEntry *NTE = (NetTableEntry *)MyContext; // Local NTE received on + NetTableEntry *DestNTE; // NTE to receive on. + Interface *RcvIF; // Interface corresponding to NTE. + CTELockHandle Handle; + PNDIS_PACKET TDPacket; // NDIS packet used for TD. + TDContext *TDC = (TDContext *)NULL; // Transfer data context. + NDIS_STATUS Status; + IPAddr DAddr; // Dest. IP addr. of received packet. + uint HeaderLength; // Size in bytes of received header. + uint IPDataLength; // Length in bytes of IP (including UL) data in packet. + IPOptInfo OptInfo; // Incoming header information. + uchar DestType; // Type (LOCAL, REMOTE, SR) of Daddr. + IPRcvBuf RcvBuf; + +#if 0 + CTECheckMem(RcvFile); // Check heap status. +#endif + + IPSInfo.ipsi_inreceives++; + + // Make sure we actually have data. + if (DataSize) { + + // Check the header length, the xsum and the version. If any of these + // checks fail silently discard the packet. + HeaderLength = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2); + if (HeaderLength >= sizeof(IPHeader) && HeaderLength <= DataSize && + xsum(Data, HeaderLength) == (ushort)0xffff) { + + // Check the version, and sanity check the total length. + IPDataLength = (uint)net_short(IPH->iph_length); + if ((IPH->iph_verlen & IP_VER_FLAG) == IP_VERSION && + IPDataLength > sizeof(IPHeader) && IPDataLength <= TotalSize) { + + IPDataLength -= HeaderLength; + Data = (uchar *)Data + HeaderLength; + DataSize -= HeaderLength; + + DAddr = IPH->iph_dest; + DestNTE = NTE; + + // Find local NTE, if any. + DestType = GetLocalNTE(DAddr, &DestNTE); + + // Check to see if this is a non-broadcast IP address that + // came in as a link layer broadcast. If it is, throw it out. + // This is an important check for DHCP, since if we're + // DHCPing an interface all otherwise unknown addresses will + // come in as DEST_LOCAL. This check here will throw them out + // if they didn't come in as unicast. + + if (BCast && !IS_BCAST_DEST(DestType)) { + IPSInfo.ipsi_inhdrerrors++; + return; // Non bcast packet on bcast address. + } + + OptInfo.ioi_ttl = IPH->iph_ttl; + OptInfo.ioi_tos = IPH->iph_tos; + OptInfo.ioi_flags = (net_short(IPH->iph_offset) >> 13) & + IP_FLAG_DF; + OptInfo.ioi_options = (uchar *)NULL; + OptInfo.ioi_optlength = 0; + + if (DestType < DEST_REMOTE) { + // It's either local or some sort of broadcast. + + // The data probably belongs at this station. If there + // aren't any options, it definetly belongs here, and we'll + // dispatch it either to our reasssmbly code or to the + // deliver to user code. If there are options, we'll check + // them and then either handle the packet locally or pass it + // to our forwarding code. + + if (HeaderLength != sizeof(IPHeader)) { + // We have options. + + uchar NewDType; + NewDType = CheckLocalOptions(NTE, IPH, &OptInfo, + DestType); + if (NewDType != DEST_LOCAL) { + if (NewDType == DEST_REMOTE) + goto forward; + else { + IPSInfo.ipsi_inhdrerrors++; + return; // Bad Options. + } + } + } + + // Before we go further, if we have a filter installed + // call it to see if we should take this. + + if (ForwardFilterPtr != NULL) { + FORWARD_ACTION Action; + + Action = (*ForwardFilterPtr)(IPH, + Data, + DataSize, + NTE->nte_if->if_filtercontext, + NULL); + + if (Action != FORWARD) { + IPSInfo.ipsi_indiscards++; + return; + } + } + + // No options. See if it's a fragment. If it is, call our + // reassembly handler. + if ((IPH->iph_offset & ~(IP_DF_FLAG | IP_RSVD_FLAG)) == 0) { + + // We don't have a fragment. If the data all fits, + // handle it here. Otherwise transfer data it. + +#ifdef VXD + if (IPDataLength > DataSize) { + // Data isn't all in the buffer. +#else + // Make sure data is all in buffer, and directly + // accesible. + if ((IPDataLength > DataSize) || + !(NTE->nte_flags & NTE_COPY)) { +#endif + // The data isn't all here. Transfer data it. + RcvIF = NTE->nte_if; + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDPacket = RcvIF->if_tdpacket; + + if (TDPacket != (PNDIS_PACKET)NULL) { + + TDC = (TDContext *)TDPacket->ProtocolReserved; + RcvIF->if_tdpacket = TDC->tdc_common.pc_link; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + + TDC->tdc_nte = DestNTE; + TDC->tdc_dtype = DestType; + TDC->tdc_hlength = (uchar)HeaderLength; + CTEMemCopy(TDC->tdc_header, IPH, + HeaderLength + 8); + Status = (*(RcvIF->if_transfer))( + RcvIF->if_lcontext, LContext1, + LContext2, HeaderLength, IPDataLength, + TDPacket, &IPDataLength); + + // Check the status. If it's success, call the + // receive procedure. Otherwise, if it's pending + // wait for the callback. + Data = TDC->tdc_buffer; + if (Status != NDIS_STATUS_PENDING) { + if (Status != NDIS_STATUS_SUCCESS) { + IPSInfo.ipsi_indiscards++; + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDC->tdc_common.pc_link = + RcvIF->if_tdpacket; + RcvIF->if_tdpacket = TDPacket; + CTEFreeLockFromDPC(&RcvIF->if_lock, + Handle); + return; + } + } else + return; // Status is pending. + } else { // Couldn't get a packet. + IPSInfo.ipsi_indiscards++; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + return; + } + } + + RcvBuf.ipr_next = NULL; + RcvBuf.ipr_owner = IPR_OWNER_IP; + RcvBuf.ipr_buffer = (uchar *)Data; + RcvBuf.ipr_size = IPDataLength; + // When we get here, we have the whole packet. Deliver + // it. + DeliverToUser(NTE, DestNTE, IPH, HeaderLength, &RcvBuf, + IPDataLength, &OptInfo, DestType); + // When we're here, we're through with the packet + // locally. If it's a broadcast packet forward it on. + if (IS_BCAST_DEST(DestType)) { + IPForward(NTE, IPH, HeaderLength, Data, IPDataLength, + NULL, 0, DestType); + } + if (TDC != NULL) { + CTEGetLockAtDPC(&RcvIF->if_lock, &Handle); + TDC->tdc_common.pc_link = RcvIF->if_tdpacket; + RcvIF->if_tdpacket = TDPacket; + CTEFreeLockFromDPC(&RcvIF->if_lock, Handle); + } + return; + } else { + // This is a fragment. Reassemble it. + IPReassemble(NTE, DestNTE, IPH, HeaderLength, Data, + DataSize, IPDataLength, DestType, LContext1, + LContext2); + return; + } + + } + + // Not for us, may need to be forwarded. It might be an outgoing + // broadcast that came in through a source route, so we need to + // check that. +forward: + if (DestType != DEST_INVALID) + IPForward(NTE, IPH, HeaderLength, Data, DataSize, + LContext1, LContext2, DestType); + else + IPSInfo.ipsi_inaddrerrors++; + return; + } // Bad version + } // Bad checksum + + } // No data + + IPSInfo.ipsi_inhdrerrors++; +} + +//* IPTDComplete - IP Transfer data complete handler. +// +// This is the routine called by the link layer when a transfer data completes. +// +// Entry: MyContext - Context value we gave to the link layer. +// Packet - Packet we originally gave to transfer data. +// Status - Final status of command. +// BytesCopied - Number of bytes copied. +// +// Exit: Nothing +// +void +IPTDComplete(void *MyContext, PNDIS_PACKET Packet, NDIS_STATUS Status, uint BytesCopied) +{ + TDContext *TDC = (TDContext *)Packet->ProtocolReserved; + + if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_FW)) + if (!(TDC->tdc_common.pc_flags & PACKET_FLAG_RA)) + TDUserRcv(MyContext, Packet, Status, BytesCopied); + else + RATDComplete(MyContext, Packet, Status, BytesCopied); + else + SendFWPacket(Packet, Status, BytesCopied); + + +} diff --git a/private/ntos/tdi/tcpip/ip/iproute.c b/private/ntos/tdi/tcpip/ip/iproute.c new file mode 100644 index 000000000..3a0bb109d --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iproute.c @@ -0,0 +1,4703 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1995 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** iproute.c - IP routing routines. +// +// This file contains all the routines related to IP routing, including +// routing table lookup and management routines. + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "ipinit.h" +#include "info.h" +#include "tdistat.h" +#include "iproute.h" +#include "iprtdef.h" +#include "ipxmit.h" +#include "igmp.h" +#include "tdiinfo.h" +#include "ipfilter.h" + +extern NetTableEntry *NetTableList; // Pointer to the net table. +extern NetTableEntry *DHCPNTE; // Pointer to NTE being DHCP'd. + +extern NetTableEntry *LoopNTE; // Pointer to loopback NTE. +extern Interface LoopInterface; // Pointer to loopback interface. + +extern AddrTypeCache ATCache[]; +extern int ATCIndex; + +extern IP_STATUS SendICMPErr(IPAddr, IPHeader UNALIGNED *, uchar, uchar, ulong); +extern uchar ParseRcvdOptions(IPOptInfo *, OptIndex *); +extern void ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr, + uint NewMTU); + +extern Interface *IFList; +extern Interface *FirstIF; + +#define ROUTE_TABLE_SIZE 32 // Size of the route table. +DEFINE_LOCK_STRUCTURE(RouteTableLock) + +#define FWPACKET_GROW_AMOUNT 20 + +#define FW_BUF_SIZE 256 // Size of a forwarding buffer. + +#define FW_BUF_GROW_AMOUNT 30720 // Enough for 20 Ethernet packets. + +#define NO_SR 0 + +RouteTableEntry *RouteTable[ROUTE_TABLE_SIZE]; + +DEFINE_LOCK_STRUCTURE(FWPacketFreeLock) +DEFINE_LOCK_STRUCTURE(FWBufFreeLock) + +PNDIS_PACKET FWPacketFree; // Free list of forwarding packets. +PNDIS_BUFFER FWBufFree; // Free list of forwarding buffers. + +uint MaxFWPackets; // Maximum number of forward packets allowed. +uint CurrentFWPackets; // Number of forwarding packets currently + // allocated. +uint MaxFWBufferSize; // Maximum number of forwarding buffers allowed. +uint CurrentFWBufferSize; // Number of forwarding buffers allocated. + +uchar ForwardPackets; // Flag indicating whether we should forward. +uchar RouterConfigured; // TRUE if we were initially + // configured as a router. +uchar ForwardBCast; // Flag indicating if we should forward bcasts. +RouteSendQ *BCastRSQ; + +uint DefGWConfigured; // Number of default gateways configed. +uint DefGWActive; // Number of def. gateways active. + +uint DeadGWDetect; +uint PMTUDiscovery; + +ProtInfo *RtPI = NULL; + +IPMask IPMaskTable[] = { + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSA_MASK, + CLASSB_MASK, + CLASSB_MASK, + CLASSB_MASK, + CLASSB_MASK, + CLASSC_MASK, + CLASSC_MASK, + CLASSD_MASK, + CLASSE_MASK }; + +extern void TransmitFWPacket(PNDIS_PACKET, uint); + +uint MTUTable[] = { + + 65535 - sizeof(IPHeader), + 32000 - sizeof(IPHeader), + 17914 - sizeof(IPHeader), + 8166 - sizeof(IPHeader), + 4352 - sizeof(IPHeader), + 2002 - sizeof(IPHeader), + 1492 - sizeof(IPHeader), + 1006 - sizeof(IPHeader), + 508 - sizeof(IPHeader), + 296 - sizeof(IPHeader), + MIN_VALID_MTU - sizeof(IPHeader) +}; + +CTETimer IPRouteTimer; + +// Pointer to callout routine for dial on demand. +IPMapRouteToInterfacePtr DODCallout; + +// Pointer to packet filter handler. +IPPacketFilterPtr ForwardFilterPtr; + +RouteInterface DummyInterface; // Dummy interface. + +#ifdef NT +#ifdef ALLOC_PRAGMA +// +// Make init code disposable. +// +int InitRouting(IPConfigInfo *ci); + +#pragma alloc_text(INIT, InitRouting) + +#endif // ALLOC_PRAGMA +#endif // NT + + +#define InsertAfterRTE(P, R) (R)->rte_next = (P)->rte_next;\ + (P)->rte_next = (R) + +#define InsertRTE(R) {\ + RouteTableEntry *__P__; \ + __P__ = FindInsertPoint((R)); \ + InsertAfterRTE(__P__, (R)); \ + } + +#define RemoveRTE(P, R) (P)->rte_next = (R)->rte_next; + +//** DuumyXmit - Dummy interface transmit handler. +// +// A dummy routine that should never be called. +// +// Entry: Context - NULL. +// Packet - Pointer to packet to be transmitted. +// Dest - Destination addres of packet. +// RCE - Pointer to RCE (should be NULL). +// +// Returns: NDIS_STATUS_PENDING +// +NDIS_STATUS +DummyXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE) +{ + DbgPrint("TCPIP: Dummy Xmit called - NOT GOOD\n"); + CTEAssert(FALSE); + return NDIS_STATUS_SUCCESS; +} + +//* DummyXfer - Dummy interface transfer data routine. +// +// A dummy routine that should never be called. +// +// Entry: Context - NULL. +// TDContext - Original packet that was sent. +// Dummy - Unused +// Offset - Offset in frame from which to start copying. +// BytesToCopy - Number of bytes to copy. +// DestPacket - Packet describing buffer to copy into. +// BytesCopied - Place to return bytes copied. +// +// Returns: NDIS_STATUS_SUCCESS +// +NDIS_STATUS +DummyXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy, + PNDIS_PACKET DestPacket, uint *BytesCopied) +{ + DbgPrint("TCPIP: DummyXfer called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return NDIS_STATUS_FAILURE; +} + +//* DummyClose - Dummy close routine. +// +// A dummy routine that should never be called. +// +// Entry: Context - Unused. +// +// Returns: Nothing. +// +void +DummyClose(void *Context) +{ + DbgPrint("TCPIP: Dummy Close called - NOT GOOD\n"); + + CTEAssert(FALSE); +} + +//* DummyInvalidate - . +// +// A dummy routine that should never be called. +// +// Entry: Context - Unused. +// RCE - Pointer to RCE to be invalidated. +// +// Returns: Nothing. +// +void +DummyInvalidate(void *Context, RouteCacheEntry *RCE) +{ + DbgPrint("TCPIP: Dummy Invalidate called - NOT GOOD\n"); + + CTEAssert(FALSE); + +} + +//* DummyQInfo - Dummy query information handler. +// +// A dummy routine that should never be called. +// +// Input: IFContext - Interface context (unused). +// 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 +DummyQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size, + void *Context) +{ + DbgPrint("TCPIP: DummyQInfo called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return TDI_INVALID_REQUEST; +} + +//* DummySetInfo - Dummy query information handler. +// +// A dummy routine that should never be called. +// +// Input: IFContext - Interface context (unused). +// ID - TDIObjectID for object. +// Buffer - Buffer to put data into. +// Size - Pointer to size of buffer. On return, filled with +// bytes copied. +// +// Returns: Status of attempt to query information. +// +int +DummySetInfo(void *IFContext, TDIObjectID *ID, void *Buffer, uint Size) +{ + DbgPrint("TCPIP: DummySetInfo called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return TDI_INVALID_REQUEST; +} + +//* DummyAddAddr - Dummy add address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +DummyAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2) +{ + CTEAssert(FALSE); + + return TRUE; +} + +//* DummyDelAddr - Dummy del address routine. +// +// Called at init time when we need to initialize ourselves. +// +uint +DummyDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask) +{ + DbgPrint("TCPIP: DummyAddAddr called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return TRUE; +} + +//* DummyGetEList - Dummy get entity list. +// +// A dummy routine that should never be called. +// +// Input: Context - Unused. +// 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 +DummyGetEList(void *Context, TDIEntityID *EntityList, uint *Count) +{ + DbgPrint("TCPIP: DummyGetEList called - NOT GOOD\n"); + + CTEAssert(FALSE); + + return FALSE; +} + +#ifdef _PNP_POWER +//* DerefIF - Dereference an interface. +// +// Called when we need to dereference an interface. We decrement the +// refcount, and if it goes to zero we signal whoever is blocked on +// it. +// +// Input: IF - Interfaec to be dereferenced. +// +// Returns: Nothing. +// +void +DerefIF(Interface *IF) +{ + uint Original; + + Original = CTEInterlockedAddUlong( + &IF->if_refcount, + (ULONG)-1, + &RouteTableLock + ); + if (Original != 1) { + return; + } else { + // We just decremented the last reference. Wake whoever is + // blocked on it. + CTEAssert(IF->if_block != NULL); + CTESignal(IF->if_block, NDIS_STATUS_SUCCESS); + } +} + +//* LockedDerefIF - Dereference an interface w/RouteTableLock held. +// +// Called when we need to dereference an interface. We decrement the +// refcount, and if it goes to zero we signal whoever is blocked on +// it. The difference here is that we assume the caller already holds +// the RouteTableLock. +// +// Input: IF - Interfaec to be dereferenced. +// +// Returns: Nothing. +// +void +LockedDerefIF(Interface *IF) +{ + uint Original; + + IF->if_refcount--; + + if (IF->if_refcount != 0) { + return; + } else { + // We just decremented the last reference. Wake whoever is + // blocked on it. + CTEAssert(IF->if_block != NULL); + CTESignal(IF->if_block, NDIS_STATUS_SUCCESS); + } +} +#endif + +//* GetHashMask - Get mask to use with address when hashing. +// +// Called when we need to decide on the mask to use when hashing. If the +// supplied mask is the host mask or the default mask, we'll use that. Else +// if the supplied mask is at least as specific as the net mask, we'll use the +// net mask. Otherwise we drop back to the default mask. +// +// Input: Destination - Destination we'll be hashing on. +// Mask - Caller supplied mask. +// +// Returns: Mask to use. +// +IPMask +GetHashMask(IPAddr Destination, IPMask Mask) +{ + IPMask NetMask; + + if (Mask == HOST_MASK || Mask == DEFAULT_MASK) + return Mask; + + NetMask = IPNetMask(Destination); + + if ((NetMask & Mask) == NetMask) + return NetMask; + + return DEFAULT_MASK; + +} + +//** AddrOnIF - Check to see if a given address is local to an IF +// +// Called when we want to see if a given address is a valid local address +// for an interface. We walk down the chain of NTEs in the interface, and +// see if we get a match. We assume the caller holds the RouteTableLock +// at this point. +// +// Input: IF - Interface to check. +// Addr - Address to check. +// +// Returns: TRUE if Addr is an address for IF, FALSE otherwise. +// +uint +AddrOnIF(Interface *IF, IPAddr Addr) +{ + NetTableEntry *NTE; + + NTE = IF->if_nte; + while (NTE != NULL) { + if ((NTE->nte_flags & NTE_VALID) && IP_ADDR_EQUAL(NTE->nte_addr, Addr)) + return TRUE; + else + NTE = NTE->nte_ifnext; + } + + return FALSE; +} + +//** BestNTEForIF - Find the 'best match' NTE on a given interface. +// +// This is a utility function that takes an address and tries to find the +// 'best match' NTE on a given interface. This is really only useful when we +// have multiple IP addresses on a single interface. +// +// Input: Address - Source address of packet. +// IF - Pointer to IF to be searched. +// +// Returns: The 'best match' NTE. +// +NetTableEntry * +BestNTEForIF(IPAddr Address, Interface *IF) +{ + NetTableEntry *CurrentNTE, *FoundNTE; + + if (IF->if_nte != NULL) { + // Walk the list of NTEs, looking for a valid one. + CurrentNTE = IF->if_nte; + FoundNTE = NULL; + do { + if (CurrentNTE->nte_flags & NTE_VALID) { + if (IP_ADDR_EQUAL(Address & CurrentNTE->nte_mask, + CurrentNTE->nte_addr & CurrentNTE->nte_mask)) + return CurrentNTE; + else + if (FoundNTE == NULL) + FoundNTE = CurrentNTE; + + } + + CurrentNTE = CurrentNTE->nte_ifnext; + } while (CurrentNTE != NULL); + + // If we found a match, or we didn't and the destination is not + // a broadcast, return the result. We have special case code to + // handle broadcasts, since the interface doesn't really matter there. + if (FoundNTE != NULL || (!IP_ADDR_EQUAL(Address, IP_LOCAL_BCST) && + !IP_ADDR_EQUAL(Address, IP_ZERO_BCST))) + return FoundNTE; + + } + + // An 'anonymous' I/F, or the address we're reaching is a broadcast and the + // first interface has no address. Find a valid (non-loopback) address. + for (CurrentNTE = NetTableList; CurrentNTE != NULL; + CurrentNTE = CurrentNTE->nte_next) { + if (CurrentNTE != LoopNTE && (CurrentNTE->nte_flags & NTE_VALID)) + return CurrentNTE; + + } + + return NULL; + +} + +//** IsBCastonNTE - Determine if the specified addr. is a bcast on a spec. NTE. +// +// This routine is called when we need to know if an address is a broadcast +// on a particular net. We check in the order we expect to be most common - a +// subnet bcast, an all ones broadcast, and then an all subnets broadcast. We +// return the type of broadcast it is, or return DEST_LOCAL if it's not a +// broadcast. +// +// Entry: Address - Address in question. +// NTE - NetTableEntry to check Address against. +// +// Returns: Type of broadcast. +// +uchar +IsBCastOnNTE(IPAddr Address, NetTableEntry *NTE) +{ + IPMask Mask; + IPAddr BCastAddr; + + BCastAddr = NTE->nte_if->if_bcast; + + if (NTE->nte_flags & NTE_VALID) { + + Mask = NTE->nte_mask; + + if(Mask != 0xFFFFFFFF) + { + if (IP_ADDR_EQUAL(Address, (NTE->nte_addr & Mask) | (BCastAddr & ~Mask))) + return DEST_SN_BCAST; + } + + // See if it's an all subnet's broadcast. + if (!CLASSD_ADDR(Address)) { + Mask = IPNetMask(Address); + + if (IP_ADDR_EQUAL(Address, + (NTE->nte_addr & Mask) | (BCastAddr & ~Mask))) + return DEST_BCAST; + } else { + // This is a class D address. If we're allowed to receive + // mcast datagrams, check our list. + + if (IGMPLevel == 2) { + IGMPAddr *AddrPtr; + CTELockHandle Handle; + + CTEGetLock(&NTE->nte_lock, &Handle); + AddrPtr = NTE->nte_igmplist; + while (AddrPtr != NULL) { + if (IP_ADDR_EQUAL(Address, AddrPtr->iga_addr)) + break; + else + AddrPtr = AddrPtr->iga_next; + } + + CTEFreeLock(&NTE->nte_lock, Handle); + if (AddrPtr != NULL) + return DEST_MCAST; + } + } + } + + // A global bcast is certainly a bcast on this net. + if (IP_ADDR_EQUAL(Address, BCastAddr)) + return DEST_BCAST; + + return DEST_LOCAL; + +} + +//** InvalidSourceAddress - Check to see if a source address is invalid. +// +// This function takes an input address and checks to see if it is valid +// if used as the source address of an incoming packet. An address is invalid +// if it's 0, -1, a Class D or Class E address, is a net or subnet broadcast, +// or has a 0 subnet or host part. +// +// Input: Address - Address to be check. +// +// Returns: FALSE if the address is not invalid, TRUE if it is invalid. +// +uint +InvalidSourceAddress(IPAddr Address) +{ + NetTableEntry *NTE; // Pointer to current NTE. + IPMask Mask; // Mask for address. + uchar Result; // Result of broadcast check. + IPMask SNMask; + IPAddr MaskedAddress; + IPAddr LocalAddress; + + + if ( !CLASSD_ADDR(Address) && + !CLASSE_ADDR(Address) && + !IP_ADDR_EQUAL(Address, IP_ZERO_BCST) && + !IP_ADDR_EQUAL(Address, IP_LOCAL_BCST) + ) { + // It's not an obvious broadcast. See if it's an all subnets + // broadcast, or has a zero host part. + Mask = IPNetMask(Address); + MaskedAddress = Address & Mask; + + if (!IP_ADDR_EQUAL(Address, MaskedAddress) && + !IP_ADDR_EQUAL(Address, (MaskedAddress | ~Mask)) + ) { + // It's not an all subnet's broadcast, and it has a non-zero + // host/subnet part. Walk our local IP addresses, and see if it's + // a subnet broadcast. + NTE = NetTableList; + do { + + LocalAddress = NTE->nte_addr; + + if ((NTE->nte_flags & NTE_VALID) && + !IP_LOOPBACK(LocalAddress)) { + + Mask = NTE->nte_mask; + MaskedAddress = LocalAddress & Mask; + + if (IP_ADDR_EQUAL(Address, MaskedAddress) || + IP_ADDR_EQUAL(Address, + (MaskedAddress | + (NTE->nte_if->if_bcast & ~Mask)))) { + return TRUE; + } + } + + + NTE = NTE->nte_next; + } while (NTE != NULL); + + return FALSE; + } + } + + return TRUE; +} + +//** FlushATCache - Flush an address from the ATCache +// +// This function takes an input address, and removes it from the ATCache, +// if it is present. +// +// Input: Address - Address to be check. +// +// Returns: Destination type. +// +void +FlushATCache(IPAddr Address) +{ + uint i; + + + for (i=0; inte_addr, Address) && + (NTE->nte_flags & NTE_VALID)) { + Result = DEST_LOCAL; + goto gat_exit; + } + + if ((Result = IsBCastOnNTE(Address, NTE)) != DEST_LOCAL) { + goto gat_exit; + } + + // See if the destination has a valid host part. + if (NTE->nte_flags & NTE_VALID) { + SNMask = NTE->nte_mask; + if (IP_ADDR_EQUAL(Address & SNMask, NTE->nte_addr & SNMask)) { + // On this subnet. See if the host part is invalid. + + if (IP_ADDR_EQUAL(Address & SNMask, Address)) { + Result = DEST_INVALID; // Invalid 0 host part. + goto gat_exit; + } + } + } + NTE = NTE->nte_next; + } while (NTE != NULL); + + // It's not a local address, see if it's loopback. + if (IP_LOOPBACK(Address)) { + Result = DEST_LOCAL; + goto gat_exit; + } + + // If we're doing IGMP, see if it's a Class D address. If it it, + // return that. + if (CLASSD_ADDR(Address)) { + if (IGMPLevel != 0) { + Result = DEST_REM_MCAST; + goto gat_exit; + } + else { + Result = DEST_INVALID; + goto gat_exit; + } + } + + Mask = IPNetMask(Address); + + // Now check remote broadcast. When we get here we know that the + // address is not a global broadcast, a subnet broadcast for a subnet + // of which we're a member, or an all-subnets broadcast for a net of + // which we're a member. Since we're avoiding making assumptions about + // all subnet of a net having the same mask, we can't really check for + // a remote subnet broadcast. We'll use the net mask and see if it's + // a remote all-subnet's broadcast. + if (IP_ADDR_EQUAL(Address, (Address & Mask) | (IP_LOCAL_BCST & ~Mask))) { + Result = DEST_REM_BCAST; + goto gat_exit; + } + + // Check for invalid 0 parts. All we can do from here is see if he's + // sending to a remote net with all zero subnet and host parts. We + // can't check to see if he's sending to a remote subnet with an all + // zero host part. + if (IP_ADDR_EQUAL(Address, Address & Mask) || + IP_ADDR_EQUAL(Address, NULL_IP_ADDR)) { + Result = DEST_INVALID; + goto gat_exit; + } + + // Must be remote. + Result = DEST_REMOTE; + goto gat_exit; + } + + Result = DEST_INVALID; + +gat_exit: + + ++ATCIndex; + + i = ATCIndex & ATC_MASK; + + ATCache[i].atc_addr = Address; + ATCache[i].atc_type = Result; + ATCache[i].atc_flags = 1; + return(Result); + +} + +//** IPHash - IP hash function. +// +// This is the function to compute the hash index from a masked address. +// +// Input: Address - Masked address to be hashed. +// +// Returns: Hashed value. +// +uint +IPHash(IPAddr Address) +{ + uchar *i = (uchar *)&Address; + return (i[0] + i[1] + i[2] + i[3]) & (ROUTE_TABLE_SIZE-1); +} + +//** GetLocalNTE - Get the local NTE for an incoming packet. +// +// Called during receive processing to find a matching NTE for a packet. +// First we check against the NTE we received it on, then against any NTE. +// +// Input: Address - The dest. address of the packet. +// NTE - Pointer to NTE packet was received on - filled in on +// exit w/correct NTE. +// +// Returns: DEST_LOCAL if the packet is destined for this host, DEST_REMOTE if it needs to +// be routed, DEST_SN_BCAST or DEST_BCAST if it's some sort of a broadcast. +uchar +GetLocalNTE(IPAddr Address, NetTableEntry **NTE) +{ + NetTableEntry *LocalNTE = *NTE; + IPMask Mask; + uchar Result; + int i; + Interface *LocalIF; + NetTableEntry *OriginalNTE; + + // Quick check to see if it is for the NTE it came in on (the common case). + if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) && + (LocalNTE->nte_flags & NTE_VALID)) + return DEST_LOCAL; // For us, just return. + + // Now check to see if it's a broadcast of some sort on the interface it + // came in on. + if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL) + return Result; + + // The common cases failed us. Loop through the NetTable and see if + // it is either a valid local address or is a broadcast on one of the NTEs + // on the incoming interface. We won't check the NTE we've already looked + // at. We look at all NTEs, including the loopback NTE, because a loopback + // frame could come through here. Also, frames from ourselves to ourselves + // will come in on the loopback NTE. + + i = 0; + LocalIF = LocalNTE->nte_if; + OriginalNTE = LocalNTE; + LocalNTE = NetTableList; + do { + if (LocalNTE != OriginalNTE) { + if (IP_ADDR_EQUAL(Address, LocalNTE->nte_addr) && + (LocalNTE->nte_flags & NTE_VALID)) { + *NTE = LocalNTE; + return DEST_LOCAL; // For us, just return. + } + + // If this NTE is on the same interface as the NTE it arrived on, + // see if it's a broadcast. + if (LocalIF == LocalNTE->nte_if) + if ((Result = IsBCastOnNTE(Address, LocalNTE)) != DEST_LOCAL) { + *NTE = LocalNTE; + return Result; + } + + } + + LocalNTE = LocalNTE->nte_next; + + } while (LocalNTE != NULL); + + // It's not a local address, see if it's loopback. + if (IP_LOOPBACK(Address)) { + *NTE = LoopNTE; + return DEST_LOCAL; + } + + // If it's a class D address and we're receiveing multicasts, handle it + // here. + if (CLASSD_ADDR(Address)) { + if (IGMPLevel != 0) + return DEST_REM_MCAST; + else + return DEST_INVALID; + } + + // It's not local. Check to see if maybe it's a net broadcast for a net + // of which we're not a member. If so, return remote bcast. We can't check + // for subnet broadcast of subnets for which we're not a member, since we're + // not making assumptions about all subnets of a single net having the + // same mask. If we're here it's not a subnet broadcast for a net of which + // we're a member, so we don't know a subnet mask for it. We'll just use + // the net mask. + Mask = IPNetMask(Address); + if (IP_ADDR_EQUAL(Address, (Address & Mask) | + ((*NTE)->nte_if->if_bcast & ~Mask))) + return DEST_REM_BCAST; + + // If it's to the 0 address, or a Class E address, or has an all-zero + // subnet and net part, it's invalid. + + + if (IP_ADDR_EQUAL(Address, IP_ZERO_BCST) || + IP_ADDR_EQUAL(Address, (Address & Mask)) || + CLASSE_ADDR(Address)) + return DEST_INVALID; + + // If we're DHCPing the interface on which this came in we'll accept this. + // If it came in as a broadcast a check in IPRcv() will reject it. If it's + // a unicast to us we'll pass it up. + if (DHCPNTE != NULL && DHCPNTE == *NTE) { + return DEST_LOCAL; + } + + return DEST_REMOTE; +} + + +//** FindSpecificRTE - Look for a particular RTE. +// +// Called when we're adding a route and want to find a particular RTE. +// We take in the destination, mask, first hop, and src addr, and search +// the appropriate routeing table chain. We assume the caller has the +// RouteTableLock held. If we find the match, we'll return a pointer to the +// RTE with the RTE lock held, as well as a pointer to the previous RTE in +// the chain. +// +// Input: Dest - Destination to search for. +// Mask - Mask for destination. +// FirstHop - FirstHop to Dest. +// OutIF - Pointer to outgoing interface structure. +// PrevRTE - Place to put PrevRTE, if found. +// +// Returns: Pointer to matching RTE if found, or NULL if not. +// +RouteTableEntry * +FindSpecificRTE(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF, + RouteTableEntry **PrevRTE) +{ + uint Index; + IPMask HashMask; + RouteTableEntry *TempRTE, *CurrentRTE; + + HashMask = GetHashMask(Dest, Mask); + + Index = IPHash(Dest & HashMask); + TempRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next); + CurrentRTE = TempRTE->rte_next; + + // + // If this has been called because user mode was trying to set the route + // to INVALID, then the OUTIF will be DummyInterface, but we want to match + // any interface, since the IF will have already been plumbed by DODCallOut + // + + if(OutIF == (Interface *)&DummyInterface) + { + // + // Match everything but the interface + // + + while (CurrentRTE != NULL) + { + if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) && + CurrentRTE->rte_mask == Mask && + IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop)) + { + break; + } + + TempRTE = CurrentRTE; + CurrentRTE = CurrentRTE->rte_next; + } + } + else + { + // Walk the table, looking for a match. + while (CurrentRTE != NULL) { + // See if everything matches. + if (IP_ADDR_EQUAL(CurrentRTE->rte_dest,Dest) && + CurrentRTE->rte_mask == Mask && + IP_ADDR_EQUAL(CurrentRTE->rte_addr, FirstHop) && + CurrentRTE->rte_if == OutIF) + break; + + TempRTE = CurrentRTE; + CurrentRTE = CurrentRTE->rte_next; + } + } + + *PrevRTE = TempRTE; + return CurrentRTE; + +} + +//** IsRouteICMP - This function is used by Router Discovery to determine +// how we learned about the route. We are not allowed to update or timeout +// routes that were not learned about via icmp. If the route is new then +// we treat it as icmp and add a new entry. +// Input: Dest - Destination to search for. +// Mask - Mask for destination. +// FirstHop - FirstHop to Dest. +// OutIF - Pointer to outgoing interface structure. +// +// Returns: TRUE if learned via ICMP, FALSE otherwise. +// +uint +IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop, Interface *OutIF) +{ + RouteTableEntry *RTE; + RouteTableEntry *TempRTE; + + RTE = FindSpecificRTE(Dest, Mask, FirstHop, OutIF, &TempRTE); + + if (RTE == NULL) + return(TRUE); + + if (RTE->rte_proto == IRE_PROTO_ICMP) { + return(TRUE); + } else { + return(FALSE); + } +} + + +//** FindRTE - Find a matching RTE in a hash table chain. +// +// Called when we want to find a matching RTE. We take in a destination, +// a source, a hash index, and a maximum priority, and walk down the +// chain specified by the index looking for a matching RTE. If we can find +// one, we'll keep looking hoping for a match on the source address. +// +// The caller must hold the RouteTableLock before calling this function. +// +// Input: Dest - Destination we're trying to reach. +// Source - Source address to match. +// Index - Index of chain to search. +// MaxPri - Maximum acceptable priority. +// MinPri - Minimum acceptable priority. +// +// Returns: Pointer to RTE if found, or NULL if not. +// +RouteTableEntry * +FindRTE(IPAddr Dest, IPAddr Source, uint Index, uint MaxPri, uint MinPri) +{ + RouteTableEntry *CurrentRTE; + uint RTEPri; + uint Metric; + RouteTableEntry *FoundRTE; + + // First walk down the chain, skipping those RTEs that have a + // a priority greater than what we want. + + CurrentRTE = RouteTable[Index]; + + for (;;) { + if (CurrentRTE == NULL) + return NULL; // Hit end of chain, bounce out. + + if (CurrentRTE->rte_priority <= MaxPri) + break; // He's a possible match. + + // Priority is too big. Try the next one. + CurrentRTE = CurrentRTE->rte_next; + } + + FoundRTE = NULL; + + // When we get here, we have a locked RTE with a priority less than or + // equal to what was specifed. + // Examine it, and if it doesn't match try the next one. If it does match + // we'll stash it temporarily and keep looking for one that matches the + // specifed source. + for (;;) { + + // The invariant at the top of this loop is that CurrentRTE points to + // a candidate RTE, locked with the handle in CurrentHandle. + + if (CurrentRTE->rte_flags & RTE_VALID) { + // He's valid. Make sure he's at least the priority we need. If + // he is, see if he matches. Otherwise we're done. + + if (CurrentRTE->rte_priority < MinPri) { + // His priority is too small. Since the list is in sorted order, + // all following routes must have too low a priority, so we're + // done. + return NULL; + } + + if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask, CurrentRTE->rte_dest)) { + // He's valid for this route. Save the current information, + // and look for a matching source. + FoundRTE = CurrentRTE; + + if (!IP_ADDR_EQUAL(Source, NULL_IP_ADDR) && + !AddrOnIF(CurrentRTE->rte_if, Source)) { + RTEPri = CurrentRTE->rte_priority; + Metric = CurrentRTE->rte_metric; + + CurrentRTE = CurrentRTE->rte_next; + + // We've save the info. Starting at the next RTE, look for + // an RTE that matches both the mask criteria and the source + // address. The search will terminate when we hit the end + // of the list, or the RTE we're examing has a different + // (presumably lesser) priority (or greater metric), or we + // find a match. + while (CurrentRTE != NULL && + CurrentRTE->rte_priority == RTEPri && + CurrentRTE->rte_metric == Metric) { + + + // Skip invalid route types. + if (CurrentRTE->rte_flags & RTE_VALID) { + if (IP_ADDR_EQUAL(Dest & CurrentRTE->rte_mask, + CurrentRTE->rte_dest)) { + if (AddrOnIF(CurrentRTE->rte_if, Source)) { + // He matches the source. Free the old lock, + // and break out. + FoundRTE = CurrentRTE; + break; + } + } + + } + CurrentRTE = CurrentRTE->rte_next; + } + } + + // At this point, FoundRTE points to the RTE we want to return, + // and *Handle has the lock handle for the RTE. Break out. + break; + } + + } + + CurrentRTE = CurrentRTE->rte_next; + + if (CurrentRTE != NULL) { + continue; + } else + break; + } + + return FoundRTE; +} + +//* ValidateDefaultGWs - Mark all default gateways as valid. +// +// Called to one or all of our default gateways as up. The caller specifies +// the IP address of the one to mark as up, or NULL_IP_ADDR if they're all +// supposed to be marked up. We return a count of how many we marked as +// valid. +// +// Input: IP address of G/W to mark as up. +// +// Returns: Count of gateways marked as up. +// +uint +ValidateDefaultGWs(IPAddr Addr) +{ + RouteTableEntry *RTE; + uint Count = 0; + uint Now = CTESystemUpTime() / 1000L; + + RTE = RouteTable[IPHash(0)]; + + while (RTE != NULL) { + if (RTE->rte_mask == DEFAULT_MASK && !(RTE->rte_flags & RTE_VALID) && + (IP_ADDR_EQUAL(Addr, NULL_IP_ADDR) || + IP_ADDR_EQUAL(Addr, RTE->rte_addr))) { + RTE->rte_flags |= RTE_VALID; + RTE->rte_valid = Now; + Count++; + } + RTE = RTE->rte_next; + } + + DefGWActive += Count; + return Count; +} + +//* InvalidateRCEChain - Invalidate the RCEs on an RCE. +// +// Called to invalidate the RCE chain on an RTE. We assume the caller holds +// the route table lock. +// +// Input: RTE - RTE on which to invalidate RCEs. +// +// Returns: Nothing. +// +void +InvalidateRCEChain(RouteTableEntry *RTE) +{ + CTELockHandle RCEHandle; // Lock handle for RCE being updated. + RouteCacheEntry *TempRCE, *CurrentRCE; + Interface *OutIF; + + OutIF = RTE->rte_if; + + // If there is an RCE chain on this RCE, invalidate the RCEs on it. We still + // hold the RouteTableLock, so RCE closes can't happen. + + + CurrentRCE = RTE->rte_rcelist; + RTE->rte_rcelist = NULL; + + // Walk down the list, nuking each RCE. + while (CurrentRCE != NULL) { + + CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle); + + if (CurrentRCE->rce_flags & RCE_VALID) { + CTEAssert(CurrentRCE->rce_rte == RTE); + CurrentRCE->rce_flags &= ~RCE_VALID; + CurrentRCE->rce_rte = (RouteTableEntry *)OutIF; + if ((CurrentRCE->rce_flags & RCE_CONNECTED) && + CurrentRCE->rce_usecnt == 0) { + + (*(OutIF->if_invalidate))(OutIF->if_lcontext, CurrentRCE); +#ifdef _PNP_POWER + if (CurrentRCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(OutIF); + CurrentRCE->rce_flags &= ~RCE_REFERENCED; + } +#endif + } + } else + CTEAssert(FALSE); + + TempRCE = CurrentRCE->rce_next; + CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle); + CurrentRCE = TempRCE; + } + +} + +//** FindValidIFForRTE - Find a valid inteface for an RTE. +// +// Called when we're going to send a packet out a route that currently marked +// as disconnected. If we have a valid callout routine we'll call it to find +// the outgoing interface index, and set up the RTE to point at that interface. +// This routine is called with the RouteTableLock held. +// +// Input: RTE - A pointer to the RTE for the route being used. +// Destination - Destination IP address we're trying to reach. +// Source - Source IP address we're sending from. +// Protocol - Protocol type of packet that caused send. +// Buffer - Pointer to first part of packet that caused send. +// Length - Length of buffer. +// +// Returns: A pointer to the RTE, or NULL if that RTE couldn't be connected. +// +RouteTableEntry * +FindValidIFForRTE(RouteTableEntry *RTE, IPAddr Destination, IPAddr Source, + uchar Protocol, uchar *Buffer, uint Length) +{ + uint NewIFIndex; + Interface *NewIF; + NetTableEntry *NewNTE; + + if (DODCallout != NULL) { + // There is a callout. See if it can help us. + NewIFIndex = (*DODCallout)(RTE->rte_context, Destination, Source, + Protocol, Buffer, Length); + if (NewIFIndex != INVALID_IF_INDEX) { + // We got what should be a valid index. Walk our interface table list + // and see if we can find a matching interface structure. + for (NewIF = IFList; NewIF != NULL; NewIF = NewIF->if_next) { + if (NewIF->if_index == NewIFIndex) { + // Found one. + break; + } + } + if (NewIF != NULL) { + // We found a matching structure. Set the RTE interface to point + // to this, and mark as connected. + if (RTE->rte_addr != IPADDR_LOCAL) { + // See if the first hop of the route is a local address on this + // new interface. If it is, mark it as local. + for (NewNTE = NewIF->if_nte; NewNTE != NULL; + NewNTE = NewNTE->nte_ifnext) { + + // Don't look at him if he's not valid. + if (!(NewNTE->nte_flags & NTE_VALID)) { + continue; + } + + // See if the first hop in the RTE is equal to this IP + // address. + if (IP_ADDR_EQUAL(NewNTE->nte_addr, RTE->rte_addr)) { + // It is, so mark as local and quit looking. + RTE->rte_addr = IPADDR_LOCAL; + RTE->rte_type = IRE_TYPE_DIRECT; + break; + } + } + } + + // Set the RTE to the new interface, and mark him as valid. + RTE->rte_if = NewIF; + RTE->rte_flags |= RTE_IF_VALID; + RTE->rte_mtu = NewIF->if_mtu - sizeof(IPHeader); + return RTE; + } else + CTEAssert(FALSE); + } + } + + // Either the callout is NULL, or the callout couldn't map a inteface index. + return NULL; +} + +//** LookupRTE - Lookup a routing table entry. +// +// This routine looks up a routing table entry, and returns with the entry +// locked if it finds one. If it doesn't find one, it returns NULL. This +// routine assumes that the routing table is locked when it is called. +// +// The routeing table is organized as an open hash table. The table contains +// routes to hosts, subnets, and nets. Host routes are hashed on the host +// address, other non-default routes on the destination anded with the net +// mask, and default routes wind up in bucket 0. Within each bucket chain +// the routes are sorted with the greatest priority (i.e. number of bits in the +// route mask) first, and within each priority class the routes are sorted +// with the lowest metric first. The caller may specify a maximum priority +// for the route to be found. We look for routes in order of most specific to +// least specifc, i.e. first host routes, then other non-default routes, and +// finally default routes. We give preference to routes that are going out +// on an interface with an address that matches the input source address. +// +// It might be worthile in the future to split this up into multiple tables, +// so that we have a table for host routes, a table for non-default routes, +// and a list of default routes. +// +// Entry: Address - Address for which a route is to be found. +// Src - IPAddr of source (may be 0). +// MaxPri - Maximum priority of route to find. +// +// Returns: A pointer to the locked RTE if we find one, or NULL if we don't +// +RouteTableEntry * +LookupRTE(IPAddr Address, IPAddr Src, uint MaxPri) +{ + RouteTableEntry *RTE; + + // First try to find a host route, if we're allowed to. + if (MaxPri == HOST_ROUTE_PRI) { + RTE = FindRTE(Address, Src, IPHash(Address), MaxPri, MaxPri); + if (RTE != NULL) { + return RTE; + } + } + + // Don't have or weren't allowed to find a host route. See if we can + // find a non-default route. + if (MaxPri > DEFAULT_ROUTE_PRI) { + RTE = FindRTE(Address, Src, IPHash(Address & IPNetMask(Address)), + MaxPri, DEFAULT_ROUTE_PRI + 1); + if (RTE != NULL) { + return RTE; + } + } + + // No non-default route. Try a default route. + RTE = FindRTE(Address, Src, IPHash(0), MaxPri, DEFAULT_ROUTE_PRI); + + return RTE; + +} + +//** GetRouteContext - Routine to get the route context for a specific route. +// +// Called when we need to get the route context for a path, usually when we're adding +// a route derived from an existing route. We return the route context for the +// existing route, or NULL if we can't find one. +// +// Input: Destination - Destination address of path. +// Source - Source address of path. +// +// Returns: A ROUTE_CONTEXT, or NULL. +// +void * +GetRouteContext(IPAddr Destination, IPAddr Source) +{ + CTELockHandle Handle; + RouteTableEntry *RTE; + ROUTE_CONTEXT Context; + + CTEGetLock(&RouteTableLock, &Handle); + RTE = LookupRTE(Destination, Source, HOST_ROUTE_PRI); + if (RTE != NULL) { + Context = RTE->rte_context; + } else + Context = NULL; + + CTEFreeLock(&RouteTableLock, Handle); + + return(Context); +} + +//* FindInsertPoint - Find out where to insert an RTE in the table. +// +// Called to find out where to insert an RTE. We hash into the table, +// and walk the chain until we hit the end or we find an RTE with a +// lesser priority or a greater metric. Once we find the appropriate spot +// we return a pointer to the RTE immediately prior to the one we want to +// insert. We assume the caller holds the lock on the route table when calling +// this function. +// +// Input: InsertRTE - RTE we're going to (eventually) insert. +// +// Returns: Pointer to RTE in insert after. +// +RouteTableEntry * +FindInsertPoint(RouteTableEntry *InsertRTE) +{ + RouteTableEntry *PrevRTE, *CurrentRTE; + IPMask HashMask, Mask; + uint Priority, Metric; + uint Index; + + Priority = InsertRTE->rte_priority; + Metric = InsertRTE->rte_metric; + + // First figure out where he should go. We'll hash on the whole address + // if the mask allows us to, or on the net portion if this is a non-default + // route. + + Mask = InsertRTE->rte_mask; + HashMask = GetHashMask(InsertRTE->rte_dest, Mask); + + Index = IPHash(InsertRTE->rte_dest & HashMask); + + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[Index], rte_next); + CurrentRTE = PrevRTE->rte_next; + + // Walk the table, looking for a place to insert it. + while (CurrentRTE != NULL) { + if (CurrentRTE->rte_priority < Priority) { + break; + } + + if (CurrentRTE->rte_priority == Priority) { + // Priorities match. Check the metrics. + if (CurrentRTE->rte_metric > Metric) { + // Our metric is smaller than his, so we're done. + break; + } + } + + // Either his priority is greater than ours or his metric is less + // than or equal to ours. Check the next one. + PrevRTE = CurrentRTE; + CurrentRTE = CurrentRTE->rte_next; + } + + // At this point, we've either found the correct spot or hit the end + // of the list. + return PrevRTE; + +} + + +//** LookupNextHop - Look up the next hop +// +// Called when we need to find the next hop on our way to a destination. We +// call LookupRTE to find it, and return the appropriate information. +// +// In a PnP build, the interface is referenced here. +// +// Entry: Destination - IP address we're trying to reach. +// Src - Source address of datagram being routed. +// NextHop - Pointer to IP address of next hop (returned). +// MTU - Pointer to where to return max MTU used on the +// route. +// +// Returns: Pointer to outgoing interface if we found one, NULL otherwise. +// +Interface * +LookupNextHop(IPAddr Destination, IPAddr Src, IPAddr *NextHop, uint *MTU) +{ + CTELockHandle TableLock; // Lock handle for routing table. + RouteTableEntry *Route; // Pointer to route table entry for route. + Interface *IF; + + CTEGetLock(&RouteTableLock, &TableLock); + Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI); + + if (Route != (RouteTableEntry *)NULL) { + IF = Route->rte_if; + + // If this is a direct route, send straight to the destination. + *NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination : + Route->rte_addr; + + *MTU = Route->rte_mtu; +#ifdef _PNP_POWER + IF->if_refcount++; +#endif + CTEFreeLock(&RouteTableLock, TableLock); + return IF; + } else { // Couldn't find a route. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL; + } +} + +//** LookupNextHopWithBuffer - Look up the next hop, with packet information. +// +// Called when we need to find the next hop on our way to a destination and we +// have packet information that we may use for dial on demand support. We call +// LookupRTE to find it, and return the appropriate information. We may bring up +// the link if neccessary. +// +// In a PnP build, the interface is referenced here. +// +// Entry: Destination - IP address we're trying to reach. +// Src - Source address of datagram being routed. +// NextHop - Pointer to IP address of next hop (returned). +// MTU - Pointer to where to return max MTU used on the +// route. +// Protocol - Protocol type for packet that's causing this lookup. +// Buffer - Pointer to first part of packet causing lookup. +// Length - Length of Buffer. +// +// Returns: Pointer to outgoing interface if we found one, NULL otherwise. +// +Interface * +LookupNextHopWithBuffer(IPAddr Destination, IPAddr Src, IPAddr *NextHop, + uint *MTU, uchar Protocol, uchar *Buffer, uint Length) +{ + CTELockHandle TableLock; // Lock handle for routing table. + RouteTableEntry *Route; // Pointer to route table entry for route. + Interface *IF; + + CTEGetLock(&RouteTableLock, &TableLock); + Route = LookupRTE(Destination, Src, HOST_ROUTE_PRI); + + if (Route != (RouteTableEntry *)NULL) { + + // If this is a direct route, send straight to the destination. + *NextHop = IP_ADDR_EQUAL(Route->rte_addr, IPADDR_LOCAL) ? Destination : + Route->rte_addr; + + + // See if the route we found is connected. If not, try to connect it. + if (!(Route->rte_flags & RTE_IF_VALID)) { + Route = FindValidIFForRTE(Route, Destination, Src, Protocol, Buffer, + Length); + if (Route == NULL) { + // Couldn't bring it up. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL; + } else + IF = Route->rte_if; + } else + IF = Route->rte_if; + + *MTU = Route->rte_mtu; +#ifdef _PNP_POWER + IF->if_refcount++; +#endif + CTEFreeLock(&RouteTableLock, TableLock); + return IF; + } else { // Couldn't find a route. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL; + } +} + +//* RTReadNext - Read the next route in the table. +// +// Called by the GetInfo code to read the next route in the table. We assume +// the context passed in is valid, and the caller has the RouteTableLock. +// +// Input: Context - Pointer to a RouteEntryContext. +// Buffer - Pointer to an IPRouteEntry structure. +// +// Returns: TRUE if more data is available to be read, FALSE is not. +// +uint +RTReadNext(void *Context, void *Buffer) +{ + RouteEntryContext *REContext = (RouteEntryContext *)Context; + IPRouteEntry *IPREntry = (IPRouteEntry *)Buffer; + RouteTableEntry *CurrentRTE; + uint i; + uint Now = CTESystemUpTime() / 1000L; + Interface *IF; + NetTableEntry *SrcNTE; + + CurrentRTE = REContext->rec_rte; + + // Fill in the buffer. + IF = CurrentRTE->rte_if; + + IPREntry->ire_dest = CurrentRTE->rte_dest; + IPREntry->ire_index = IF->if_index; + IPREntry->ire_metric1 = CurrentRTE->rte_metric; + IPREntry->ire_metric2 = IRE_METRIC_UNUSED; + IPREntry->ire_metric3 = IRE_METRIC_UNUSED; + IPREntry->ire_metric4 = IRE_METRIC_UNUSED; + IPREntry->ire_metric5 = IRE_METRIC_UNUSED; + if (IP_ADDR_EQUAL(CurrentRTE->rte_addr, IPADDR_LOCAL)) { + SrcNTE = BestNTEForIF(CurrentRTE->rte_dest, IF); + if (IF->if_nte != NULL && SrcNTE != NULL) + IPREntry->ire_nexthop = SrcNTE->nte_addr; + else + IPREntry->ire_nexthop = IPREntry->ire_dest; + } else { + IPREntry->ire_nexthop = CurrentRTE->rte_addr; + } + IPREntry->ire_type = (CurrentRTE->rte_flags & RTE_VALID ? + CurrentRTE->rte_type : IRE_TYPE_INVALID); + IPREntry->ire_proto = CurrentRTE->rte_proto; + IPREntry->ire_age = Now - CurrentRTE->rte_valid; + IPREntry->ire_mask = CurrentRTE->rte_mask; + IPREntry->ire_context = CurrentRTE->rte_context; + + // We've filled it in. Now update the context. + if (CurrentRTE->rte_next != NULL) { + REContext->rec_rte = CurrentRTE->rte_next; + return TRUE; + } else { + // The next RTE is NULL. Loop through the RouteTable looking for a new + // one. + i = REContext->rec_index + 1; + while (i < ROUTE_TABLE_SIZE) { + if (RouteTable[i] != NULL) { + REContext->rec_rte = RouteTable[i]; + REContext->rec_index = i; + return TRUE; + break; + } else + i++; + } + + REContext->rec_index = 0; + REContext->rec_rte = NULL; + return FALSE; + } + +} + +//* RTValidateContext - Validate the context for reading the route table. +// +// Called to start reading the route 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 route table lock. +// +// Input: Context - Pointer to a RouteEntryContext. +// 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 +RTValidateContext(void *Context, uint *Valid) +{ + RouteEntryContext *REContext = (RouteEntryContext *)Context; + uint i; + RouteTableEntry *TargetRTE; + RouteTableEntry *CurrentRTE; + + i = REContext->rec_index; + TargetRTE = REContext->rec_rte; + + // If the context values are 0 and NULL, we're starting from the beginning. + if (i == 0 && TargetRTE == NULL) { + *Valid = TRUE; + do { + if ((CurrentRTE = RouteTable[i]) != NULL) { + break; + } + i++; + } while (i < ROUTE_TABLE_SIZE); + + if (CurrentRTE != NULL) { + REContext->rec_index = i; + REContext->rec_rte = CurrentRTE; + return TRUE; + } else + return FALSE; + + } else { + + // We've been given a context. We just need to make sure that it's + // valid. + + if (i < ROUTE_TABLE_SIZE) { + CurrentRTE = RouteTable[i]; + while (CurrentRTE != NULL) { + if (CurrentRTE == TargetRTE) { + *Valid = TRUE; + return TRUE; + break; + } else { + CurrentRTE = CurrentRTE->rte_next; + } + } + + } + + // If we get here, we didn't find the matching RTE. + *Valid = FALSE; + return FALSE; + + } + +} + + +//* DeleteRTE - Delete an RTE. +// +// Called when we need to delete an RTE. We assume the caller has the +// RouteTableLock. We'll splice out the RTE, invalidate his RCEs, and +// free the memory. +// +// Input: PrevRTE - RTE in 'front' of one being deleted. +// RTE - RTE to be deleted. +// +// Returns: Nothing. +// +void +DeleteRTE(RouteTableEntry *PrevRTE, RouteTableEntry *RTE) +{ + PrevRTE->rte_next = RTE->rte_next; // Splice him from the table. + IPSInfo.ipsi_numroutes--; + + if (RTE->rte_mask == DEFAULT_MASK) { + // We're deleting a default route. + DefGWConfigured--; + if (RTE->rte_flags & RTE_VALID) + DefGWActive--; + if (DefGWActive == 0) + ValidateDefaultGWs(NULL_IP_ADDR); + } + + InvalidateRCEChain(RTE); + // Free the old route. + CTEFreeMem(RTE); + +} + +//* DeleteRTEOnIF - Delete all RTEs on a particular IF. +// +// A function called by RTWalk when we want to delete all RTEs on a particular +// inteface. We just check the I/F of each RTE, and if it matches we return +// FALSE. +// +// Input: RTE - RTE to check. +// Context - Interface on which we're deleting. +// +// Returns: FALSE if we want to delete it, TRUE otherwise. +// +uint +DeleteRTEOnIF(RouteTableEntry *RTE, void *Context, void *Context1) +{ + Interface *IF = (Interface *)Context; + + if (RTE->rte_if == IF && !IP_ADDR_EQUAL(RTE->rte_dest, IF->if_bcast)) + return FALSE; + else + return TRUE; + +} + +//* InvalidateRCEOnIF - Invalidate all RCEs on a particular IF. +// +// A function called by RTWalk when we want to invalidate all RCEs on a +// particular inteface. We just check the I/F of each RTE, and if it +// matches we call InvalidateRCEChain to invalidate the RCEs. +// +// Input: RTE - RTE to check. +// Context - Interface on which we're invalidating. +// +// Returns: TRUE. +// +uint +InvalidateRCEOnIF(RouteTableEntry *RTE, void *Context, void *Context1) +{ + Interface *IF = (Interface *)Context; + + if (RTE->rte_if == IF) + InvalidateRCEChain(RTE); + + return TRUE; + +} + +//* SetMTUOnIF - Set the MTU on an interface. +// +// Called when we need to set the MTU on an interface. +// +// Input: RTE - RTE to check. +// Context - Pointer to a context. +// Context1 - Pointer to the new MTU. +// +// Returns: TRUE. +// +uint +SetMTUOnIF(RouteTableEntry *RTE, void *Context, void *Context1) +{ + uint NewMTU = *(uint *)Context1; + Interface *IF = (Interface *)Context; + + if (RTE->rte_if == IF) + RTE->rte_mtu = NewMTU; + + return TRUE; +} + +//* SetMTUToAddr - Set the MTU to a specific address. +// +// Called when we need to set the MTU to a specific address. We set the MTU +// for all routes that use the specified address as a first hop to the new +// MTU. +// +// Input: RTE - RTE to check. +// Context - Pointer to a context. +// Context1 - Pointer to the new MTU. +// +// Returns: TRUE. +// +uint +SetMTUToAddr(RouteTableEntry *RTE, void *Context, void *Context1) +{ + uint NewMTU = *(uint *)Context1; + IPAddr Addr = *(IPAddr *)Context; + + if (IP_ADDR_EQUAL(RTE->rte_addr, Addr)) + RTE->rte_mtu = NewMTU; + + return TRUE; +} + +//* RTWalk - Routine to walk the route table. +// +// This routine walks the route table, calling the specified function +// for each entry. If the called function returns FALSE, the RTE is +// deleted. +// +// Input: CallFunc - Function to call for each entry. +// Context - Context value to pass to each call. +// +// Returns: Nothing. +// +void +RTWalk(uint (*CallFunc)(struct RouteTableEntry *, void *, void *), + void *Context, void *Context1) +{ + uint i; + CTELockHandle Handle; + RouteTableEntry *RTE, *PrevRTE; + + CTEGetLock(&RouteTableLock, &Handle); + + for (i = 0; i < ROUTE_TABLE_SIZE; i++) { + + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next); + RTE = RouteTable[i]; + while (RTE != NULL) { + if (!(*CallFunc)(RTE, Context, Context1)) { + DeleteRTE(PrevRTE, RTE); + } else { + PrevRTE = RTE; + } + RTE = PrevRTE->rte_next; + } + } + + CTEFreeLock(&RouteTableLock, Handle); +} + +//** AttachRCEToRTE - Attach an RCE to an RTE. +// +// This procedure takes an RCE, finds the appropriate RTE, and attaches it. +// We check to make sure that the source address is still valid. +// +// Entry: RCE - RCE to be attached. +// Protocol - Protocol type for packet causing this call. +// Buffer - Pointer to buffer for packet causing this +// call. +// Length - Length of buffer. +// +// Returns: TRUE if we attach it, false if we don't. +// +uint +AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol, uchar *Buffer, uint Length) +{ + CTELockHandle TableHandle, RCEHandle; + RouteTableEntry *RTE; + NetTableEntry *NTE; + uint Status; + + + CTEGetLock(&RouteTableLock, &TableHandle); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if ((NTE->nte_flags & NTE_VALID) && + IP_ADDR_EQUAL(RCE->rce_src, NTE->nte_addr)) + break; + + if (NTE == NULL) { + // Didn't find a match. + CTEFreeLock(&RouteTableLock, TableHandle); + return FALSE; + } + + RTE = LookupRTE(RCE->rce_dest, RCE->rce_src, HOST_ROUTE_PRI); + + // See if we found an RTE. + if (RTE != NULL) { + + Status = TRUE; + + // Yep, we found one. Get the lock on the RCE, and make sure he's + // not pointing at an RTE already. We also need to make sure that the usecnt + // is 0, so that we can invalidate the RCE at the low level. If we set valid + // to TRUE without doing this we may get into a wierd situation where we + // link the RCE onto an RTE but the lower layer information is wrong, so we + // send to IP address X at mac address Y. So to be safe we don't set valid + // to TRUE until both usecnt is 0 and valid is FALSE. We'll keep coming + // through this routine on every send until that happens. + + CTEGetLock(&RCE->rce_lock, &RCEHandle); + if (RCE->rce_usecnt == 0) { + // Nobody is using him, so we can link him up. + if (!(RCE->rce_flags & RCE_VALID)) { + Interface *IF; + // He's not valid. Invalidate the lower layer info, just in + // case. Make sure he's connected before we try to do this. If + // he's not marked as connected, don't bother to try and invalidate + // him as there is no interface. + if (RCE->rce_flags & RCE_CONNECTED) { + IF = (Interface *)RCE->rce_rte; + (*(IF->if_invalidate))(IF->if_lcontext, RCE); +#ifdef _PNP_POWER + if (RCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(IF); + RCE->rce_flags &= ~RCE_REFERENCED; + } +#endif + } else { + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); + } + + // Link the RCE on the RTE, and set up the back pointer. + RCE->rce_rte = RTE; + RCE->rce_flags |= RCE_VALID; + RCE->rce_next = RTE->rte_rcelist; + RTE->rte_rcelist = RCE; + + // Make sure the RTE is connected. If not, try to connect him. + if (!(RTE->rte_flags & RTE_IF_VALID)) { + // Not connected. Try to connect him. + RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src, + Protocol, Buffer, Length); + if (RTE != NULL) { + // Got one, so mark as connected. + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); +#ifdef _PNP_POWER + RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED); + RTE->rte_if->if_refcount++; +#else + RCE->rce_flags |= RCE_CONNECTED; + +#endif + } else { + + // Couldn't get a valid i/f. Mark the RCE as not connected, + // and set up to fail this call. + CTEAssert(FALSE); + RCE->rce_flags &= ~RCE_CONNECTED; + Status = FALSE; + } + } else { + // The RTE is connected, mark the RCE as connected. + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); +#ifdef _PNP_POWER + RCE->rce_flags |= (RCE_CONNECTED | RCE_REFERENCED); + RTE->rte_if->if_refcount++; +#else + RCE->rce_flags |= RCE_CONNECTED; +#endif + } + } else { + // The RCE is valid. See if it's connected. + if (!(RCE->rce_flags & RCE_CONNECTED)) { + + // Not connected, try to get a valid i/f. + if (!(RTE->rte_flags & RTE_IF_VALID)) { + RTE = FindValidIFForRTE(RTE, RCE->rce_dest, RCE->rce_src, + Protocol, Buffer, Length); + if (RTE != NULL) { + RCE->rce_flags |= RCE_CONNECTED; +#ifdef _PNP_POWER + CTEAssert(!(RCE->rce_flags & RCE_REFERENCED)); + RCE->rce_flags |= RCE_REFERENCED; + RTE->rte_if->if_refcount++; +#endif + } else { + + // Couldn't connect, so fail. + CTEAssert(FALSE); + Status = FALSE; + } + } else { // Already connected, just mark as valid. + RCE->rce_flags |= RCE_CONNECTED; +#ifdef _PNP_POWER + if (!(RCE->rce_flags & RCE_REFERENCED)) { + RCE->rce_flags |= RCE_REFERENCED; + RTE->rte_if->if_refcount++; + } +#endif + } + } + } + } + + // Free the locks and we're done. + CTEFreeLock(&RCE->rce_lock, RCEHandle); + CTEFreeLock(&RouteTableLock, TableHandle); + return Status; + } else { + // No route! Fail the call. + CTEFreeLock(&RouteTableLock, TableHandle); + return FALSE; + } + +} +//** IPGetPInfo - Get information.. +// +// Called by an upper layer to get information about a path. We return the +// MTU of the path and the maximum link speed to be expected on the path. +// +// Input: Dest - Destination address. +// Src - Src address. +// NewMTU - Where to store path MTU (may be NULL). +// MaxPathSpeed - Where to store maximum path speed (may be NULL). +// +// Returns: Status of attempt to get new MTU. +// +IP_STATUS +IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU, uint *MaxPathSpeed) +{ + CTELockHandle Handle; + RouteTableEntry *RTE; + IP_STATUS Status; + + CTEGetLock(&RouteTableLock, &Handle); + RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI); + if (RTE != NULL) { + if (NewMTU != NULL) + *NewMTU = RTE->rte_mtu; + if (MaxPathSpeed != NULL) + *MaxPathSpeed = RTE->rte_if->if_speed; + Status = IP_SUCCESS; + } else + Status = IP_DEST_HOST_UNREACHABLE; + + CTEFreeLock(&RouteTableLock, Handle); + return Status; + +} + +//** IPCheckRoute - Check that a route is valid. +// +// Called by an upper layer when it believes a route might be invalid. +// We'll check if we can. If the upper layer is getting there through a +// route derived via ICMP (presumably a redirect) we'll check to see +// if it's been learned within the last minute. If it has, it's assumed +// to still be valid. Otherwise, we'll mark it as down and try to find +// another route there. If we can, we'll delete the old route. Otherwise +// we'll leave it. If the route is through a default gateway we'll switch +// to another one if we can. Otherwise, we'll just leave - we don't mess +// with manually configured routes. +// +// Input: Dest - Destination to be reached. +// Src - Src we're sending from. +// +// Returns: Nothing. +// +void +IPCheckRoute(IPAddr Dest, IPAddr Src) +{ + RouteTableEntry *RTE; + RouteTableEntry *NewRTE; + RouteTableEntry *TempRTE; + CTELockHandle Handle; + uint Now = CTESystemUpTime() / 1000L; + + if (DeadGWDetect) { + // We are doing dead G/W detection. Get the lock, and try and + // find the route. + CTEGetLock(&RouteTableLock, &Handle); + RTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI); + if (RTE != NULL && ((Now - RTE->rte_valid) > MIN_RT_VALID)) { + + // Found a route, and it's older than the minimum valid time. If it + // goes through a G/W, and is a route we learned via ICMP or is a + // default route, do something with it. + if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) { + // It's not through a G/W. + + if (RTE->rte_proto == IRE_PROTO_ICMP) { + + // Came from ICMP. Mark as invalid, and then make sure + // we have another route there. + RTE->rte_flags &= ~RTE_VALID; + NewRTE = LookupRTE(Dest, Src, HOST_ROUTE_PRI); + + if (NewRTE == NULL) { + // Can't get there any other way so leave this + // one alone. + RTE->rte_flags |= RTE_VALID; + } else { + // There is another route, so destroy this one. Use + // FindSpecificRTE to find the previous RTE. + TempRTE = FindSpecificRTE(RTE->rte_dest, RTE->rte_mask, + RTE->rte_addr, RTE->rte_if, &NewRTE); + CTEAssert(TempRTE == RTE); + DeleteRTE(NewRTE, TempRTE); + } + } else { + if (RTE->rte_mask == DEFAULT_MASK) { + + // This is a default gateway. If we have more than one + // configured move to the next one. + + if (DefGWConfigured > 1) { + // Have more than one. Try the next one. First + // invalidate any RCEs on this G/W. + + InvalidateRCEChain(RTE); + if (DefGWActive == 1) { + // No more active. Revalidate all of them, + // and try again. + ValidateDefaultGWs(NULL_IP_ADDR); + CTEAssert(DefGWActive == DefGWConfigured); + } else { + // More than one active, so invalidate this + // one, and move to the next one. Stamp the + // next one with a valid time of Now, so we + // don't move from him too easily. + --DefGWActive; + RTE->rte_flags &= ~RTE_VALID; + RTE = FindRTE(Dest, Src, IPHash(0), + DEFAULT_ROUTE_PRI, DEFAULT_ROUTE_PRI); + if (RTE == NULL) { + // No more default gateways! This is bad. + CTEAssert(FALSE); + ValidateDefaultGWs(NULL_IP_ADDR); + CTEAssert(DefGWActive == DefGWConfigured); + } else { + CTEAssert(RTE->rte_mask == DEFAULT_MASK); + RTE->rte_valid = Now; + } + } + } + } + } + } + } + CTEFreeLock(&RouteTableLock, Handle); + } +} + + +//** FindRCE - Find an RCE on an RTE. +// +// A routine to find an RCE that's chained on an RTE. We assume the lock +// is held on the RTE. +// +// Entry: RTE - RTE to search. +// Dest - Destination address of RTE to find. +// Src - Source address of RTE to find. +// +// Returns: Pointer to RTE found, or NULL. +// +RouteCacheEntry * +FindRCE(RouteTableEntry *RTE, IPAddr Dest, IPAddr Src) +{ + RouteCacheEntry *CurrentRCE; + + CTEAssert(!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)); + for (CurrentRCE = RTE->rte_rcelist; CurrentRCE != NULL; + CurrentRCE = CurrentRCE->rce_next) { + if ( IP_ADDR_EQUAL(CurrentRCE->rce_dest, Dest) && + IP_ADDR_EQUAL(CurrentRCE->rce_src, Src)) { + break; + } + } + return CurrentRCE; + +} + +//** OpenRCE - Open an RCE for a specific route. +// +// Called by the upper layer to open an RCE. We look up the type of the address +// - if it's invalid, we return 'Destination invalid'. If not, we look up the +// route, fill in the RCE, and link it on the correct RTE. +// +// As an added bonus, this routine will return the local address to use +// to reach the destination. +// +// Entry: Address - Address for which we are to open an RCE. +// Src - Source address we'll be using. +// RCE - Pointer to where to return pointer to RCE. +// Type - Pointer to where to return destination type. +// MSS - Pointer to where to return MSS for route. +// OptInfo - Pointer to option information, such as TOS and +// any source routing info. +// +// Returns: Source IP address to use. This will be NULL_IP_ADDR if the +// specified destination is unreachable for any reason. +// +IPAddr +OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE, uchar *Type, + ushort *MSS, IPOptInfo *OptInfo) +{ + RouteTableEntry *RTE; // Pointer to RTE to put RCE on. + CTELockHandle TableLock; + uchar LocalType; + + + if (!IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) + Address = OptInfo->ioi_addr; + + CTEGetLock(&RouteTableLock, &TableLock); + + // Make sure we're not in DHCP update. + if (DHCPActivityCount != 0) { + // We are updating DHCP. Just fail this now, since we're in an + // indeterminate state. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + + LocalType = GetAddrType(Address); + + *Type = LocalType; + + // If the specified address isn't invalid, continue. + if (LocalType != DEST_INVALID) { + RouteCacheEntry *NewRCE; + + // If he's specified a source address, loop through the NTE table + // now and make sure it's valid. + if (!IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { + NetTableEntry *NTE; + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) + if ((NTE->nte_flags & NTE_VALID) && + IP_ADDR_EQUAL(Src, NTE->nte_addr)) + break; + + if (NTE == NULL) { + // Didn't find a match. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + } + + // Find the route for this guy. If we can't find one, return NULL. + RTE = LookupRTE(Address, Src, HOST_ROUTE_PRI); + + if (RTE != (RouteTableEntry *)NULL) { + CTELockHandle RCEHandle; + RouteCacheEntry *OldRCE; + + // We found one. + *MSS = (ushort)RTE->rte_mtu; // Return the route MTU. + + if (IP_LOOPBACK_ADDR(Src) && (RTE->rte_if != &LoopInterface)) { + // The upper layer is sending from a loopback address, but the + // destination isn't reachable through the loopback interface. + // Fail the request. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + + // We have the RTE. Fill in the RCE, and link it on the RTE. + if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL)) + *Type |= DEST_OFFNET_BIT; // Tell upper layer it's off + // net. + + // + // If no source address was specified, then use the best address + // for the interface. This will generally prevent dynamic NTE's from + // being chosen as the source for wildcard binds. + // + if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) { + + if (LocalType == DEST_LOCAL) + Src = Address; + else { + NetTableEntry *SrcNTE; + + SrcNTE = BestNTEForIF( + ADDR_FROM_RTE(RTE, Address), + RTE->rte_if + ); + + if (SrcNTE == NULL) { + // Can't find an address! Fail the request. + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + + Src = SrcNTE->nte_addr; + } + } + + // Now, see if an RCE already exists for this. + if ((OldRCE = FindRCE(RTE, Address, Src)) == NULL) { + + // Don't have an existing RCE. See if we can get a new one, + // and fill it in. + + NewRCE = CTEAllocMem(sizeof(RouteCacheEntry)); + *RCE = NewRCE; + + if (NewRCE != NULL) { + CTEMemSet(NewRCE, 0, sizeof(RouteCacheEntry)); + + NewRCE->rce_src = Src; + NewRCE->rce_dtype = LocalType; + NewRCE->rce_cnt = 1; + CTEInitLock(&NewRCE->rce_lock); + NewRCE->rce_dest = Address; + NewRCE->rce_rte = RTE; + NewRCE->rce_flags = RCE_VALID; + if (RTE->rte_flags & RTE_IF_VALID) { + NewRCE->rce_flags |= RCE_CONNECTED; +#ifdef _PNP_POWER + //* Update the ref. count for this interface. + NewRCE->rce_flags |= RCE_REFERENCED; + RTE->rte_if->if_refcount++; +#endif + } + NewRCE->rce_next = RTE->rte_rcelist; + RTE->rte_rcelist = NewRCE; + } + + CTEFreeLock(&RouteTableLock, TableLock); + return Src; + } else { + // We have an existing RCE. We'll return his source as the + // valid source, bump the reference count, free the locks + // and return. + CTEGetLock(&OldRCE->rce_lock, &RCEHandle); + OldRCE->rce_cnt++; + *RCE = OldRCE; + CTEFreeLock(&OldRCE->rce_lock, RCEHandle); + CTEFreeLock(&RouteTableLock, TableLock); + return Src; + } + } else { + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; + } + } + + CTEFreeLock(&RouteTableLock, TableLock); + return NULL_IP_ADDR; +} + +//* CloseRCE - Close an RCE. +// +// Called by the upper layer when it wants to close the RCE. We unlink it from +// the RTE. +// +// Entry: RCE - Pointer to the RCE to be closed. +// +// Exit: Nothing. +// +void +CloseRCE(RouteCacheEntry *RCE) +{ + RouteTableEntry *RTE; // Route on which RCE is linked. + RouteCacheEntry *PrevRCE; + CTELockHandle TableLock; // Lock handles used. + CTELockHandle RCEHandle; + Interface *IF; + + if (RCE != NULL) { + CTEGetLock(&RouteTableLock, &TableLock); + CTEGetLock(&RCE->rce_lock, &RCEHandle); + + if (--RCE->rce_cnt == 0) { + CTEAssert(RCE->rce_usecnt == 0); + if (RCE->rce_flags & RCE_VALID) { + // The RCE is valid, so we have a valid RTE in the pointer + // field. Walk down the RTE rcelist, looking for this guy. + + RTE = RCE->rce_rte; + IF = RTE->rte_if; + + PrevRCE = STRUCT_OF(RouteCacheEntry, &RTE->rte_rcelist, + rce_next); + + // Walk down the list until we find him. + while (PrevRCE != NULL) { + if (PrevRCE->rce_next == RCE) + break; + PrevRCE = PrevRCE->rce_next; + } + + CTEAssert(PrevRCE != NULL); + PrevRCE->rce_next = RCE->rce_next; + } else + IF = (Interface *)RCE->rce_rte; + + if (RCE->rce_flags & RCE_CONNECTED) { + (*(IF->if_invalidate))(IF->if_lcontext, RCE); + } + +#ifdef _PNP_POWER + if (RCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(IF); + } +#endif + CTEFreeLock(&RCE->rce_lock, RCEHandle); + CTEFreeMem(RCE); + } + else { + CTEFreeLock(&RCE->rce_lock, RCEHandle); + } + + CTEFreeLock(&RouteTableLock, TableLock); + + } + +} + + +//* LockedAddRoute - Add a route to the routing table. +// +// Called by AddRoute to add a route to the routing table. We assume the +// route table lock is already held. If the route to be added already exists +// we update it. Routes are identified by a (Destination, Mask, FirstHop, +// Interface) tuple. If an exact match exists we'll update the metric, which +// may cause us to promote RCEs from other RTEs, or we may be demoted in which +// case we'll invalidate our RCEs and let them be reassigned at transmission +// time. +// +// If we have to create a new RTE we'll do so, and find the best previous +// RTE, and promote RCEs from that one to the new one. +// +// The route table is an open hash structure. Within each hash chain the +// RTEs with the longest masks (the 'priority') come first, and within +// each priority the RTEs with the smallest metric come first. +// +// +// Entry: Destination - Destination address for which route is being +// added. +// Mask - Mask for destination. +// FirstHop - First hop for address. Could be IPADDR_LOCAL. +// OutIF - Pointer to outgoing I/F. +// MTU - Maximum MTU for this route. +// Metric - Metric for this route. +// Proto - Protocol type to store in route. +// AType - Administrative type of route. +// +// Returns: Status of attempt to add route. +// +IP_STATUS +LockedAddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, + Interface *OutIF, uint MTU, uint Metric, uint Proto, uint AType, + ROUTE_CONTEXT Context) +{ + uint RouteType; // SNMP route type. + RouteTableEntry *NewRTE, *OldRTE; // Entries for new and previous + // RTEs. + RouteTableEntry *PrevRTE; // Pointer to previous RTE. + CTELockHandle RCEHandle; // Lock handle for RCEs. + uint OldMetric; // Previous metric in use. + uint OldPriority; // Priority of previous route to + // destination. + RouteCacheEntry *CurrentRCE; // Current RCE being examined. + RouteCacheEntry *PrevRCE; // Previous RCE examined. + Interface *IF; // Interface being added on. + uint Priority; // Priority of the route. + uint TempMask; // Temporary copy of the mask. + uint Now = CTESystemUpTime() / 1000L; // System up time, + // in seconds. + uint MoveAny; // TRUE if we'll move any RCE. + ushort OldFlags; + + // First do some consistency checks. Make sure that the Mask and + // Destination agree. + if (!IP_ADDR_EQUAL(Destination & Mask, Destination)) + return IP_BAD_DESTINATION; + + if (AType != ATYPE_PERM && AType != ATYPE_OVERRIDE && AType != ATYPE_TEMP) + return IP_BAD_REQ; + +#ifdef _PNP_POWER + // If the interface is marked as going away, fail this. + if (OutIF->if_flags & IF_FLAGS_DELETING) { + return IP_BAD_REQ; + } +#endif + + RouteType = IP_ADDR_EQUAL(FirstHop, IPADDR_LOCAL) ? IRE_TYPE_DIRECT : + IRE_TYPE_INDIRECT; + + + MTU = MAX(MTU, MIN_VALID_MTU); + + // If the outgoing interface has NTEs attached but none are valid, fail + // this request unless it's a request to add the broadcast route. + if (OutIF != (Interface *)&DummyInterface) { + if (OutIF->if_ntecount == 0 && OutIF->if_nte != NULL && + !IP_ADDR_EQUAL(Destination, OutIF->if_bcast) ) { + // This interface has NTEs attached, but none are valid. Fail the + // request. + return IP_BAD_REQ; + } + } + + + // First look and see if the RTE already exists. + NewRTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE); + + if (NewRTE != NULL) { + + // The RTE already exists, and we have a lock on it. See if we're + // updating the metric. If we're not, just return. Otherwise + // we'll remove the RTE and update the metric. If the metric is + // increasing, walk down the RCE chain on the RTE and invalidate + // the RCE back pointers so that they'll be revalidated upon + // transmits, and then reinsert the RTE. If the metric is + // decreasing we'll just fall through to the case below where we'll + // promote RCEs and insert the RTE. + + + // Update the things that won't cause routing table motion. + NewRTE->rte_mtu = MTU; + NewRTE->rte_proto = Proto; + NewRTE->rte_valid = Now; + NewRTE->rte_mtuchange = Now; + NewRTE->rte_context = Context; + OldFlags = NewRTE->rte_flags; + + // We always turn off the increase flag when a route is updated, so + // that we take the long timeout to try to increase it. We also + // always turn on the valid flag. + NewRTE->rte_flags = (OldFlags & ~RTE_INCREASE) | RTE_VALID; + if (OutIF != (Interface *)&DummyInterface) { + NewRTE->rte_flags |= RTE_IF_VALID; + } else { + + // + // Invalidating a previously valid route + // + + NewRTE->rte_flags &= ~RTE_IF_VALID; + } + + // If the RTE is for a default gateway and the old flags indicate + // he wasn't valid then we're essentially creating a new active + // default gateway. In the case bump the active default gateway count. + if (NewRTE->rte_mask == DEFAULT_MASK && !(OldFlags & RTE_VALID)) + DefGWActive++; + + // Need to update the metric, which will cause this RTE to move + // in the table. + OldMetric = NewRTE->rte_metric; // Save the old one. + RemoveRTE(PrevRTE, NewRTE); // Pull him from the chain. + NewRTE->rte_metric = Metric; + + // Now see if we're increasing or decreasing the metric, or + // re-validating a previously invalid route. By definition, if the + // route wasn't valid before this there can't have been any RCEs + // attached to it, so we'll fall through to the promotion code + // and see if we can move any onto it. Otherwise, if we're + // demoting this route invalidate any RCEs on it. + + if (Metric >= OldMetric && (OldFlags & RTE_VALID)) { + // We're increasing it, or leaving it the same. His valid state + // may have changed, so we'll we'll invalidate any RCEs on him + // now in any case. + InsertRTE(NewRTE); + + InvalidateRCEChain(NewRTE); + + // + // Whether the IF has changed or not, we can always overwrite it + // We wait till here to overwrite because if old rte_if was + // not dummy if, then we will want to invalidate RCEChain + // Since the invalidate function is stored in the interface + // structure we want to keep it around + // + + NewRTE->rte_if = OutIF; + + return IP_SUCCESS; + } + + + NewRTE->rte_if = OutIF; + + // The metric is less than the old metric, so we're promoting this + // RTE. Fall through to the code below that deals with this, after + // saving the priority of the RTE. + Priority = NewRTE->rte_priority; + + } else { + + // Didn't find a matching RTE. Allocate a new one, and fill it in. + NewRTE = CTEAllocMem(sizeof(RouteTableEntry)); + + if (NewRTE == NULL) { + // Couldn't get the memory. + return IP_NO_RESOURCES; + } + + IPSInfo.ipsi_numroutes++; + + // Fill him in, and take the lock on him. To do this we'll need to + // calculate the priority. + Priority = 0; + TempMask = Mask; + + while (TempMask) { + Priority += TempMask & 1; + TempMask >>= 1; + } + + // Now initialize all of his fields. + NewRTE->rte_priority = Priority; + + NewRTE->rte_dest = Destination; + NewRTE->rte_mask = Mask; + if (Mask == DEFAULT_MASK) { + // We're adding a default route. + DefGWConfigured++; + DefGWActive++; + } + NewRTE->rte_addr = FirstHop; + NewRTE->rte_metric = Metric; + NewRTE->rte_mtu = MTU; + NewRTE->rte_if = OutIF; + NewRTE->rte_rcelist = NULL; + NewRTE->rte_type = (ushort)RouteType; + NewRTE->rte_flags = RTE_VALID; + if (OutIF != (Interface *)&DummyInterface) { + NewRTE->rte_flags |= RTE_IF_VALID; + } + NewRTE->rte_proto = Proto; + NewRTE->rte_valid = Now; + NewRTE->rte_mtuchange = Now; + NewRTE->rte_admintype = AType; + NewRTE->rte_context = Context; + } + + // At this point, NewRTE points to an initialized RTE that is not in the + // table. We hold the lock on NewRTE and on the RouteTable. First we'll + // find where we'll eventually insert the new RTE (we can't insert it + // yet, because we'll still need to search the table again and we can't + // do that while we hold a lock on an element in the table). Then we'll + // search the table for the next best route (i.e. a route with a priority + // less than or equal to ours), and if we find one we'll promote RCEs from + // that route to us. We'll actually have to search a chain of RTEs. + + PrevRTE = FindInsertPoint(NewRTE); + OldRTE = LookupRTE(Destination, NULL_IP_ADDR, Priority); + + // If we found one, try to promote from him. + if (OldRTE != NULL) { + + // Found another RTE, and we have the lock on it. Starting with him, + // walk down the chain. At the start of this loop we know that the + // route described by OldRTE can reach our destination. If his metric + // is better than ours, we're done and we'll just insert our route. + // If his priority and metric are equal to ours we'll promote only + // those RCEs that exactly match our source address. Otherwise either + // his priority or metric is worse than ours, and we'll promote all + // appropriate RCEs. + // + // Since we specified the maximum priority in the call to LookupRTE + // as our priority, we know the priority of the route we found + // can't be greater than our priority. + + OldMetric = OldRTE->rte_metric; + OldPriority = OldRTE->rte_priority; + + // We'll do the search if his priority is less than ours, or his + // metric is > (i.e. worse than) our metric, or his metric equals + // our metric and the old route is on a different interface. We + // know OldPriority is <= our Priority, since we specified our + // priority in the call to LookupRTE above. + + CTEAssert(OldPriority <= Priority); + + if (OldPriority < Priority || OldMetric > Metric || + (OldMetric == Metric && OldRTE->rte_if != OutIF)) { + + // We're going to search. Figure out the mask to use in comparing + // source addresses. + if (OldPriority == Priority && OldMetric == Metric) + MoveAny = FALSE; // Only promote an exact match. + else + MoveAny = TRUE; // Promote any match. + + for (;;) { + IF = OldRTE->rte_if; + + PrevRCE = STRUCT_OF(RouteCacheEntry, &OldRTE->rte_rcelist, + rce_next); + CurrentRCE = PrevRCE->rce_next; + // Walk the list, promoting any that match. + while (CurrentRCE != NULL) { + CTEGetLock(&CurrentRCE->rce_lock, &RCEHandle); + + // If the masked source address matches our masked address, + // and the destinations match, promote him. + if ((MoveAny || AddrOnIF(OutIF, CurrentRCE->rce_src)) && + IP_ADDR_EQUAL(CurrentRCE->rce_dest & Mask, Destination)) { + // He matches. Pull him from the list and mark him as invalid. + // This will force a new lookup of the route next time he's + // used, which as a side effect will cause the route to be + // connected in the dial-on-demand case. + CTEAssert(CurrentRCE->rce_flags & RCE_VALID); + PrevRCE->rce_next = CurrentRCE->rce_next; + CurrentRCE->rce_flags &= ~RCE_VALID; + CurrentRCE->rce_rte = (RouteTableEntry *)IF; + if (CurrentRCE->rce_usecnt == 0) { + // No one's currently using him, so invalidate him. + if (CurrentRCE->rce_flags & RCE_CONNECTED) { + (*(IF->if_invalidate))(IF->if_lcontext, CurrentRCE); +#ifdef _PNP_POWER + if (CurrentRCE->rce_flags & RCE_REFERENCED) { + LockedDerefIF(IF); + CurrentRCE->rce_flags &= ~RCE_REFERENCED; + } +#endif + } else { +#ifdef _PNP_POWER + CTEAssert(!(CurrentRCE->rce_flags & RCE_REFERENCED)); +#endif + } + } + + } else { + // Doesn't match. Try the next one. + PrevRCE = CurrentRCE; + } + CTEFreeLock(&CurrentRCE->rce_lock, RCEHandle); + CurrentRCE = PrevRCE->rce_next; + } + + // We've walked the RCE list on that old RTE. Look at the + // next one. If it has the same priority and metric as the + // old one, and also matches our destination, check it also. We + // don't need to RTEs that don't match this criteria, since we know + // RCEs are always kept on the 'best' RTE. + OldRTE = OldRTE->rte_next; + if (OldRTE != NULL) { + // We have another one. Check it out. + if (OldRTE->rte_priority == Priority && + OldRTE->rte_metric == OldMetric && + IP_ADDR_EQUAL(Destination & OldRTE->rte_mask, + OldRTE->rte_dest)) + continue; // It matches, so try to promote + // RCEs. + } + break; // Exit out of the for (;;) loop. + } + } else { + // OldRTE is a better route than the one we're inserting, so don't + // do anything. + } + } + + // At this point we're promoted any routes we need to, we hold the lock + // on NewRTE which still isn't inserted, and PrevRTE describes where to + // insert NewRTE. Insert it, free the lock, and return success. + InsertAfterRTE(PrevRTE, NewRTE); + return IP_SUCCESS; + +} + +//* AddRoute - Add a route to the routing table. +// +// This is just a shell for the real add route routine. All we do is take +// the route table lock, and call the LockedAddRoute routine to deal with +// the request. This is done this way because there are certain routines that +// need to be able to atomically examine and add routes. +// +// Entry: Destination - Destination address for which route is being +// added. +// Mask - Mask for destination. +// FirstHop - First hop for address. Could be IPADDR_LOCAL. +// OutIF - Pointer to outgoing I/F. +// MTU - Maximum MTU for this route. +// Metric - Metric for this route. +// Proto - Protocol type to store in route. +// AType - Administrative type of route. +// Context - Context for this route. +// +// Returns: Status of attempt to add route. +// +IP_STATUS +AddRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, + Interface *OutIF, uint MTU, uint Metric, uint Proto, uint AType, + ROUTE_CONTEXT Context) +{ + CTELockHandle TableHandle; + IP_STATUS Status; + + CTEGetLock(&RouteTableLock, &TableHandle); + Status = LockedAddRoute(Destination, Mask, FirstHop, OutIF, MTU, Metric, + Proto, AType, Context); + + CTEFreeLock(&RouteTableLock, TableHandle); + return Status; +} + +//* DeleteRoute - Delete a route from the routing table. +// +// Called by upper layer or management code to delete a route from the routing +// table. If we can't find the route we return an error. If we do find it, we +// remove it, and invalidate any RCEs associated with it. These RCEs will be +// reassigned the next time they're used. A route is uniquely identified by +// a (Destination, Mask, FirstHop, Interface) tuple. +// +// Entry: Destination - Destination address for which route is being +// deleted. +// Mask - Mask for destination. +// FirstHop - First hop on way to Destination. -1 means route is +// local. +// OutIF - Outgoing interface for route. +// +// Returns: Status of attempt to delete route. +// +IP_STATUS +DeleteRoute(IPAddr Destination, IPMask Mask, IPAddr FirstHop, Interface *OutIF) +{ + RouteTableEntry *RTE; // RTE being deleted. + RouteTableEntry *PrevRTE; // Pointer to RTE in front of one + // being deleted. + CTELockHandle TableLock; // Lock handle for table. + + + // Look up the route by calling FindSpecificRTE. If we can't find it, + // fail the call. + CTEGetLock(&RouteTableLock, &TableLock); + RTE = FindSpecificRTE(Destination, Mask, FirstHop, OutIF, &PrevRTE); + + if (RTE == NULL) { + // Didn't find the route, so fail the call. + CTEFreeLock(&RouteTableLock, TableLock); + return IP_BAD_ROUTE; + } + +#ifndef NT +// +// Disable admin check for NT, because the RAS server needs to be able to +// delete a subnet route. This ability is restricted to Administrators only +// by NT security checks. +// +// + if (RTE->rte_admintype == ATYPE_PERM) { + // Can't delete a permanent route. + CTEFreeLock(&RouteTableLock, TableLock); + return IP_BAD_REQ; + } +#endif + + // When we reach here we hold the lock on the RTE to be deleted, and + // PrevRTE points to the RTE immediately ahead of ours in the table. + // Call DeleteRTE to delete him. + DeleteRTE(PrevRTE, RTE); + + CTEFreeLock(&RouteTableLock, TableLock); + return IP_SUCCESS; + + +} + +//** Redirect - Process a redirect request. +// +// This is the redirect handler . We treat all redirects as host redirects as per the +// host requirements RFC. We make a few sanity checks on the new first hop address, and then +// we look up the current route. If it's not through the source of the redirect, just return. +// If the current route to the destination is a host route, update the first hop and return. +// If the route is not a host route, remove any RCE for this route from the RTE, create a +// host route and place the RCE (if any) on the new RTE. +// +// Entry: NTE - Pointer to NetTableEntry for net on which Redirect +// arrived. +// RDSrc - IPAddress of source of redirect. +// Target - IPAddress being redirected. +// Src - Src IP address of DG that triggered RD. +// FirstHop - New first hop for Target. +// +// Returns: Nothing. +// +void +Redirect(NetTableEntry *NTE, IPAddr RDSrc, IPAddr Target, IPAddr Src, + IPAddr FirstHop) +{ + uint MTU; + RouteTableEntry *RTE; + CTELockHandle Handle; + + if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR) || IP_LOOPBACK(FirstHop)) + return; // Can't redirect to loopback + // address. + + CTEAssert(IP_ADDR_EQUAL(NTE->nte_addr, Src)); + + // First make sure that this came from the gateway we're currently using to + // get to Target, and then lookup up the route to the new first hop. The new + // firsthop must be directly reachable, and on the same subnetwork or + // physical interface on which we received the redirect. + + + CTEGetLock(&RouteTableLock, &Handle); + + // Make sure the source of the redirect is the current first hop gateway. + RTE = LookupRTE(Target, Src, HOST_ROUTE_PRI); + if (RTE == NULL || IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) || + !IP_ADDR_EQUAL(RTE->rte_addr, RDSrc)) { + CTEFreeLock(&RouteTableLock, Handle); + return; // A bad redirect. + } + + CTEAssert(RTE->rte_flags & RTE_IF_VALID); + + // If the current first hop gateway is a default gateway, see if we have + // another default gateway at FirstHop that is down. If so, mark him as + // up and invalidate the RCEs on this guy. + if (RTE->rte_mask == DEFAULT_MASK && ValidateDefaultGWs(FirstHop) != 0) { + // Have a default gateway that's been newly activated. Invalidate RCEs + // on the route, and we're done. + InvalidateRCEChain(RTE); + CTEFreeLock(&RouteTableLock, Handle); + return; + } + + // We really need to add a host route through FirstHop. Make sure he's + // a valid first hop. + RTE = LookupRTE(FirstHop, Src, HOST_ROUTE_PRI); + if (RTE == NULL) { + CTEFreeLock(&RouteTableLock, Handle); + return; // Can't get there from here. + } + + CTEAssert(RTE->rte_flags & RTE_IF_VALID); + + + // Check to make sure the new first hop is directly reachable, and is on the + // same subnet or physical interface we received the redirect on. + if (!IP_ADDR_EQUAL(RTE->rte_addr, IPADDR_LOCAL) || // Not directly reachable + // or wrong subnet. + ((NTE->nte_addr & NTE->nte_mask) != (FirstHop & NTE->nte_mask))) { + CTEFreeLock(&RouteTableLock, Handle); + return; + } + + MTU = RTE->rte_mtu; + + // Now add a host route. AddRoute will do the correct things with shifting + // RCEs around. We know that FirstHop is on the same subnet as NTE (from + // the check above), so it's valid to add the route to FirstHop as out + // going through NTE. + LockedAddRoute(Target, HOST_MASK, IP_ADDR_EQUAL(FirstHop, Target) ? IPADDR_LOCAL : + FirstHop, NTE->nte_if, MTU, 1, IRE_PROTO_ICMP, ATYPE_OVERRIDE, + RTE->rte_context); + + CTEFreeLock(&RouteTableLock, Handle); +} + +//* GetRaisedMTU - Get the next largest MTU in table.. +// +// A utility function to search the MTU table for a larger value. +// +// Input: PrevMTU - MTU we're currently using. We want the +// next largest one. +// +// Returns: New MTU size. +// +uint +GetRaisedMTU(uint PrevMTU) +{ + uint i; + + for (i = (sizeof(MTUTable)/sizeof(uint)) - 1; i != 0; i--) { + if (MTUTable[i] > PrevMTU) + break; + } + + return MTUTable[i]; +} + +//* GuessNewMTU - Guess a new MTU, giving a DG size too big. +// +// A utility function to search the MTU table. As input we take in an MTU +// size we believe to be too large, and search the table looking for the +// next smallest one. +// +// Input: TooBig - Size that's too big. +// +// Returns: New MTU size. +// +uint +GuessNewMTU(uint TooBig) +{ + uint i; + + for (i = 0; i < ((sizeof(MTUTable)/sizeof(uint)) - 1); i++) + if (MTUTable[i] < TooBig) + break; + + return MTUTable[i]; +} + +//* RouteFragNeeded - Handle being told we need to fragment. +// +// Called when we receive some external indication that we need to fragment +// along a particular path. If we're doing MTU discovery we'll try to +// update the route, if we can. We'll also notify the upper layers about +// the new MTU. +// +// Input: IPH - Pointer to IP Header of datagram needing +// fragmentation. +// NewMTU - New MTU to be used (may be 0). +// +// Returns: Nothing. +// +void +RouteFragNeeded(IPHeader UNALIGNED *IPH, ushort NewMTU) +{ + uint OldMTU; + CTELockHandle Handle; + RouteTableEntry *RTE; + ushort HeaderLength; + + // If we're not doing PMTU discovery, don't do anything. + if (PMTUDiscovery) { + + // We're doing PMTU discovery. Correct the given new MTU for the IP + // header size, which we don't save as we track MTUs. + if (NewMTU != 0) { + // Make sure the new MTU we got is at least the minimum valid size. + NewMTU = MAX(NewMTU, MIN_VALID_MTU); + NewMTU -= sizeof(IPHeader); + } + + HeaderLength = (IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2; + + // Get the current routing information. + + CTEGetLock(&RouteTableLock, &Handle); + + // Find an RTE for the destination. + RTE = LookupRTE(IPH->iph_dest, IPH->iph_src, HOST_ROUTE_PRI); + + // If we couldn't find one, or the existing MTU is less than the new + // MTU, give up now. + + if (RTE == NULL || (OldMTU = RTE->rte_mtu) < NewMTU) { + // No RTE, or an invalid new MTU. Just bail out now. + CTEFreeLock(&RouteTableLock, Handle); + return; + } + + // If the new MTU is zero, figure out what the new MTU should be. + if (NewMTU == 0) { + ushort DGLength; + + // The new MTU is zero. We'll make a best guess what the new + // MTU should be. We have the RTE for this route already. + + + // Get the length of the datagram that triggered this. Since we'll + // be comparing it against MTU values that we track without the + // IP header size included, subtract off that amount. + DGLength = (ushort)net_short(IPH->iph_length) - sizeof(IPHeader); + + // We may need to correct this as per RFC 1191 for dealing with + // old style routers. + if (DGLength >= OldMTU) { + // The length of the datagram sent is not less than our + // current MTU estimate, so we need to back it down (assuming + // that the sending route has incorrectly added in the header + // length). + DGLength -= HeaderLength; + + } + + // If it's still larger than our current MTU, use the current + // MTU. This could happen if the upper layer sends a burst of + // packets which generate a sequence of ICMP discard messages. The + // first one we receive will cause us to lower our MTU. We then + // want to discard subsequent messages to avoid lowering it + // too much. This could conceivably be a problem if our + // first adjustment still results in an MTU that's too big, + // but we should converge adequately fast anyway, and it's + // better than accidentally underestimating the MTU. + + if (DGLength > OldMTU) + NewMTU = OldMTU; + else + // Move down the table to the next lowest MTU. + NewMTU = GuessNewMTU(DGLength); + } + + // We have the new MTU. Now add it to the table as a host route. + if (NewMTU != OldMTU) + LockedAddRoute(IPH->iph_dest, HOST_MASK, RTE->rte_addr, RTE->rte_if, + NewMTU, RTE->rte_metric, IRE_PROTO_ICMP, ATYPE_OVERRIDE, + RTE->rte_context); + + CTEFreeLock(&RouteTableLock, Handle); + + // We've added the route. Now notify the upper layers of the change. + ULMTUNotify(IPH->iph_dest, IPH->iph_src, IPH->iph_protocol, + (void *)((uchar *)IPH + HeaderLength), NewMTU); + + } +} + +//** IPRouteTimeout - IP routeing timeout handler. +// +// The IP routeing timeout routine, called once a minute. We look at all +// host routes, and if we raise the MTU on them we do so. +// +// Entry: Timer - Timer being fired. +// Context - Pointer to NTE being time out. +// +// Returns: Nothing. +// +void +IPRouteTimeout(CTEEvent *Timer, void *Context) +{ + uint Now = CTESystemUpTime() / 1000L; + CTELockHandle Handle; + uint i; + RouteTableEntry *RTE, *PrevRTE; + uint RaiseMTU, Delta; + Interface *IF; + IPAddr Dest; + uint NewMTU; + NetTableEntry *NTE; + + CTEGetLock(&RouteTableLock, &Handle); + + for (i = 0; i < ROUTE_TABLE_SIZE; i++) { + // Walk down each chain, looking at the host routes. If we're + // doing PMTU discovery, see if we can raise the MTU. + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], rte_next); + RTE = RouteTable[i]; + while (RTE != NULL && RTE->rte_mask == HOST_MASK) { + // Make sure he's valid. + if (RTE->rte_flags & RTE_VALID) { + if (PMTUDiscovery) { + // Check to see if we can raise the MTU on this guy. + Delta = Now - RTE->rte_mtuchange; + + if (RTE->rte_flags & RTE_INCREASE) + RaiseMTU = (Delta >= MTU_INCREASE_TIME ? 1 : 0); + else + RaiseMTU = (Delta >= MTU_DECREASE_TIME ? 1 : 0); + + if (RaiseMTU) { + // We need to raise this MTU. Set his change time to + // Now, so we don't do this again, and figure out + // what the new MTU should be. + RTE->rte_mtuchange = Now; + IF = RTE->rte_if; + if (RTE->rte_mtu < IF->if_mtu) { + + RTE->rte_flags |= RTE_INCREASE; + // This is a candidate for change. Figure out + // what it should be. + NewMTU = MIN(GetRaisedMTU(RTE->rte_mtu), + IF->if_mtu); + RTE->rte_mtu = NewMTU; + Dest = RTE->rte_dest; + + // We have the new MTU. Free the lock, and walk + // down the NTEs on the I/F. For each NTE, + // call up to the upper layer and tell him what + // his new MTU is. + CTEFreeLock(&RouteTableLock, Handle); + NTE = IF->if_nte; + while (NTE != NULL) { + if (NTE->nte_flags & NTE_VALID) { + ULMTUNotify(Dest, NTE->nte_addr, 0, NULL, + MIN(NewMTU, NTE->nte_mss)); + } + NTE = NTE->nte_ifnext; + } + + // We've notified everyone. Get the lock again, + // and start from the first element of this + // chain in case something's changed after we + // free the lock. We've updated the mtuchange + // time of this RTE, so we won't hit him again. + CTEGetLock(&RouteTableLock, &Handle); + PrevRTE = STRUCT_OF(RouteTableEntry, &RouteTable[i], + rte_next); + RTE = RouteTable[i]; + continue; + } else + RTE->rte_flags &= ~RTE_INCREASE; + } + } + // If this route came in via ICMP, and we have no RCEs on it, + // and it's at least 10 minutes old, delete it. + if (RTE->rte_proto == IRE_PROTO_ICMP && + RTE->rte_rcelist == NULL && + (Now - RTE->rte_valid) > MAX_ICMP_ROUTE_VALID) { + // He needs to be deleted. Call DeleteRTE to do this. + DeleteRTE(PrevRTE, RTE); + RTE = PrevRTE->rte_next; + continue; + } + } + PrevRTE = RTE; + RTE = RTE->rte_next; + } + } + + CTEFreeLock(&RouteTableLock, Handle); + CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL); + +} + +//* FreeFWPacket - Free a forwarding packet when we're done with it. +// +// +// Input: Packet - Packet to be freed. +// +// Returns: Nothing. +// +void +FreeFWPacket(PNDIS_PACKET Packet) +{ + CTELockHandle Handle; + FWContext *FWC; + +// BUGBUG - Portability issue + +#ifdef VXD + Packet->Private.Head = (PNDIS_BUFFER)NULL; + Packet->Private.Count = 0; + Packet->Private.PhysicalCount = 0; + Packet->Private.TotalLength = 0; +#else // VXD +#ifdef NT + // + // BUGBUG: This is inefficient. Need something better. + // + NdisReinitializePacket(Packet); +#else // NT +#error Need portable way to do this. +#endif // NT +#endif // VXD + + FWC = (FWContext *)Packet->ProtocolReserved; + if (FWC->fc_options) { + CTEFreeMem(FWC->fc_options); + FWC->fc_options = (uchar *)NULL; + } + + if (FWC->fc_buffhead) { + CTEGetLock(&FWBufFreeLock, &Handle); + FWC->fc_bufftail->Next = FWBufFree; // BUGBUG more portable. + FWBufFree = FWC->fc_buffhead; + CTEFreeLock(&FWBufFreeLock, Handle); + FWC->fc_buffhead = (PNDIS_BUFFER)NULL; + } +#ifdef _PNP_POWER + // If there's an interface pointer here, dereference in now. + if (FWC->fc_if != NULL) { + DerefIF(FWC->fc_if); + FWC->fc_if = NULL; + } +#endif + + CTEGetLock(&FWPacketFreeLock, &Handle); + FWC->fc_pc.pc_common.pc_link = FWPacketFree; + FWPacketFree = Packet; + CTEFreeLock(&FWPacketFreeLock, Handle); + +} + +//* GrowFWPackets - Grow the FW packet list, if we can. +// +// Called when we need to allocate a FW packet, but don't have one. We'll try to grow the +// FWPacket list now. +// +// Input: Nothing. +// +// Returns: TRUE if we succeeded in growing the list, FALSE otherwise. +// +uint +GrowFWPackets(void) +{ + CTELockHandle Handle; + IPHeader *HeaderPtr; + NDIS_HANDLE BufferPool; + NDIS_HANDLE PacketPool; + PNDIS_BUFFER Buffer; + PNDIS_PACKET Packet; + NDIS_STATUS Status; + uint i; + uint AmountToGrow; + + CTEGetLock(&FWPacketFreeLock, &Handle); + + AmountToGrow = MIN(MaxFWPackets - CurrentFWPackets, FWPACKET_GROW_AMOUNT); + HeaderPtr = NULL; + + if (AmountToGrow != 0) { + + // We have room to grow yet, so try to. First get the memory for our header buffers. + HeaderPtr = CTEAllocMem(AmountToGrow * sizeof(IPHeader)); + if (HeaderPtr == (IPHeader *)NULL) + goto failure; // Couldn't get it. + + // Now try to get NDIS buffers for the headers. + NdisAllocateBufferPool(&Status, &BufferPool, AmountToGrow); + if (Status != NDIS_STATUS_SUCCESS) { + goto failure; + } + + // Now try to get the packets themselves. + NdisAllocatePacketPool(&Status, &PacketPool, AmountToGrow, sizeof(FWContext)); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(BufferPool); + goto failure; + } + + // Since we have everything we need, update the current count. + CurrentFWPackets += AmountToGrow; + + CTEFreeLock(&FWPacketFreeLock, Handle); + + // We got the resources we need. Loop through and put them on. + for (i = 0; i < AmountToGrow; i++) { + FWContext *FWC; + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr, + sizeof(IPHeader)); + if (Status != NDIS_STATUS_SUCCESS) + CTEAssert(FALSE); + NdisAllocatePacket(&Status, &Packet, PacketPool); + if (Status != NDIS_STATUS_SUCCESS) + CTEAssert(FALSE); + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext)); + FWC = (FWContext *)Packet->ProtocolReserved; + FWC->fc_hndisbuff = Buffer; + FWC->fc_hbuff = HeaderPtr; + FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW; + FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP; + FWC->fc_pc.pc_pi = RtPI; + FWC->fc_pc.pc_context = Packet; + + FreeFWPacket(Packet); + HeaderPtr++; + } + return TRUE; + } + +failure: + CTEFreeLock(&FWPacketFreeLock, Handle); + if (HeaderPtr != NULL) { + CTEFreeMem(HeaderPtr); + } + return FALSE; +} + +//* GrowFWBuffer - Grow the FW buffer pool, if we can. +// +// Called when we need to grow the FW buffer pool. We'll grow it up to the maximum size +// specified by the user. +// +// Input: Nothing. +// +// Returns: TRUE if we succeeded in growing the pool, FALSE otherwise. +// +uint +GrowFWBuffer(void) +{ + CTELockHandle Handle; + uint AvailableBufferSpace; + uint NewBufferCount; + uint i; + uchar *BufferPtr = NULL; + NDIS_STATUS Status; + PNDIS_BUFFER Buffer; + NDIS_HANDLE BufferPool; + + CTEGetLock(&FWPacketFreeLock, &Handle); + AvailableBufferSpace = MIN(MaxFWBufferSize - CurrentFWBufferSize, FW_BUF_GROW_AMOUNT); + + // If we have room to grow, do so. + if (AvailableBufferSpace >= FW_BUF_SIZE) { + // We have room to grow the buffer, so do so. First, round to a multiple of our + // FW buffer size. + NewBufferCount = AvailableBufferSpace / FW_BUF_SIZE; + AvailableBufferSpace = NewBufferCount * FW_BUF_SIZE; + + // Allocate the resources we need. + BufferPtr = CTEAllocMem(AvailableBufferSpace); + if (BufferPtr == NULL) { + goto failure; + } + + NdisAllocateBufferPool(&Status, &BufferPool, NewBufferCount); + if (Status != NDIS_STATUS_SUCCESS) { + goto failure; + } + + // We got what we needed. Now loop through and put them on the list. + for (i = 0; i < NewBufferCount; i++) { + NdisAllocateBuffer(&Status, &Buffer, BufferPool, BufferPtr, FW_BUF_SIZE); + if (Status != NDIS_STATUS_SUCCESS) + CTEAssert(FALSE); + + Buffer->Next = FWBufFree; + FWBufFree = Buffer; + BufferPtr += FW_BUF_SIZE; + } + + CurrentFWBufferSize += AvailableBufferSpace; + CTEFreeLock(&FWPacketFreeLock, Handle); + return TRUE; + + } + +failure: + CTEFreeLock(&FWPacketFreeLock, Handle); + if (BufferPtr != NULL) { + CTEFreeMem(BufferPtr); + } + return FALSE; + +} + +//* FWSendComplete - Complete the transmission of a forwarded packet. +// +// This is called when the send of a forwarded packet is done. We'll free the resources +// and get the next send going, if there is one. If there isn't, we'll decrement the pending +// count. +// +// Input: Packet - Packet being completed. +// Buffer - Pointer to buffer chain being completed. +// +// Returns: Nothing. +// +void +FWSendComplete(void *SendContext, PNDIS_BUFFER Buffer) +{ + PNDIS_PACKET Packet = (PNDIS_PACKET)SendContext; + FWContext *FWC = (FWContext *)Packet->ProtocolReserved; + RouteSendQ *RSQ; + CTELockHandle Handle; + FWQ *NewFWQ; + PNDIS_PACKET NewPacket; + + +#ifdef DEBUG + if (!Buffer) + DEBUGCHK; +#endif + + if (!IS_BCAST_DEST(FWC->fc_dtype)) + RSQ = &((RouteInterface *)FWC->fc_if)->ri_q; + else + RSQ = BCastRSQ; + + FreeFWPacket(Packet); + + CTEGetLock(&RSQ->rsq_lock, &Handle); + CTEAssert(RSQ->rsq_pending <= RSQ->rsq_maxpending); + + RSQ->rsq_pending--; + + CTEAssert(*(int *)&RSQ->rsq_pending >= 0); + + if (RSQ->rsq_qlength != 0) { // Have more to send. + // Make sure we're not already running through this. If we are, quit. + if (!RSQ->rsq_running) { + + // We could schedule this off for an event, but under NT that + // could me a context switch for every completing packet in the + // normal case. For now, just do it in a loop guarded with + // rsq_running. + RSQ->rsq_running = TRUE; + + // Loop while we haven't hit our send limit and we still have + // stuff to send. + while (RSQ->rsq_pending < RSQ->rsq_maxpending && + RSQ->rsq_qlength != 0) { +#ifdef DEBUG + if (RSQ->rsq_qh.fq_next == &RSQ->rsq_qh) + DEBUGCHK; // Empty Q! +#endif + // Pull one off the queue, and update qlength. + NewFWQ = RSQ->rsq_qh.fq_next; + RSQ->rsq_qh.fq_next = NewFWQ->fq_next; + NewFWQ->fq_next->fq_prev = NewFWQ->fq_prev; + RSQ->rsq_qlength--; + + // Update pending before we send. + RSQ->rsq_pending++; + CTEFreeLock(&RSQ->rsq_lock, Handle); + NewPacket = PACKET_FROM_FWQ(NewFWQ); + TransmitFWPacket(NewPacket, + ((FWContext *)NewPacket->ProtocolReserved)->fc_datalength); + CTEGetLock(&RSQ->rsq_lock, &Handle); + } + + RSQ->rsq_running = FALSE; + } + } + + CTEFreeLock(&RSQ->rsq_lock, Handle); + +} + +//* TransmitFWPacket - Transmit a forwarded packet on a link. +// +// Called when we know we can send a packet. We fix up the header, and send it. +// +// Input: Packet - Packet to be sent. +// DataLength - Length of data. +// +// Returns: Nothing. +// +void +TransmitFWPacket(PNDIS_PACKET Packet, uint DataLength) +{ + FWContext *FC = (FWContext *)Packet->ProtocolReserved; + PNDIS_BUFFER HBuffer, Buffer; + IP_STATUS Status; + PVOID VirtualAddress; + UINT BufLen; + + // Fix up the packet. Remove the existing buffer chain, and put our header on + // the front. + + // BUGBUG - Get NDIS fixed to make this portable. +#ifdef VXD + Buffer = Packet->Private.Head; + HBuffer = FC->fc_hndisbuff; + Packet->Private.Head = HBuffer; + Packet->Private.Tail = HBuffer; + HBuffer->Next = (PNDIS_BUFFER)NULL; + Packet->Private.TotalLength = sizeof(IPHeader); + Packet->Private.Count = 1; + + Packet->Private.PhysicalCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(HBuffer->VirtualAddress, + sizeof(IPHeader)); +#else // VXD +#ifdef NT + Buffer = Packet->Private.Head; + HBuffer = FC->fc_hndisbuff; + Packet->Private.Head = HBuffer; + Packet->Private.Tail = HBuffer; + NDIS_BUFFER_LINKAGE(HBuffer) = (PNDIS_BUFFER)NULL; + Packet->Private.TotalLength = sizeof(IPHeader); + Packet->Private.Count = 1; + + NdisQueryBuffer(HBuffer, &VirtualAddress, &BufLen); + + Packet->Private.PhysicalCount = + ADDRESS_AND_SIZE_TO_SPAN_PAGES( + VirtualAddress, + sizeof(IPHeader) + ); +#else // NT +#error HELP! Need to make this code portable. +#endif // NT +#endif // VXD + + // Figure out how to send it. If it's not a broadcast we'll either send it or + // have it fragmented. If it is a broadcast we'll let our send broadcast routine + // handle it. + if (FC->fc_dtype != DEST_BCAST) { + + if ((DataLength + (uint)FC->fc_optlength) <= FC->fc_mtu) + Status = SendIPPacket(FC->fc_if, FC->fc_nexthop, Packet, Buffer, + FC->fc_hbuff, FC->fc_options, (uint)FC->fc_optlength); + else { // Need to fragment this. + BufferReference *BR = CTEAllocMem(sizeof(BufferReference)); + + if (BR == (BufferReference *)NULL) { // Couldn't get a BufferReference + FWSendComplete(Packet, Buffer); + return; + } + BR->br_buffer = Buffer; + BR->br_refcount = 0; + CTEInitLock(&BR->br_lock); + FC->fc_pc.pc_br = BR; + Status = IPFragment(FC->fc_if, FC->fc_mtu, FC->fc_nexthop, Packet, + FC->fc_hbuff, Buffer, DataLength, FC->fc_options, + (uint)FC->fc_optlength, (int *)NULL); + + // + // Fragmentation needed with the DF flag set should have been + // handled in IPForward. We don't have the original header + // any longer, so silently drop the packet. + // + CTEAssert(Status != IP_PACKET_TOO_BIG); + } + } else + Status = SendIPBCast(FC->fc_srcnte, FC->fc_nexthop, Packet, FC->fc_hbuff, + Buffer, DataLength, FC->fc_options, (uint)FC->fc_optlength, + FC->fc_sos, &FC->fc_index); + + if (Status != IP_PENDING) + FWSendComplete(Packet, Buffer); +} + +//* SendFWPacket - Send a packet that needs to be forwarded. +// +// This routine is invoked when we actually get around to sending a packet. +// We look and see if we can give another queued send to the outgoing link, +// and if so we send on that link. Otherwise we put it on the outgoing queue +// and remove it later. +// +// Input: SrcNTE - Source NTE of packet. +// Packet - Packet to be send, containg all needed context info. +// Status - Status of transfer data. +// DataLength - Length in bytes of data to be send. +// +// Returns: Nothing. +// +void +SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status, uint DataLength) +{ + + FWContext *FC = (FWContext *)Packet->ProtocolReserved; + Interface *IF = FC->fc_if; + RouteSendQ *RSQ; + CTELockHandle Handle; + + if (Status == NDIS_STATUS_SUCCESS) { + // Figure out which logical queue it belongs on, and if we don't already + // have too many things going there, send it. If we can't send it now we'll + // queue it for later. + if (IS_BCAST_DEST(FC->fc_dtype)) + RSQ = BCastRSQ; + else + RSQ = &((RouteInterface *)IF)->ri_q; + + CTEGetLock(&RSQ->rsq_lock, &Handle); + + if (RSQ->rsq_pending < RSQ->rsq_maxpending && RSQ->rsq_qlength == 0) { + // We can send on this interface. + RSQ->rsq_pending++; + CTEFreeLock(&RSQ->rsq_lock, Handle); + + TransmitFWPacket(Packet, DataLength); + + } else { // Need to queue this packet for later. + + FC->fc_datalength = DataLength; + FC->fc_q.fq_next = &RSQ->rsq_qh; + FC->fc_q.fq_prev = RSQ->rsq_qh.fq_prev; + RSQ->rsq_qh.fq_prev->fq_next = &FC->fc_q; + RSQ->rsq_qh.fq_prev = &FC->fc_q; + RSQ->rsq_qlength++; + CTEFreeLock(&RSQ->rsq_lock, Handle); + } + } else{ + IPSInfo.ipsi_outdiscards++; + FreeFWPacket(Packet); + } + +} + +//* RemoveRandomFWPacket - Remove a random packet from the FW queue. +// +// Called when we run out of resources. We pick a random packet from the FW queue, +// free it, and return. The caller will hopefully then get it for his own use. +// +// Input: RSQ - Pointer to outgoing route send q.. +// +// Returns: TRUE if we free a packet, false if we didn't. +// +uchar +RemoveRandomFWPacket(RouteSendQ *RSQ) +{ + uint Now = (uint)CTESystemUpTime(); + CTELockHandle Handle; + uint PacketCount; + PNDIS_PACKET FreedPacket; + FWQ *CurrentFWQ; +#ifdef DEBUG + FWQ *FirstFWQ; +#endif + +#ifdef DEBUG + FirstFWQ = &RSQ->rsq_qh; +#endif + + CTEGetLock(&RSQ->rsq_lock, &Handle); + if (RSQ->rsq_qlength) { // We have a least one in the list. + + + PacketCount = Now % (RSQ->rsq_qlength + 1); + if (PacketCount == RSQ->rsq_qlength) { + CTEFreeLock(&RSQ->rsq_lock, Handle); + return FALSE; + } + + CurrentFWQ = RSQ->rsq_qh.fq_next; + while (PacketCount--) { +#ifdef DEBUG + if (CurrentFWQ == FirstFWQ) + DEBUGCHK; +#endif + CurrentFWQ = CurrentFWQ->fq_next; + } + + // We've got the proper packet. Splice him out. + CurrentFWQ->fq_next->fq_prev = CurrentFWQ->fq_prev; + CurrentFWQ->fq_prev->fq_next = CurrentFWQ->fq_next; + RSQ->rsq_qlength--; + CTEFreeLock(&RSQ->rsq_lock, Handle); + FreedPacket = PACKET_FROM_FWQ(CurrentFWQ); + FreeFWPacket(FreedPacket); + IPSInfo.ipsi_outdiscards++; + return TRUE; + } + CTEFreeLock(&RSQ->rsq_lock, Handle); + return FALSE; + + +} + +//* GetFWBuffer - Get a list of buffers for forwarding. +// +// This routine gets a list of buffers for forwarding, and puts the data into it. This +// may involve calling TransferData, or we may be able to copy directly into them +// ourselves. +// +// Input: SrcNTE - Pointer to NTE on which packet was received. +// Packet - Packet being forwarded, used for TD. +// Data - Pointer to data buffer being forwarded. +// DataLength - Length in bytes of Data. +// BufferLength - Length in bytes available in buffer pointer to by Data. +// Offset - Offset into original data from which to transfer. +// LContext1, LContext2 - Context values for the link layer. +// +// Returns: NDIS_STATUS of attempt to get buffer. +// +NDIS_STATUS +GetFWBuffer(NetTableEntry *SrcNTE, PNDIS_PACKET Packet, uchar *Data, + uint DataLength, uint BufferLength, uint Offset, NDIS_HANDLE LContext1, + uint LContext2) +{ + CTELockHandle Handle; + uint BufNeeded, i; + PNDIS_BUFFER FirstBuffer, CurrentBuffer; + void *DestPtr; + Interface *SrcIF; + FWContext *FWC; + uint BufLen; + uint LastBufSize; +#ifdef DEBUG + uint TotalBufferSize; + PNDIS_BUFFER TempBuffer; +#endif + + // Figure out how many buffers we need. + BufNeeded = DataLength / FW_BUF_SIZE; + LastBufSize = DataLength % FW_BUF_SIZE; + if (LastBufSize != 0) + BufNeeded++; + +#ifdef DEBUG + if (!BufNeeded) + DEBUGCHK; +#endif + FWC = (FWContext *)Packet->ProtocolReserved; + + // Now run down the buffer free list, getting the buffers we need. If we + // can't get enough the first time, we'll free a random packet from our + // pending list and try again. + for (;;) { + CTEGetLock(&FWBufFreeLock, &Handle); + FirstBuffer = FWBufFree; + CurrentBuffer = STRUCT_OF(NDIS_BUFFER, &FWBufFree, Next); + i = 0; + do { + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + if (!CurrentBuffer) + break; + + // Zap this buffer length to the full buffer size, since it may + // have been modified from a previous send. + NdisAdjustBufferLength(CurrentBuffer, FW_BUF_SIZE); + i++; + } while (i < BufNeeded); + + if (i != BufNeeded) { // We ran out of buffers. Free a packet and try again. + RouteSendQ *RSQ; + + if ((MaxFWBufferSize - CurrentFWBufferSize) <= FW_BUF_SIZE) { + CTEFreeLock(&FWBufFreeLock, Handle); + if (GrowFWBuffer()) { + continue; + } + } else + CTEFreeLock(&FWBufFreeLock, Handle); + + if (!IS_BCAST_DEST(FWC->fc_dtype)) + RSQ = &((RouteInterface *)FWC->fc_if)->ri_q; + else + RSQ = BCastRSQ; + + if (!RemoveRandomFWPacket(RSQ)) { + if (IS_BCAST_DEST(FWC->fc_dtype)) + return NDIS_STATUS_RESOURCES; + + // Couldn't get one for a non-broadcast packet. If the qlen is + // 0 on the outgoing queue, we'll try other queues, on the + // presumption that traffic through some other interface is + // starving this one. + if (RSQ->rsq_qlength == 0) { + Interface *IF; + for (IF = IFList; IF != NULL; IF = IF->if_next) { + RSQ = &((RouteInterface *)IF)->ri_q; + if (RemoveRandomFWPacket(RSQ)) + break; + } + if (IF == NULL) + return NDIS_STATUS_RESOURCES; + } else + return NDIS_STATUS_RESOURCES; + } + + // Otherwise we'll fall through and try again, now that we have + // hopefully put some more on the free queue. + + } else { + // We have as many as we need. Update the free list. + FWBufFree = NDIS_BUFFER_LINKAGE(CurrentBuffer); + CTEFreeLock(&FWBufFreeLock, Handle); + NDIS_BUFFER_LINKAGE(CurrentBuffer) = (PNDIS_BUFFER)NULL; + + // If we have a non-full last buffer, adjust it's size. + if (LastBufSize != 0) + NdisAdjustBufferLength(CurrentBuffer, LastBufSize); + + FWC->fc_buffhead = FirstBuffer; + FWC->fc_bufftail = CurrentBuffer; + break; + } + } + + NdisChainBufferAtFront(Packet, FirstBuffer); + +#ifdef DEBUG + // Sanity check the buffer chain and packet. + TempBuffer = FirstBuffer; + TotalBufferSize = 0; + while (TempBuffer != NULL) { + TotalBufferSize += NdisBufferLength(TempBuffer); + TempBuffer = NDIS_BUFFER_LINKAGE(TempBuffer); + } + + CTEAssert(TotalBufferSize == DataLength); + NdisQueryPacket(Packet, NULL, NULL, NULL, &TotalBufferSize); + CTEAssert(TotalBufferSize == DataLength); +#endif + + // First buffer points to the list of buffers we have. If we can copy the + // data here, do so, otherwise invoke the link's transfer data routine. + if ((DataLength <= BufferLength) && (SrcNTE->nte_flags & NTE_COPY)) { + while (DataLength) { + uint CopyLength; + +#ifdef VXD + DestPtr = FirstBuffer->VirtualAddress; +#else + // + // BUGBUG: This is inefficient. + // + NdisQueryBuffer(FirstBuffer, &DestPtr, &BufLen); +#endif + CopyLength = MIN(DataLength, FW_BUF_SIZE); + CTEAssert(CopyLength == NdisBufferLength(FirstBuffer)); + CTEMemCopy(DestPtr, Data, CopyLength); + Data += CopyLength; + DataLength -= CopyLength; + FirstBuffer = NDIS_BUFFER_LINKAGE(FirstBuffer); + } + return NDIS_STATUS_SUCCESS; + } + + // We need to call transfer data for this. + + SrcIF = SrcNTE->nte_if; + return (*(SrcIF->if_transfer))(SrcIF->if_lcontext, LContext1, LContext2, + Offset, DataLength, Packet, &DataLength); + + +} + + +//* GetFWPacket - Get a packet for forwarding. +// +// Called when we need to get a packet to forward a datagram. +// +// Entry: Packet - Pointer to where to return a packet. +// IF - Outgoing I/F for packet. +// DestType - Type of outgoing packet. +// +// Returns: Pointer to header buffer. +// +// +IPHeader * +GetFWPacket(PNDIS_PACKET *Packet, Interface *IF, uchar DestType) +{ + CTELockHandle Handle; + PNDIS_PACKET NewPacket; + RouteSendQ *RSQ; + + for (;;) { + CTEGetLock(&FWPacketFreeLock, &Handle); + if ((NewPacket = FWPacketFree) != (PNDIS_PACKET)NULL) { + FWContext *FWC; + + FWC = (FWContext *)NewPacket->ProtocolReserved; + FWPacketFree = FWC->fc_pc.pc_common.pc_link; + FWC->fc_pc.pc_common.pc_flags |= PACKET_FLAG_IPHDR; + FWC->fc_pc.pc_br = NULL; + *Packet = NewPacket; + CTEFreeLock(&FWPacketFreeLock, Handle); + return FWC->fc_hbuff; + } + + // If we couldn't get one, try to grow the list if we can. + if (MaxFWPackets > CurrentFWPackets) { + CTEFreeLock(&FWPacketFreeLock, Handle); + // We're allowed to grow, so try to. + if (GrowFWPackets()) { + continue; // If we grew it, try again. + } + } else + CTEFreeLock(&FWPacketFreeLock, Handle); + + // Either we weren't allowed to grow the list, or we tried to but couldn't. Try yo + // get one that's on the queue already. + if (!IS_BCAST_DEST(DestType)) + RSQ = &((RouteInterface *)IF)->ri_q; + else + RSQ = BCastRSQ; + + if (!RemoveRandomFWPacket(RSQ)) + break; + } + + return (IPHeader *)NULL; +} + + +//** IPForward - Forward a packet. +// +// The routine called when we need to forward a packet. We check if we're supposed +// to act as a gateway, and if we are and the incoming packet is a bcast we check +// and see if we're supposed to forward broadcasts. Assuming we're supposed to +// forward it, we will process any options. If we find some, we do some validation +// to make sure everything is good. After that, we look up the next hop. If we can't +// find one, we'll issue an error. Then we get a packet and buffers, and send it. +// +// Input: SrcNTE - NTE for net on which we received this. +// Header - Pointer to received IPheader. +// HeaderLength - Length of header. +// Data - Pointer to data to be forwarded. +// BufferLength - Length in bytes available in the buffer. +// DestType - Type of destination. +// +// Returns: Nothing. +// +void +IPForward(NetTableEntry *SrcNTE, IPHeader UNALIGNED *Header, uint HeaderLength, + void *Data, uint BufferLength, NDIS_HANDLE LContext1, uint LContext2, + uchar DestType) +{ + uchar *Options; + uchar OptLength; + OptIndex Index; + IPAddr DestAddr; // IP address we're routing towards. + uchar SendOnSource = FALSE; + IPAddr NextHop; // Next hop IP address. + PNDIS_PACKET Packet; + FWContext *FWC; + IPHeader *NewHeader; // New header. + NDIS_STATUS Status; + uint DataLength; + CTELockHandle TableHandle; + uchar ErrIndex; + IPAddr OutAddr; // Address of interface we're send out on. + Interface *IF; // Interface we're sending out on. + uint MTU; + + if (ForwardPackets) { + + DestAddr = Header->iph_dest; + + // If it's a broadcast, see if we can forward it. We won't forward it if broadcast + // forwarding is turned off, or the destination if the local (all one's) broadcast, + // or it's a multicast (Class D address). We'll pass through subnet broadcasts in + // case there's a source route. This would be odd - maybe we should disable this? + if (IS_BCAST_DEST(DestType)) { + if (!ForwardBCast) { + if (DestType > DEST_REMOTE) + IPSInfo.ipsi_inaddrerrors++; + return; + } + if ((DestAddr == IP_LOCAL_BCST) || + (DestAddr == IP_ZERO_BCST) || + (DestType == DEST_SN_BCAST) || + CLASSD_ADDR(DestAddr)) { + return; + } + } else + if (DestType == DEST_REMOTE) { + SrcNTE = BestNTEForIF(Header->iph_src, SrcNTE->nte_if); + if (SrcNTE == NULL) { + // Something bad happened. + return; + } + } + + // If the TTL would expire, send a message. + if (Header->iph_ttl <= 1) { + IPSInfo.ipsi_inhdrerrors++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_TIME_EXCEED, TTL_IN_TRANSIT,0); + return; + } + + DataLength = net_short(Header->iph_length) - HeaderLength; + + Index.oi_srtype = NO_SR; // So we know we don't have a source route. + Index.oi_srindex = MAX_OPT_SIZE; + Index.oi_rrindex = MAX_OPT_SIZE; + Index.oi_tsindex = MAX_OPT_SIZE; + + // Now check for options, and process any we find. + if (HeaderLength != sizeof(IPHeader)) { + IPOptInfo OptInfo; + + OptInfo.ioi_options = (uchar *)(Header + 1); + OptInfo.ioi_optlength = HeaderLength - sizeof(IPHeader); + // Validate options, and set up indices. + if ((ErrIndex = ParseRcvdOptions(&OptInfo, &Index)) < MAX_OPT_SIZE) { + IPSInfo.ipsi_inhdrerrors++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_PARAM_PROBLEM, + PTR_VALID, ((ulong)ErrIndex + sizeof(IPHeader))); + return; + } + + Options = CTEAllocMem(OptInfo.ioi_optlength); + if (!Options) { + IPSInfo.ipsi_outdiscards++; + return; // Couldn't get an + } // option buffer, return; + + // Now copy into our buffer. + CTEMemCopy(Options, OptInfo.ioi_options, OptLength = OptInfo.ioi_optlength); + + // See if we have a source routing option, and if so we may need to process it. If + // we have one, and the destination in the header is us, we need to update the + // route and the header. + if (Index.oi_srindex != MAX_OPT_SIZE) { + if (DestType >= DEST_REMOTE) { // Not for us. + if (Index.oi_srtype == IP_OPT_SSRR) { + // This packet is strict source routed, but we're not the destination! + // We can't continue from here - perhaps we should send an ICMP, but + // I'm not sure which one it would be. + CTEFreeMem(Options); + IPSInfo.ipsi_inaddrerrors++; + return; + } + Index.oi_srindex = MAX_OPT_SIZE; // Don't need to update this. + + } else { // This came here, we need to update the destination address. + uchar *SROpt = Options + Index.oi_srindex; + uchar Pointer; + + Pointer = SROpt[IP_OPT_PTR] - 1; // Index starts from one. + + // Get the next hop address, and see if it's a broadcast. + DestAddr = *(IPAddr UNALIGNED *)&SROpt[Pointer]; + DestType = GetAddrType(DestAddr); // Find address type. + if (DestType == DEST_INVALID) { + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, SR_FAILED, 0); + IPSInfo.ipsi_inhdrerrors++; + CTEFreeMem(Options); + return; + } + + // If we came through here, any sort of broadcast needs to be sent out + // the way it came, so update that flag. + SendOnSource = TRUE; + } + } + } else { // No options. + Options = (uchar *)NULL; + OptLength = 0; + } + + IPSInfo.ipsi_forwdatagrams++; + + // We've processed the options. Now look up the next hop. If we can't + // find one, send back an error. + IF = LookupNextHopWithBuffer(DestAddr, SrcNTE->nte_addr, &NextHop, &MTU, + Header->iph_protocol, (uchar *)Data, BufferLength); + + if (IF == NULL) { + // Couldn't find an outgoing route. + IPSInfo.ipsi_outnoroutes++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, + HOST_UNREACH, 0); + if (Options) + CTEFreeMem(Options); + return; + } + + // + // If the DF flag is set, make sure the packet doesn't need + // fragmentation. If this is the case, send an ICMP error + // now while we still have the original IP header. The ICMP + // message includes the MTU so the source host can perform + // Path MTU discovery. + // + if ( (Header->iph_offset & IP_DF_FLAG) && + ((DataLength + (uint)OptLength) > MTU) + ) + { + CTEAssert((MTU + sizeof(IPHeader)) >= 68); + CTEAssert((MTU + sizeof(IPHeader)) <= 0xFFFF); + + IPSInfo.ipsi_fragfails++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, + FRAG_NEEDED, net_long((ulong)(MTU + sizeof(IPHeader))) + ); + + if (Options) + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + + // See if we need to filter this packet. If we do, call the filter routine + // to see if it's OK to forward it. + if (ForwardFilterPtr != NULL) { + FORWARD_ACTION Action; + + Action = (*ForwardFilterPtr)(Header, Data, BufferLength, + SrcNTE->nte_if->if_filtercontext, IF->if_filtercontext); + + if (Action != FORWARD) { + IPSInfo.ipsi_outdiscards++; + if (Options) + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + } + + // If we have a strict source route and the next hop is not the one + // specified, send back an error. + if (Index.oi_srtype == IP_OPT_SSRR) { + if (DestAddr != NextHop) { + IPSInfo.ipsi_outnoroutes++; + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_DEST_UNREACH, + SR_FAILED, 0); + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + } + + // Update the options, if we can and we need to. + if ((DestType != DEST_BCAST) && Options != NULL) { + NetTableEntry *OutNTE; + + // Need to find a valid source address for the outgoing interface. + CTEGetLock(&RouteTableLock, &TableHandle); + OutNTE = BestNTEForIF(DestAddr, IF); + if (OutNTE == NULL) { + // No NTE for this IF. Something's wrong, just bail out. + CTEFreeLock(&RouteTableLock, TableHandle); + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } else { + OutAddr = OutNTE->nte_addr; + CTEFreeLock(&RouteTableLock, TableHandle); + } + + ErrIndex = UpdateOptions(Options, &Index, + (IP_LOOPBACK(OutAddr) ? DestAddr : OutAddr)); + + if (ErrIndex != MAX_OPT_SIZE) { + IPSInfo.ipsi_inhdrerrors++; + SendICMPErr(OutAddr, Header, ICMP_PARAM_PROBLEM, PTR_VALID, + ((ulong)ErrIndex + sizeof(IPHeader))); + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + return; + } + } + + + // Send a redirect, if we need to. We'll send a redirect if the packet + // is going out on the interface it came in on and the next hop address + // is on the same subnet as the NTE we received it on, and if there + // are no source route options. We also need to make sure that the + // source of the datagram is on the I/F we received it on, so we don't + // send a redirect to another gateway. + // SendICMPErr will check and not send a redirect if this is a broadcast. + if ((SrcNTE->nte_if == IF) && + IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask, + NextHop & SrcNTE->nte_mask) && + IP_ADDR_EQUAL(SrcNTE->nte_addr & SrcNTE->nte_mask, + Header->iph_src & SrcNTE->nte_mask)) + { + if (Index.oi_srindex == MAX_OPT_SIZE) + { + +#ifdef REDIRECT_DEBUG + +#define PR_IP_ADDR(x) \ + ((x)&0x000000ff),(((x)&0x0000ff00)>>8),(((x)&0x00ff0000)>>16),(((x)&0xff000000)>>24) + + + DbgPrint("IP: Sending Redirect. IF = %x SRC_NTE = %x SrcNteIF = %x\n", + IF,SrcNTE,SrcNTE->nte_if); + + DbgPrint("IP: SrcNteAddr = %d.%d.%d.%d Mask = %d.%d.%d.%d\n", + PR_IP_ADDR(SrcNTE->nte_addr), PR_IP_ADDR(SrcNTE->nte_mask)); + + DbgPrint("IP: NextHop = %d.%d.%d.%d Header Src = %d.%d.%d.%d, Dst = %d.%d.%d.%d\n", + PR_IP_ADDR(NextHop), + PR_IP_ADDR(Header->iph_src), + PR_IP_ADDR(Header->iph_dest)); + +#endif + + SendICMPErr(SrcNTE->nte_addr, Header, ICMP_REDIRECT, + REDIRECT_HOST, NextHop); + } + } + + // We have the next hop. Now get a forwarding packet. + if ((NewHeader = GetFWPacket(&Packet, IF, DestType)) != + (IPHeader *)NULL) { + + // Got the header. Fill it in. + + NewHeader->iph_verlen = Header->iph_verlen; + NewHeader->iph_tos = Header->iph_tos; + NewHeader->iph_length = Header->iph_length; + NewHeader->iph_id = Header->iph_id; + NewHeader->iph_offset = Header->iph_offset; + NewHeader->iph_protocol = Header->iph_protocol; + NewHeader->iph_src = Header->iph_src; + + NewHeader->iph_dest = DestAddr; + NewHeader->iph_ttl = Header->iph_ttl - 1; + NewHeader->iph_xsum = 0; + + // Save the packet forwarding context info. + FWC = (FWContext *)Packet->ProtocolReserved; + FWC->fc_options = Options; + FWC->fc_optlength = OptLength; + FWC->fc_if = IF; + FWC->fc_mtu = MTU; + FWC->fc_srcnte = SrcNTE; + FWC->fc_nexthop = NextHop; + FWC->fc_sos = SendOnSource; + FWC->fc_dtype = DestType; + FWC->fc_index = Index; + + // Now that we have a packet, go ahead and transfer data the + // data in if we need to. + Status = GetFWBuffer(SrcNTE, Packet, Data, DataLength, BufferLength, + HeaderLength, LContext1, LContext2); + + // If the status is pending, don't do anything now. Otherwise, + // if the status is success send the packet. + if (Status != NDIS_STATUS_PENDING) + if (Status == NDIS_STATUS_SUCCESS) { + SendFWPacket(Packet, Status, DataLength); + } else { + // Some sort of failure. Free the packet. + IPSInfo.ipsi_outdiscards++; + FreeFWPacket(Packet); + } + } else { // Couldn't get a packet, so drop this. + IPSInfo.ipsi_outdiscards++; + if (Options) + CTEFreeMem(Options); +#ifdef _PNP_POWER + DerefIF(IF); +#endif + } + } else { // Forward called, but forwarding + // turned off. + if (DestType != DEST_BCAST && DestType != DEST_SN_BCAST) { + // No need to go through here for strictly broadcast packets, + // although we want to bump the counters for remote bcast stuff. + IPSInfo.ipsi_inaddrerrors++; + + if (!IS_BCAST_DEST(DestType)) { + if (DestType == DEST_LOCAL) // Called when local, must be SR. + SendICMPErr(SrcNTE->nte_addr, Header, + ICMP_DEST_UNREACH, SR_FAILED, 0); + } + } + } + +} + +//* AddNTERoutes - Add the routes for an NTE. +// +// Called during initalization or during DHCP address assignment to add +// routes. We add routes for the address of the NTE, including routes +// to the subnet and the address itself. +// +// Input: NTE - NTE for which to add routes. +// +// Returns: TRUE if they were all added, FALSE if not. +// +uint +AddNTERoutes(NetTableEntry *NTE) +{ + IPMask Mask, SNMask; + Interface *IF; + CTELockHandle Handle; + IPAddr AllSNBCast; + IP_STATUS Status; + + // First, add the route to the address itself. This is a route through + // the loopback interface. + + if (AddRoute(NTE->nte_addr, HOST_MASK, IPADDR_LOCAL, LoopNTE->nte_if, + LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL) != IP_SUCCESS) + return FALSE; + + Mask = IPNetMask(NTE->nte_addr); + IF = NTE->nte_if; + + // Now add the route for the all-subnet's broadcast, if one doesn't already + // exist. There is special case code to handle this in SendIPBCast, so the + // exact interface we add this on doesn't really matter. + + CTEGetLock(&RouteTableLock, &Handle); + AllSNBCast = (NTE->nte_addr & Mask) | (IF->if_bcast & ~Mask); + if (LookupRTE(AllSNBCast, NULL_IP_ADDR, HOST_ROUTE_PRI) == NULL) { + + Status = LockedAddRoute(AllSNBCast, HOST_MASK, IPADDR_LOCAL, IF, + NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL); + + } else + Status = IP_SUCCESS; + + CTEFreeLock(&RouteTableLock, Handle); + + if (Status != IP_SUCCESS) + return FALSE; + + // If we're doing IGMP, add the route to the multicast address. + if (IGMPLevel != 0) { + if (AddRoute(CLASSD_MASK, CLASSD_MASK, IPADDR_LOCAL, NTE->nte_if, + NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS) + return FALSE; + } + + if(NTE->nte_mask != HOST_MASK) + { + // And finally the route to the subnet. + SNMask = NTE->nte_mask; + if (AddRoute(NTE->nte_addr & SNMask, SNMask, IPADDR_LOCAL, NTE->nte_if, + NTE->nte_mss, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL) != IP_SUCCESS) + return FALSE; + } + + return TRUE; +} + + +#ifndef CHICAGO +#pragma BEGIN_INIT +#endif + +uint BCastMinMTU = 0xffff; + +//* InitNTERouting - do per NTE route initialization. +// +// Called when we need to initialize per-NTE routing. For the specified NTE, +// call AddNTERoutes to add a route for a net bcast, subnet bcast, and local +// attached subnet. The net bcast entry is sort of a filler - net and +// global bcasts are always handled specially. For this reason we specify +// the FirstInterface when adding the route. Subnet bcasts are assumed to +// only go out on one interface, so the actual interface to be used is +// specifed. If two interfaces are on the same subnet the last interface is +// the one that will be used. +// +// Input: NTE - NTE for which routing is to be initialized. +// NumGWs - Number of default gateways to add. +// GWList - List of default gateways. +// +// Returns: TRUE if we succeed, FALSE if we don't. +// +uint +InitNTERouting(NetTableEntry *NTE, uint NumGWs, IPAddr *GWList) +{ + uint i; + Interface *IF; + + CTERefillMem(); + if (NTE != LoopNTE) { + BCastMinMTU = MIN(BCastMinMTU, NTE->nte_mss); + + IF = NTE->nte_if; + AddRoute(IF->if_bcast, HOST_MASK, IPADDR_LOCAL, FirstIF, + BCastMinMTU, 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL);// Route for local + // bcast. + if (NTE->nte_flags & NTE_VALID) { + if (!AddNTERoutes(NTE)) + return FALSE; + + // Now add the default routes that are present on this net. We + // don't check for errors here, but we should probably + // log an error. + for (i = 0; i < NumGWs;i++) { + IPAddr GWAddr; + + GWAddr = net_long(GWList[i]); + + if (IP_ADDR_EQUAL(GWAddr, NTE->nte_addr)) { + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, + IPADDR_LOCAL, NTE->nte_if, NTE->nte_mss, 1, + IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL); + } else + AddRoute(NULL_IP_ADDR, DEFAULT_MASK, + net_long(GWList[i]), NTE->nte_if, NTE->nte_mss, 1, + IRE_PROTO_LOCAL, ATYPE_OVERRIDE, NULL); + } + } + } + return TRUE; +} + +#ifdef CHICAGO +#pragma BEGIN_INIT +#endif + +//* InitRouting - Initialize our routing table. +// +// Called during initialization to initialize the routing table. +// +// Entry: Nothing. +// +// Returns: True if we succeeded, False if we didn't. +// +int +InitRouting(IPConfigInfo *ci) +{ + int i; + + CTERefillMem(); + + CTEInitLock(&RouteTableLock); + + DefGWConfigured = 0; + DefGWActive = 0; + + CTEMemSet(&DummyInterface, 0, sizeof(DummyInterface)); + DummyInterface.ri_if.if_xmit = DummyXmit; + DummyInterface.ri_if.if_transfer = DummyXfer; + DummyInterface.ri_if.if_close = DummyClose; + DummyInterface.ri_if.if_invalidate = DummyInvalidate; + DummyInterface.ri_if.if_qinfo = DummyQInfo; + DummyInterface.ri_if.if_setinfo = DummySetInfo; + DummyInterface.ri_if.if_getelist = DummyGetEList; + DummyInterface.ri_if.if_addaddr = DummyAddAddr; + DummyInterface.ri_if.if_deladdr = DummyDelAddr; + DummyInterface.ri_if.if_bcast = IP_LOCAL_BCST; + DummyInterface.ri_if.if_speed = 10000000; + DummyInterface.ri_if.if_mtu = 1500; + DummyInterface.ri_if.if_index = INVALID_IF_INDEX; + + for (i = 0; i < ROUTE_TABLE_SIZE; i++) + RouteTable[i] = (RouteTableEntry *)NULL; + + // We've created at least one net. We need to add routing table entries for + // the global broadcast address, as well as for subnet and net broadcasts, + // and routing entries for the local subnet. We alse need to add a loopback + // route for the loopback net. Below, we'll add a host route for ourselves + // through the loopback net. + AddRoute(LOOPBACK_ADDR & CLASSA_MASK, CLASSA_MASK, IPADDR_LOCAL, + LoopNTE->nte_if, LOOPBACK_MSS, 1, IRE_PROTO_LOCAL, ATYPE_PERM, NULL); + // Route for loopback. + RouterConfigured = (uchar)ci->ici_gateway; + + CTEInitTimer(&IPRouteTimer); + + CTEStartTimer(&IPRouteTimer, IP_ROUTE_TIMEOUT, IPRouteTimeout, NULL); + return TRUE; + +} + +//* InitGateway - Initialize our gateway functionality. +// +// Called during init. time to initialize our gateway functionality. If we're +// not connfigured as a router, we do nothing. If we are, we allocate the +// resources we need and do other router initialization. +// +// Input: ci - Config info. +// +// Returns: TRUE if we succeed, FALSE if don't. +// +uint +InitGateway(IPConfigInfo *ci) +{ + uint FWBufSize, FWPackets; + uint FWBufCount; + NDIS_STATUS Status; + NDIS_HANDLE BufferPool, FWBufferPool, PacketPool; + IPHeader *HeaderPtr = NULL; + uchar *FWBuffer = NULL; + PNDIS_BUFFER Buffer; + PNDIS_PACKET Packet; + RouteInterface *RtIF; + NetTableEntry *NTE; + uint i; + + // If we're going to be a router, allocate and initialize the resources we + // need for that. + BCastRSQ = NULL; + if (RouterConfigured) { + + + CTERefillMem(); + RtPI = CTEAllocMem(sizeof(ProtInfo)); + if (RtPI == (ProtInfo *)NULL) + goto failure; + + RtPI->pi_xmitdone = FWSendComplete; + + CTEInitLock(&FWPacketFreeLock); + CTEInitLock(&FWBufFreeLock); + + MaxFWBufferSize = ci->ici_maxfwbufsize; + MaxFWPackets = ci->ici_maxfwpackets; + FWBufSize = MIN(ci->ici_fwbufsize, MaxFWBufferSize); + FWPackets = MIN(ci->ici_fwpackets, MaxFWPackets); + + for (NTE = NetTableList; NTE != NULL; NTE = NTE->nte_next) { + RtIF = (RouteInterface *)NTE->nte_if; + + RtIF->ri_q.rsq_qh.fq_next = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_qh.fq_prev = &RtIF->ri_q.rsq_qh; + RtIF->ri_q.rsq_running = FALSE; + RtIF->ri_q.rsq_pending = 0; + RtIF->ri_q.rsq_qlength = 0; + CTEInitLock(&RtIF->ri_q.rsq_lock); + } + + BCastRSQ = CTEAllocMem(sizeof(RouteSendQ)); + + if (BCastRSQ == (RouteSendQ *)NULL) + goto failure; + + BCastRSQ->rsq_qh.fq_next = &BCastRSQ->rsq_qh; + BCastRSQ->rsq_qh.fq_prev = &BCastRSQ->rsq_qh; + BCastRSQ->rsq_pending = 0; + BCastRSQ->rsq_maxpending = DEFAULT_MAX_PENDING; + BCastRSQ->rsq_qlength = 0; + BCastRSQ->rsq_running = FALSE; + CTEInitLock(&BCastRSQ->rsq_lock); + + RtIF = (RouteInterface *)&LoopInterface; + RtIF->ri_q.rsq_maxpending = DEFAULT_MAX_PENDING; + + // Round the specified size down to a multiple of our FW buf size. + CTERefillMem(); + FWBufCount = FWBufSize / FW_BUF_SIZE; + FWBufSize = FWBufCount * FW_BUF_SIZE; + + // Allocate the buffers, packets, and memory for our header buffers. + HeaderPtr = CTEAllocMem(FWPackets * sizeof(IPHeader)); + if (HeaderPtr == (IPHeader *)NULL) + goto failure; + + NdisAllocateBufferPool(&Status, &BufferPool, FWPackets); + if (Status != NDIS_STATUS_SUCCESS) { + goto failure; // Couldn't be a router, fail. + } + + NdisAllocatePacketPool(&Status, &PacketPool, FWPackets, sizeof(FWContext)); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreeBufferPool(BufferPool); + goto failure; + } + + // Allocate resources for our the buffer pool. + CTERefillMem(); + FWBuffer = CTEAllocMem(FWBufSize); + if (FWBuffer == NULL) { // Couldn't get buffer space. + NdisFreePacketPool(PacketPool); + NdisFreeBufferPool(BufferPool); + goto failure; + } + + NdisAllocateBufferPool(&Status, &FWBufferPool, FWBufCount); + if (Status != NDIS_STATUS_SUCCESS) { + NdisFreePacketPool(PacketPool); + NdisFreeBufferPool(BufferPool); + goto failure; + } + + // Everythings allocated. Put it all together and stick them on the + // free list. + for (i = 0; i < FWPackets; i++) { + FWContext *FWC; + + NdisAllocateBuffer(&Status, &Buffer, BufferPool, HeaderPtr, + sizeof(IPHeader)); + if (Status != NDIS_STATUS_SUCCESS) + DEBUGCHK; + NdisAllocatePacket(&Status, &Packet, PacketPool); + if (Status != NDIS_STATUS_SUCCESS) + DEBUGCHK; + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(FWContext)); + FWC = (FWContext *)Packet->ProtocolReserved; + FWC->fc_hndisbuff = Buffer; + FWC->fc_hbuff = HeaderPtr; + FWC->fc_pc.pc_common.pc_flags = PACKET_FLAG_FW; + FWC->fc_pc.pc_common.pc_owner = PACKET_OWNER_IP; + FWC->fc_pc.pc_pi = RtPI; + FWC->fc_pc.pc_context = Packet; + + FreeFWPacket(Packet); + HeaderPtr++; + } + + for (i = 0; i < FWBufCount; i++) { + NdisAllocateBuffer(&Status, &Buffer, FWBufferPool, FWBuffer, + FW_BUF_SIZE); + if (Status != NDIS_STATUS_SUCCESS) + DEBUGCHK; + + Buffer->Next = FWBufFree; // BUGBUG portability + FWBufFree = Buffer; + FWBuffer += FW_BUF_SIZE; + } + + CurrentFWPackets = FWPackets; + CurrentFWBufferSize = FWBufSize; + + +#if 0 + ForwardBCast = (uchar)ci->ici_fwbcast; +#else + ForwardBCast = FALSE; +#endif + ForwardPackets = TRUE; + } + + return TRUE; + +failure: + if (RtPI != NULL) + CTEFreeMem(RtPI); + if (BCastRSQ != NULL) + CTEFreeMem(BCastRSQ); + if (HeaderPtr != NULL) + CTEFreeMem(HeaderPtr); + if (FWBuffer != NULL) + CTEFreeMem(FWBuffer); + + ForwardBCast = FALSE; + ForwardPackets = FALSE; + RouterConfigured = FALSE; + return FALSE; + +} +#pragma END_INIT diff --git a/private/ntos/tdi/tcpip/ip/iproute.h b/private/ntos/tdi/tcpip/ip/iproute.h new file mode 100644 index 000000000..e06af7100 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iproute.h @@ -0,0 +1,107 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IPROUTE.H - IP routing definitions. +// +// This file contains all of the definitions for routing code that are +// visible to modules outside iproute.c + + +extern struct Interface *LookupNextHop(IPAddr Dest, IPAddr Src, + IPAddr *FirstHop, uint *MTU); +extern struct Interface *LookupNextHopWithBuffer(IPAddr Dest, IPAddr Src, + IPAddr *FirstHop, uint *MTU, uchar Protocol, + uchar *Buffer, uint Length); + +extern void FlushATCache(IPAddr Address); +extern uchar GetAddrType(IPAddr Address); +extern uint InvalidSourceAddress(IPAddr Address); +extern uchar GetLocalNTE(IPAddr Address, NetTableEntry **NTE); +extern uchar IsBCastOnNTE(IPAddr Address, NetTableEntry *NTE); +extern void SendFWPacket(PNDIS_PACKET Packet, NDIS_STATUS Status, + uint DataLength); +extern void IPForward(NetTableEntry *SrcNTE, + IPHeader UNALIGNED *Header, uint HeaderLength, + void *Data, uint BufferLength, + NDIS_HANDLE LContext1, uint LContext2, + uchar DestType); + +extern uint AttachRCEToRTE(RouteCacheEntry *RCE, uchar Protocol, + uchar *Buffer, uint Length); +extern void Redirect(NetTableEntry *NTE, IPAddr RDSrc, + IPAddr Target, IPAddr Src, IPAddr FirstHop); +extern IP_STATUS AddRoute(IPAddr Destination, IPMask Mask, + IPAddr FirstHop, Interface *OutIF, uint MTU, + uint Metric, uint Proto, uint AType, + void *Context); +extern IP_STATUS DeleteRoute(IPAddr Destination, IPMask Mask, + IPAddr FirstHop, Interface *OutIF); +extern void *GetRouteContext(IPAddr Destination, IPAddr Source); + +extern NetTableEntry *BestNTEForIF(IPAddr Dest, Interface *IF); +extern void RTWalk(uint (*CallFunc)(struct RouteTableEntry *, + void *, void *), void *Context, void *Context1); + +extern uint DeleteRTEOnIF(struct RouteTableEntry *RTE, + void *Context, void *Context1); +extern uint InvalidateRCEOnIF(struct RouteTableEntry *RTE, + void *Context, void *Context1); +extern uint SetMTUOnIF(struct RouteTableEntry *RTE, void *Context, + void *Context1); +extern uint SetMTUToAddr(struct RouteTableEntry *RTE, void *Context, + void *Context1); +extern uint AddNTERoutes(struct NetTableEntry *NTE); +extern void IPCheckRoute(IPAddr Dest, IPAddr Src); +extern void RouteFragNeeded(IPHeader UNALIGNED *IPH, ushort NewMTU); +extern IP_STATUS IPGetPInfo(IPAddr Dest, IPAddr Src, uint *NewMTU, + uint *MaxPathSpeed); +extern int InitRouting(struct IPConfigInfo *ci); +extern uint InitNTERouting(NetTableEntry *NTE, uint NumGWs, + IPAddr *GW); +extern uint InitGateway(struct IPConfigInfo *ci); +extern IPAddr OpenRCE(IPAddr Address, IPAddr Src, RouteCacheEntry **RCE, + uchar *Type, ushort *MSS, IPOptInfo *OptInfo); +extern void CloseRCE(RouteCacheEntry *RCE); +extern uint IsRouteICMP(IPAddr Dest, IPMask Mask, IPAddr FirstHop, + Interface *OutIF); + +EXTERNAL_LOCK(RouteTableLock) + +extern uint DeadGWDetect; +extern uint PMTUDiscovery; +extern uchar ForwardPackets; +extern uchar RouterConfigured; +// Pointer to callout routine for dial on demand. +extern IPMapRouteToInterfacePtr DODCallout; + +// Pointer to packet filter handler. +extern IPPacketFilterPtr ForwardFilterPtr; + +#define IPADDR_LOCAL 0xffffffff // Indicates that IP address is + // directly connected. + +#define IP_LOCAL_BCST 0xffffffff +#define IP_ZERO_BCST 0 + +#define HOST_MASK 0xffffffff +#define DEFAULT_MASK 0 + + +#ifdef NT +#define LOOPBACK_MSS (1500 - sizeof(IPHeader)) +#else // NT +#define LOOPBACK_MSS 256 +#endif // NT + + +#define LOOPBACK_ADDR 0x0100007f +#define IP_LOOPBACK(x) (((x) & CLASSA_MASK) == 0x7f) + +#define ATYPE_PERM 0 // A permanent route. +#define ATYPE_OVERRIDE 1 // Semi-permanent - can be + // overriden. +#define ATYPE_TEMP 2 // A temporary route. + diff --git a/private/ntos/tdi/tcpip/ip/iprtdef.h b/private/ntos/tdi/tcpip/ip/iprtdef.h new file mode 100644 index 000000000..1082646ba --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/iprtdef.h @@ -0,0 +1,135 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +#include "ipfilter.h" + +//** IPRTDEF.H - IP private routing definitions. +// +// This file contains all of the definitions private to the routing +// module. + +//* Route table entry. + +struct RouteTableEntry { + struct RouteTableEntry *rte_next; // Next in hash chain. + IPAddr rte_dest; // Destination of route. + IPMask rte_mask; // Mask to use when examining route. + IPAddr rte_addr; // First hop for this route. + uint rte_priority; // Priority of this route: + // essentially the number + // of bits set in mask. + uint rte_metric; // Metric of route. Lower + // is better. + uint rte_mtu; // MTU for this route. + struct Interface *rte_if; // Outbound interface. + RouteCacheEntry *rte_rcelist; + ushort rte_type; // Type of route. + ushort rte_flags; // Flags for route. + uint rte_admintype; // Admin type of route. + uint rte_proto; // How we learned about the + // route. + uint rte_valid; // Up time (in seconds) + // route was last known to be + // valid. + uint rte_mtuchange; // System up time (in seconds) + // MTU was changed. + ROUTE_CONTEXT rte_context; // Dial-on-demand context for this route. +}; /* RouteTableEntry */ + +#define ADDR_FROM_RTE(R, A) (IP_ADDR_EQUAL((R)->rte_addr, IPADDR_LOCAL) ? (A) : \ + (R)->rte_addr) +#define IF_FROM_RTE(R) ((R)->rte_if) +#define MTU_FROM_RTE(R) ((R)->rte_mtu) +#define SRC_FROM_RTE(R) ((R)->rte_src) + +#define RTE_VALID 1 +#define RTE_INCREASE 2 // Set if last MTU change was an + // increase. +#define RTE_IF_VALID 4 // Set to TRUE if rte_if is valid. + +#define IP_ROUTE_TIMEOUT 60L*1000L // Route timer fires once a minute. + +#define MTU_INCREASE_TIME 120 // Number of seconds after increase + // to re-increase. +#define MTU_DECREASE_TIME 600 // Number of seconds after decrease + // to re-increase. + +#define MAX_ICMP_ROUTE_VALID 600 // Time to timeout an unused ICMP + // derived route, in seconds. + +#define MIN_RT_VALID 60 // Minimum time a route is assumed + // to be valid, in seconds. + +#define MIN_VALID_MTU 68 // Minimum valid MTU we can have. +#define HOST_ROUTE_PRI 32 +#define DEFAULT_ROUTE_PRI 0 + +typedef struct RouteTableEntry RouteTableEntry; + +//* Forward Q linkage structure. +struct FWQ { + struct FWQ *fq_next; + struct FWQ *fq_prev; +}; /* FWQ */ + +typedef struct FWQ FWQ; + + +//* Forward context structure, used when TD'ing a packet to be forwarded. +struct FWContext { + PacketContext fc_pc; // Dummy packet context for send routines. + FWQ fc_q; // Queue structure. + PNDIS_BUFFER fc_hndisbuff; // Pointer to NDIS buffer for header. + IPHeader *fc_hbuff; // Header buffer. + PNDIS_BUFFER fc_buffhead; // Head of list of NDIS buffers. + PNDIS_BUFFER fc_bufftail; // Tail of list of NDIS buffers. + uchar *fc_options; // Options, + Interface *fc_if; // Destination interface. + IPAddr fc_outaddr; // IP address of interface. + uint fc_mtu; // Max MTU outgoing. + NetTableEntry *fc_srcnte; // Source NTE. + IPAddr fc_nexthop; // Next hop. + uint fc_datalength; // Length in bytes of data. + OptIndex fc_index; // Index of relevant options. + uchar fc_optlength; // Length of options. + uchar fc_sos; // Send on source indicator. + uchar fc_dtype; // Dest type. + uchar fc_pad; +}; /* FWContext */ + +typedef struct FWContext FWContext; + +#define PACKET_FROM_FWQ(_fwq_) (PNDIS_PACKET)((uchar *)(_fwq_) - (offsetof(struct FWContext, fc_q) + \ + offsetof(NDIS_PACKET, ProtocolReserved))) + +//* Route send queue structure. This consists of a dummy FWContext for use as +// a queue head, a count of sends pending on the interface, and a count of packets +// in the queue. +struct RouteSendQ { + FWQ rsq_qh; + uint rsq_pending; + uint rsq_maxpending; + uint rsq_qlength; + uint rsq_running; + DEFINE_LOCK_STRUCTURE(rsq_lock) +}; /* RouteSendQ */ + +typedef struct RouteSendQ RouteSendQ; + + +//* Routing interface, a superset of the ordinary interface when we're configured as a router. +struct RouteInterface { + Interface ri_if; + RouteSendQ ri_q; +}; /* RouteInterface */ + +typedef struct RouteInterface RouteInterface; + +extern IPMask IPMaskTable[]; + +#define IPNetMask(a) IPMaskTable[(*(uchar *)&(a)) >> 4] + + diff --git a/private/ntos/tdi/tcpip/ip/ipstatus.c b/private/ntos/tdi/tcpip/ip/ipstatus.c new file mode 100644 index 000000000..e71e36280 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipstatus.c @@ -0,0 +1,205 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** ipstatus.c - IP status routines. +// +// This module contains all routines related to status indications. +// + + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "llipif.h" +#include "iproute.h" +#include "ipinfo.h" + +#if 0 +EXTERNAL_LOCK(PILock) +#endif +extern ProtInfo IPProtInfo[]; // Protocol information table. +extern int NextPI; // Next PI field to be used. +extern ProtInfo *RawPI; // Raw IP protinfo + +//* FindULStatus - Find the upper layer status handler. +// +// Called when we need to find the upper layer status handler for a particular +// protocol. +// +// Entry: Protocol - Protocol to look up +// +// Returns: A pointer to the ULStatus proc, or NULL if it can't find one. +// +ULStatusProc +FindULStatus(uchar Protocol) +{ + ULStatusProc StatusProc = (ULStatusProc)NULL; + int i; +#if 0 + CTELockHandle Handle; + + + CTEGetLock(&PILock, &Handle); +#endif + for ( i = 0; i < NextPI; i++) { + if (IPProtInfo[i].pi_protocol == Protocol) { + StatusProc = IPProtInfo[i].pi_status; +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + return StatusProc; + } + } + + if (RawPI != NULL) { + StatusProc = RawPI->pi_status; + } + +#if 0 + CTEFreeLock(&PILock, Handle); +#endif + + return StatusProc; +} + + +//* ULMTUNotify - Notify the upper layers of an MTU change. +// +// Called when we need to notify the upper layers of an MTU change. We'll +// loop through the status table, calling each status proc with the info. +// +// This routine doesn't do any locking of the protinfo table. We might need +// to check this. +// +// Input: Dest - Destination address affected. +// Src - Source address affected. +// Prot - Protocol that triggered change, if any. +// Ptr - Pointer to protocol info, if any. +// NewMTU - New MTU to tell them about. +// +// Returns: Nothing. +// +void +ULMTUNotify(IPAddr Dest, IPAddr Src, uchar Prot, void *Ptr, uint NewMTU) +{ + ULStatusProc StatusProc; + int i; + + // First, notify the specific client that a frame has been dropped + // and needs to be retransmitted. + + StatusProc = FindULStatus(Prot); + if (StatusProc != NULL) + (*StatusProc)(IP_NET_STATUS, IP_SPEC_MTU_CHANGE, Dest, Src, + NULL_IP_ADDR, NewMTU, Ptr); + + // Now notify all UL entities that the MTU has changed. + for (i = 0; i < NextPI; i++) { + StatusProc = IPProtInfo[i].pi_status; + + if (StatusProc != NULL) + (*StatusProc)(IP_HW_STATUS, IP_MTU_CHANGE, Dest, Src, NULL_IP_ADDR, + NewMTU, Ptr); + } +} + +#ifdef CHICAGO + +//* IPULUnloadNotify - Notify clients that we're unloading. +// +// Called when we receive an unload message. We'll notify the upper layers +// that we're unloading. +// +// Input: Nothing. +// +// Returns: Nothing. +// +void +IPULUnloadNotify(void) +{ + ULStatusProc StatusProc; + int i; + + // Now notify all UL entities that the MTU has changed. + for (i = 0; i < NextPI; i++) { + StatusProc = IPProtInfo[i].pi_status; + + if (StatusProc != NULL) + (*StatusProc)(IP_HW_STATUS, IP_UNLOAD, NULL_IP_ADDR, NULL_IP_ADDR, + NULL_IP_ADDR, 0, NULL); + } +} + +#endif + +//* IPStatus - Handle a link layer status call. +// +// This is the routine called by the link layer when some sort of 'important' +// status change occurs. +// +// Entry: Context - Context value we gave to the link layer. +// Status - Status change code. +// Buffer - Pointer to buffer of status information. +// BufferSize - Size of Buffer. +// +// Returns: Nothing. +// +void +IPStatus(void *Context, uint Status, void *Buffer, uint BufferSize) +{ + NetTableEntry *NTE = (NetTableEntry *)Context; + LLIPSpeedChange *LSC; + LLIPMTUChange *LMC; + LLIPAddrMTUChange *LAM; + uint NewMTU; + Interface *IF; + + + switch (Status) { + + case LLIP_STATUS_SPEED_CHANGE: + if (BufferSize < sizeof(LLIPSpeedChange)) + break; + LSC = (LLIPSpeedChange *)Buffer; + NTE->nte_if->if_speed = LSC->lsc_speed; + break; + case LLIP_STATUS_MTU_CHANGE: + if (BufferSize < sizeof(LLIPMTUChange)) + break; + // Walk through the NTEs on the IF, updating their MTUs. + IF = NTE->nte_if; + LMC = (LLIPMTUChange *)Buffer; + IF->if_mtu = LMC->lmc_mtu; + NewMTU = LMC->lmc_mtu - sizeof(IPHeader); + NTE = IF->if_nte; + while (NTE != NULL) { + NTE->nte_mss = NewMTU; + NTE = NTE->nte_ifnext; + } + RTWalk(SetMTUOnIF, IF, &NewMTU); + break; + case LLIP_STATUS_ADDR_MTU_CHANGE: + if (BufferSize < sizeof(LLIPAddrMTUChange)) + break; + // The MTU for a specific remote address has changed. Update all + // routes that use that remote address as a first hop, and then + // add a host route to that remote address, specifying the new + // MTU. + LAM = (LLIPAddrMTUChange *)Buffer; + NewMTU = LAM->lam_mtu - sizeof(IPHeader); + RTWalk(SetMTUToAddr, &LAM->lam_addr, &NewMTU); + AddRoute(LAM->lam_addr, HOST_MASK, IPADDR_LOCAL, NTE->nte_if, NewMTU, + 1, IRE_PROTO_LOCAL, ATYPE_OVERRIDE, GetRouteContext(LAM->lam_addr, + NTE->nte_addr)); + break; + default: + break; + } + +} + diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.c b/private/ntos/tdi/tcpip/ip/ipxmit.c new file mode 100644 index 000000000..61ee32b90 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipxmit.c @@ -0,0 +1,1949 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//*** ipxmit.c - IP transmit routines. +// +// This module contains all transmit related IP routines. +// + +#include "oscfg.h" +#include "cxport.h" +#include "ndis.h" +#include "ip.h" +#include "ipdef.h" +#include "ipinit.h" +#include "info.h" +#include "iproute.h" +#include "iprtdef.h" +#include "ipfilter.h" + +typedef struct NdisResEntry { + struct NdisResEntry *nre_next; + NDIS_HANDLE nre_handle; + uchar *nre_buffer; +} NdisResEntry; + +extern BufferReference *GetBufferReference(void); + +extern NetTableEntry *NetTableList; // Pointer to the net table. +extern NetTableEntry *LoopNTE; // Pointer to loopback NTE. +extern NetTableEntry *DHCPNTE; // Pointer to NTE currently being + // DHCP'd. + +extern ulong TimeStamp; // Starting timestamp. +extern ulong TSFlag; // Mask to use on this. +extern uint NumNTE; + +//* Global variables for buffers and packets. +DEFINE_LOCK_STRUCTURE(HeaderLock) +#ifdef NT +SLIST_HEADER PacketList; +SLIST_HEADER HdrBufList; +#else +PNDIS_PACKET PacketList; +PNDIS_BUFFER HdrBufList = NULL; +#endif + +NdisResEntry *PacketPoolList = NULL; +NdisResEntry *HdrPoolList = NULL; + +uint CurrentPacketCount = 0; +uint MaxPacketCount = 0xfffffff; + +uint CurrentHdrBufCount = 0; +uint MaxHdrBufCount = 0xffffffff; + +NDIS_HANDLE BufferPool; + +#define HDR_BUF_GROW_COUNT 16 +#define PACKET_GROW_COUNT 16 + +//* Global IP ID. +ulong IPID; + +//** FreeIPHdrBuffer - Free a buffer back to the pool. +// +// Input: Buffer - Hdr buffer to be freed. +// +// Returns: Nothing. +// +void +FreeIPHdrBuffer(PNDIS_BUFFER Buffer) +{ + +#ifdef VXD + NDIS_BUFFER_LINKAGE(Buffer) = HdrBufList; + HdrBufList = Buffer; +#else + + ExInterlockedPushEntrySList( + &HdrBufList, + STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next), + &HeaderLock + ); + +#endif + +} + +//** FreeIPBufferChain - Free a chain of IP buffers. +// +// This routine takes a chain of NDIS_BUFFERs, and frees them all. +// +// Entry: Buffer - Pointer to buffer chain to be freed. +// +// Returns: Nothing. +// +void +FreeIPBufferChain(PNDIS_BUFFER Buffer) +{ + PNDIS_BUFFER NextBuffer; + + while (Buffer != (PNDIS_BUFFER)NULL) { + NdisGetNextBuffer(Buffer, &NextBuffer); + NdisFreeBuffer(Buffer); + Buffer = NextBuffer; + } +} + +//* FreeIPPacket - Free an IP packet when we're done with it. +// +// Called when a send completes and a packet needs to be freed. We look at the +// packet, decide what to do with it, and free the appropriate components. +// +// Entry: Packet - Packet to be freed. +// +// Returns: Pointer to next unfreed buffer on packet, or NULL if all buffers freed +// (i.e. this was a fragmented packet). +// +PNDIS_BUFFER +FreeIPPacket(PNDIS_PACKET Packet) +{ + PNDIS_BUFFER NextBuffer, OldBuffer; + PacketContext *pc = (PacketContext *)Packet->ProtocolReserved; + + + // BUGBUG - Get NDIS fixed to make this portable. +#ifdef VXD + NextBuffer = Packet->Private.Head; +#else // VXD + NdisQueryPacket(Packet, NULL, NULL, &NextBuffer, NULL); +#endif // VXD + + // If there's no IP header on this packet, we have nothing else to do. + if (!(pc->pc_common.pc_flags & (PACKET_FLAG_IPHDR | PACKET_FLAG_FW))) { + CTEAssert(pc->pc_common.pc_flags == 0); + + NdisReinitializePacket(Packet); + +#ifdef VXD + pc->pc_common.pc_link = PacketList; + PacketList = Packet; +#else + ExInterlockedPushEntrySList( + &PacketList, + STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next), + &HeaderLock + ); +#endif + + return NextBuffer; + } + + pc->pc_common.pc_flags &= ~PACKET_FLAG_IPHDR; + + OldBuffer = NextBuffer; + CTEAssert(OldBuffer != NULL); + + NextBuffer = NDIS_BUFFER_LINKAGE(NextBuffer); + + if (pc->pc_common.pc_flags & PACKET_FLAG_OPTIONS) { // Have options with + // this packet. + PNDIS_BUFFER OptBuffer; + void *Options; + uint OptSize; + + OptBuffer = NextBuffer; + CTEAssert(OptBuffer != NULL); + + NdisGetNextBuffer(OptBuffer,&NextBuffer); + + CTEAssert(NextBuffer != NULL); + + NdisQueryBuffer(OptBuffer, &Options, &OptSize); + // If this is a FW packet, the options don't really belong to us, so + // don't free them. + if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) + CTEFreeMem(Options); + NdisFreeBuffer(OptBuffer); + pc->pc_common.pc_flags &= ~PACKET_FLAG_OPTIONS; + } + + if (pc->pc_common.pc_flags & PACKET_FLAG_IPBUF) { // This packet is all + // IP buffers. + (void)FreeIPBufferChain(NextBuffer); + NextBuffer = (PNDIS_BUFFER)NULL; + pc->pc_common.pc_flags &= ~PACKET_FLAG_IPBUF; + } + + + if (!(pc->pc_common.pc_flags & PACKET_FLAG_FW)) { + FreeIPHdrBuffer(OldBuffer); + NdisReinitializePacket(Packet); +#ifdef _PNP_POWER + pc->pc_if = NULL; +#endif + +#ifdef VXD + pc->pc_common.pc_link = PacketList; + PacketList = Packet; +#else + ExInterlockedPushEntrySList( + &PacketList, + STRUCT_OF(SINGLE_LIST_ENTRY, &(pc->pc_common.pc_link), Next), + &HeaderLock + ); +#endif + } + + return NextBuffer; +} + +//** GrowIPPacketList - Grow the number of packets in our list. +// +// Called when we need to grow the number of packets in our list. We assume +// this routine is called with the HeaderLock held. We check to see if +// we've reached our limit on the number of packets, and if we haven't we'll +// grow the free list. +// +// Input: Nothing. +// +// Returns: Pointer to newly allocated packet, or NULL if this faild. +// +PNDIS_PACKET +GrowIPPacketList(void) +{ + NdisResEntry *NewEntry; + NDIS_STATUS Status; + PNDIS_PACKET Packet, ReturnPacket; + uint i; + CTELockHandle Handle; + + CTEGetLock(&HeaderLock, &Handle); + + if (CurrentPacketCount >= MaxPacketCount) + goto failure; + + // First, allocate a tracking structure. + NewEntry = CTEAllocMem(sizeof(NdisResEntry)); + if (NewEntry == NULL) + goto failure; + + // Got a tracking structure. Now allocate a packet pool. + NdisAllocatePacketPool(&Status, &NewEntry->nre_handle, PACKET_GROW_COUNT, + sizeof(PacketContext)); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(NewEntry); + goto failure; + } + + // We've allocated the pool. Now initialize the packets, and link them + // on the free list. + ReturnPacket = NULL; + + // Link the new NDIS resource tracker entry onto the list. + NewEntry->nre_next = PacketPoolList; + PacketPoolList = NewEntry; + CurrentPacketCount += PACKET_GROW_COUNT; + CTEFreeLock(&HeaderLock, Handle); + + for (i = 0; i < PACKET_GROW_COUNT; i++) { + PacketContext *PC; + + NdisAllocatePacket(&Status, &Packet, NewEntry->nre_handle); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); + break; + } + + CTEMemSet(Packet->ProtocolReserved, 0, sizeof(PacketContext)); + PC = (PacketContext *)Packet->ProtocolReserved; + PC->pc_common.pc_owner = PACKET_OWNER_IP; + if (i != 0) { + (void)FreeIPPacket(Packet); + } else + ReturnPacket = Packet; + + } + + // We've put all but the first one on the list. Return the first one. + return ReturnPacket; + +failure: + CTEFreeLock(&HeaderLock, Handle); + return NULL; + +} + + +//** GrowHdrBufList - Grow the our IP header buffer list. +// +// Called when we need to grow our header buffer list. We allocate a tracking +// structure, a buffer pool and a bunch of buffers. Put them all together +// and link them on the list. +// +// Input: Nothing. +// +// Returns: Pointer to newly header buffer, or NULL if this faild. +// +PNDIS_BUFFER +GrowHdrBufList(void) +{ + NdisResEntry *NewEntry; + NDIS_STATUS Status; + PNDIS_BUFFER Buffer, ReturnBuffer; + uchar *Hdr; + uint i; + CTELockHandle Handle; + + CTEGetLock(&HeaderLock, &Handle); + + // Make sure we can grow. + if (CurrentHdrBufCount >= MaxHdrBufCount) + goto failure; + + // First, allocate a tracking structure. + NewEntry = CTEAllocMem(sizeof(NdisResEntry)); + if (NewEntry == NULL) + goto failure; + + // Got a tracking structure. Now allocate a buffer pool. + NdisAllocateBufferPool(&Status, &NewEntry->nre_handle, HDR_BUF_GROW_COUNT); + + if (Status != NDIS_STATUS_SUCCESS) { + CTEFreeMem(NewEntry); + goto failure; + } + + // We've allocated the pool. Now allocate memory for the buffers. + Hdr = CTEAllocMem(sizeof(IPHeader) * HDR_BUF_GROW_COUNT); + if (Hdr == NULL) { + // Couldn't get memory for the headers. + NdisFreeBufferPool(NewEntry->nre_handle); + CTEFreeMem(NewEntry); + goto failure; + } + + NewEntry->nre_buffer = Hdr; + + NewEntry->nre_next = HdrPoolList; + HdrPoolList = NewEntry; + ReturnBuffer = NULL; + CurrentHdrBufCount += HDR_BUF_GROW_COUNT; + CTEFreeLock(&HeaderLock, Handle); + + for (i = 0; i < HDR_BUF_GROW_COUNT; i++) { + + NdisAllocateBuffer(&Status, &Buffer, NewEntry->nre_handle, + Hdr, sizeof(IPHeader)); + if (Status != NDIS_STATUS_SUCCESS) { + CTEAssert(FALSE); + break; + } + if (i != 0) { + FreeIPHdrBuffer(Buffer); + } else + ReturnBuffer = Buffer; + + Hdr += sizeof(IPHeader); + + } + + // Update the count for any we didn't actually allocate. + CTEInterlockedAddUlong(&CurrentHdrBufCount, i - HDR_BUF_GROW_COUNT, + &HeaderLock); + + // We've put all but the first one on the list. Return the first one. + return ReturnBuffer; + +failure: + CTEFreeLock(&HeaderLock, Handle); + return NULL; + +} + +//** GetIPPacket - Get an NDIS packet to use. +// +// A routine to allocate an NDIS packet. +// +// Entry: Nothing. +// +// Returns: Pointer to NDIS_PACKET if allocated, or NULL. +// +PNDIS_PACKET +GetIPPacket(void) +{ + PNDIS_PACKET Packet; + + +#ifdef VXD + Packet = PacketList; + if (Packet != (PNDIS_PACKET)NULL) { + PacketContext *PC; + + PC = (PacketContext *)Packet->ProtocolReserved; + PacketList = PC->pc_common.pc_link; + return Packet; +#else + PSINGLE_LIST_ENTRY Link; + PacketContext *PC; + struct PCCommon *Common; + + Link = ExInterlockedPopEntrySList( + &PacketList, + &HeaderLock + ); + if (Link != NULL) { + Common = STRUCT_OF(struct PCCommon, Link, pc_link); + PC = STRUCT_OF(PacketContext, Common, pc_common); + Packet = STRUCT_OF(NDIS_PACKET, PC, ProtocolReserved); + + return Packet; +#endif + + + } else { + // Couldn't get a packet. Try to grow the list. + Packet = GrowIPPacketList(); + } + + return Packet; +} + + +//** GetIPHdrBuffer - Get an IP header buffer. +// +// A routine to allocate an IP header buffer, with an NDIS buffer. +// +// Entry: Nothing. +// +// Returns: Pointer to NDIS_BUFFER if allocated, or NULL. +// +PNDIS_BUFFER +GetIPHdrBuffer(void) +{ + PNDIS_BUFFER Buffer; + +#ifdef VXD + Buffer = HdrBufList; + if (Buffer != NULL) { + + HdrBufList = NDIS_BUFFER_LINKAGE(Buffer); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; +#else + PSINGLE_LIST_ENTRY BufferLink; + + BufferLink = ExInterlockedPopEntrySList( + &HdrBufList, + &HeaderLock + ); + if (BufferLink != NULL) { + Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next); + NDIS_BUFFER_LINKAGE(Buffer) = NULL; + + return Buffer; + +#endif + + } else { + Buffer = GrowHdrBufList(); + } + + return Buffer; + +} + + +//** GetIPHeader - Get a header buffer and packet. +// +// Called when we need to get a header buffer and packet. We allocate both, +// and chain them together. +// +// Input: Pointer to where to store packet. +// +// Returns: Pointer to IP header. +// +IPHeader * +GetIPHeader(PNDIS_PACKET *PacketPtr) +{ + PNDIS_BUFFER Buffer; + PNDIS_PACKET Packet; + + + Packet = GetIPPacket(); + if (Packet != NULL) { + Buffer = GetIPHdrBuffer(); + if (Buffer != NULL) { + PacketContext *PC = (PacketContext *)Packet->ProtocolReserved; + + NdisChainBufferAtBack(Packet, Buffer); + *PacketPtr = Packet; + PC->pc_common.pc_flags |= PACKET_FLAG_IPHDR; + return (IPHeader *)NdisBufferVirtualAddress(Buffer); + + } else + FreeIPPacket(Packet); + } + return NULL; +} + + +//** ReferenceBuffer - Reference a buffer. +// +// Called when we need to update the count of a BufferReference strucutre, either +// by a positive or negative value. If the count goes to 0, we'll free the buffer +// reference and return success. Otherwise we'll return pending. +// +// Entry: BR - Pointer to buffer reference. +// Count - Amount to adjust refcount by. +// +// Returns: Success, or pending. +// +int +ReferenceBuffer(BufferReference *BR, int Count) +{ + CTELockHandle handle; + int NewCount; + + CTEGetLock(&BR->br_lock, &handle); + BR->br_refcount += Count; + NewCount = BR->br_refcount; + CTEFreeLock(&BR->br_lock, handle); + return NewCount; +} + +//* IPSendComplete - IP send complete handler. +// +// Called by the link layer when a send completes. We're given a pointer to a +// net structure, as well as the completing send packet and the final status of +// the send. +// +// Entry: Context - Context we gave to the link layer. +// Packet - Completing send packet. +// Status - Final status of send. +// +// Returns: Nothing. +// +void +IPSendComplete(void *Context, PNDIS_PACKET Packet, NDIS_STATUS Status) +{ + NetTableEntry *NTE = (NetTableEntry *)Context; + PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved; + PNDIS_BUFFER Buffer; + void (*xmitdone)(void *, PNDIS_BUFFER); // Pointer to xmit done routine. + void *UContext; // Upper layer context. + BufferReference *BufRef; // Buffer reference, if any. +#ifdef _PNP_POWER + Interface *IF; // The interface on which this + // completed. +#endif + + xmitdone = PContext->pc_pi->pi_xmitdone; // Copy useful information from packet. + UContext = PContext->pc_context; + BufRef = PContext->pc_br; +#ifdef _PNP_POWER + IF = PContext->pc_if; +#endif + + Buffer = FreeIPPacket(Packet); + if (BufRef == (BufferReference *)NULL) { +#ifdef DEBUG + if (!Buffer) + DEBUGCHK; +#endif + (*xmitdone)(UContext, Buffer); + } else { + if (!ReferenceBuffer(BufRef, -1)) { + Buffer = BufRef->br_buffer; +#ifdef DEBUG + if (!Buffer) + DEBUGCHK; +#endif + CTEFreeMem(BufRef); + (*xmitdone)(UContext, Buffer); + } else { +#ifdef _PNP_POWER + // We're not done with the send yet, so NULL the IF to + // prevent dereferencing it. + IF = NULL; +#endif + } + } + +#ifdef _PNP_POWER + // We're done with the packet now, we may need to dereference + // the interface. + if (IF == NULL) { + return; + } else { + DerefIF(IF); + } +#endif + +} + + +#ifndef NT + +//** xsum - Checksum a flat buffer. +// +// This is the lowest level checksum routine. It returns the uncomplemented +// checksum of a flat buffer. +// +// Entry: Buffer - Buffer to be checksummed. +// Size - Size in bytes of Buffer. +// +// Returns: The uncomplemented checksum of buffer. +// +ushort +xsum(void *Buffer, int Size) +{ + ushort UNALIGNED *Buffer1 = (ushort UNALIGNED *)Buffer; // Buffer expressed as shorts. + ulong csum = 0; + + while (Size > 1) { + csum += *Buffer1++; + Size -= sizeof(ushort); + } + + if (Size) + csum += *(uchar *)Buffer1; // For odd buffers, add in last byte. + + csum = (csum >> 16) + (csum & 0xffff); + csum += (csum >> 16); + return (ushort)csum; +} + +#endif // NT + + +//** SendIPPacket - Send an IP packet. +// +// Called when we have a filled in IP packet we need to send. Basically, we +// compute the xsum and send the thing. +// +// Entry: IF - Interface to send it on. +// FirstHop - First hop address to send it to. +// Packet - Packet to be sent. +// Buffer - Buffer to be sent. +// Header - Pointer to IP Header of packet. +// Options - Pointer to option buffer. +// OptionLength - Length of options. +// +// Returns: IP_STATUS of attempt to send. +IP_STATUS +SendIPPacket(Interface *IF, IPAddr FirstHop, PNDIS_PACKET Packet, + PNDIS_BUFFER Buffer, IPHeader *Header, uchar *Options, uint OptionSize) +{ + ulong csum; + NDIS_STATUS Status; + + + csum = xsum(Header, sizeof(IPHeader)); + if (Options) { // We have options, oh boy. + PNDIS_BUFFER OptBuffer; + PacketContext *pc = (PacketContext *)Packet->ProtocolReserved; + NdisAllocateBuffer(&Status, &OptBuffer, BufferPool, Options, OptionSize); + if (Status != NDIS_STATUS_SUCCESS) { // Couldn't get the needed + // option buffer. + CTEFreeMem(Options); + FreeIPPacket(Packet); + return IP_NO_RESOURCES; + } + pc->pc_common.pc_flags |= PACKET_FLAG_OPTIONS; + NdisChainBufferAtBack(Packet, OptBuffer); + csum += xsum(Options, OptionSize); + csum = (csum >> 16) + (csum & 0xffff); + csum += (csum >> 16); + } + Header->iph_xsum = ~(ushort)csum; + NdisChainBufferAtBack(Packet,Buffer); + + Status = (*(IF->if_xmit))(IF->if_lcontext, Packet, FirstHop, NULL); + + if (Status == NDIS_STATUS_PENDING) + return IP_PENDING; + + // Status wasn't pending. Free the packet, and map the status. + FreeIPPacket(Packet); + if (Status == NDIS_STATUS_SUCCESS) + return IP_SUCCESS; + else + return IP_HW_ERROR; +} + +//* SendDHCPPacket - Send a broadcast for DHCP. +// +// Called when somebody is sending a broadcast packet with a NULL source +// address. We assume this means they're sending a DHCP packet. We loop +// through the NTE table, and when we find an entry that's not valid we +// send out the interface associated with that entry. +// +// Input: Dest - Destination of packet. +// Packet - Packet to be send. +// Buffer - Buffer chain to be sent. +// Header - Pointer to header buffer being sent. +// +// Return: Status of send attempt. +// +IP_STATUS +SendDHCPPacket(IPAddr Dest, PNDIS_PACKET Packet, PNDIS_BUFFER Buffer, + IPHeader *IPH) +{ + if (DHCPNTE != NULL && ((DHCPNTE->nte_flags & (NTE_VALID | NTE_ACTIVE)) + == NTE_ACTIVE)) { + // The DHCP NTE is currently invalid, and active. Send on that + // interface. + return SendIPPacket(DHCPNTE->nte_if, Dest, Packet, Buffer, IPH, NULL, + 0); + } + + // Didn't find an invalid NTE! Free the resources, and return the failure. + FreeIPPacket(Packet); + IPSInfo.ipsi_outdiscards++; + return IP_DEST_HOST_UNREACHABLE; + +} + + +// +// Macros needed by IpCopyBuffer +// +#ifdef VXD + +#define NdisBufferLength(Buffer) (Buffer)->Length +#define NdisBufferVirtualAddress(Buffer) (Buffer)->VirtualAddress + +#else // VXD +#ifdef NT + +#define NdisBufferLength(Buffer) MmGetMdlByteCount(Buffer) +#define NdisBufferVirtualAddress(Buffer) MmGetSystemAddressForMdl(Buffer) + +#else // NT + +#error Need appropriate NDIS macros here + +#endif NT +#endif // VXD +//* IPCopyBuffer - Copy an NDIS buffer chain at a specific offset. +// +// This is the IP version of the function NdisCopyBuffer, which didn't +// get done properly in NDIS3. We take in an NDIS buffer chain, an offset, +// and a length, and produce a buffer chain describing that subset of the +// input buffer chain. +// +// This routine is not particularly efficient. Since only IPFragment uses +// it currently, it might be better to just incorporate this functionality +// directly into IPFragment. +// +// Input: OriginalBuffer - Original buffer chain to copy from. +// Offset - Offset from start to dup. +// Length - Length in bytes to dup. +// +// Returns: Pointer to new chain if we can make one, NULL if we can't. +// +PNDIS_BUFFER +IPCopyBuffer(PNDIS_BUFFER OriginalBuffer,uint Offset, uint Length) +{ + + PNDIS_BUFFER CurrentBuffer; // Pointer to current buffer. + PNDIS_BUFFER *NewBuffer; // Pointer to pointer to current new buffer. + PNDIS_BUFFER FirstBuffer; // First buffer in new chain. + UINT CopyLength; // Length of current copy. + NDIS_STATUS NewStatus; // Status of NdisAllocateBuffer operation. + + // First skip over the number of buffers we need to to reach Offset. + CurrentBuffer = OriginalBuffer; + + while (Offset >= NdisBufferLength(CurrentBuffer)) { + Offset -= NdisBufferLength(CurrentBuffer); + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + if (CurrentBuffer == (PNDIS_BUFFER)NULL) + return NULL; + } + + // Now CurrentBuffer is the buffer from which we start building the new chain, and + // Offset is the offset into CurrentBuffer from which to start. + FirstBuffer = NULL; + NewBuffer = &FirstBuffer; + + do { + + CopyLength = MIN( + Length, + NdisBufferLength(CurrentBuffer) - Offset + ); + NdisAllocateBuffer(&NewStatus, NewBuffer, BufferPool, + ((uchar *)NdisBufferVirtualAddress(CurrentBuffer)) + Offset, + CopyLength); + if (NewStatus != NDIS_STATUS_SUCCESS) + break; + + Offset = 0; // No offset from next buffer. + NewBuffer = &(NDIS_BUFFER_LINKAGE(*NewBuffer)); + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + Length -= CopyLength; + } while (Length != 0 && CurrentBuffer != (PNDIS_BUFFER)NULL); + + if (Length == 0) { // We succeeded + return FirstBuffer; + } else { // We exited the loop because of an error. + + // We need to free any allocated buffers, and return. + CurrentBuffer = FirstBuffer; + while (CurrentBuffer != (PNDIS_BUFFER)NULL) { + PNDIS_BUFFER Temp = CurrentBuffer; + CurrentBuffer = NDIS_BUFFER_LINKAGE(CurrentBuffer); + NdisFreeBuffer(Temp); + } + return NULL; + } +} + +//** IPFragment - Fragment and send an IP datagram. +// +// Called when an outgoing datagram is larger than the local MTU, and needs to be +// fragmented. This is a somewhat complicated operation. The caller gives us a +// prebuilt IP header, packet, and options. We use the header and packet on +// the last fragment of the send, as the passed in header already has the more +// fragments bit set correctly for the last fragment. +// +// The basic idea is to figure out the maximum size which we can send as a multiple +// of 8. Then, while we can send a maximum size fragment we'll allocate a header, packet, +// etc. and send it. At the end we'll send the final fragment using the provided header +// and packet. +// +// Entry: DestIF - Outbound interface of datagram. +// MTU - MTU to use in transmitting. +// FirstHop - First (or next) hop for this datagram. +// Packet - Packet to be sent. +// Header - Prebuilt IP header. +// Buffer - Buffer chain for data to be sent. +// DataSize - Size in bytes of data. +// Options - Pointer to option buffer, if any. +// OptionSize - Size in bytes of option buffer. +// SentCount - Pointer to where to return pending send count (may be NULL). +// +// Returns: IP_STATUS of send. +// +IP_STATUS +IPFragment(Interface *DestIF, uint MTU, IPAddr FirstHop, + PNDIS_PACKET Packet, IPHeader *Header, PNDIS_BUFFER Buffer, uint DataSize, + uchar *Options, uint OptionSize, int *SentCount) +{ + BufferReference *BR; // Buffer reference we'll use. + PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved; + PacketContext *CurrentContext; // Current Context in use. + uint MaxSend; // Maximum size (in bytes) we can send here. + uint PendingSends = 0; // Counter of how many pending sends we have. + PNDIS_BUFFER CurrentBuffer; // Current buffer to be sent. + PNDIS_PACKET CurrentPacket; // Current packet we're using. + IP_STATUS SendStatus; // Status of send command. + IPHeader *CurrentHeader; // Current header buffer we're using. + ushort Offset = 0; // Current offset into fragmented packet. + ushort StartOffset; // Starting offset of packet. + ushort RealOffset; // Offset of new fragment. + uint FragOptSize = 0; // Size (in bytes) of fragment options. + uchar FragmentOptions[MAX_OPT_SIZE]; // Master copy of options sent for fragments. + uchar Error = FALSE; // Set if we get an error in our main loop. + + MaxSend = (MTU - OptionSize) & ~7; // Determine max send size. + +#ifdef DEBUG + if (MaxSend >= DataSize) + DEBUGCHK; +#endif + + BR = PContext->pc_br; // Get the buffer reference we'll need. + +#ifdef DEBUG + if (!BR) + DEBUGCHK; +#endif + + if (Header->iph_offset & IP_DF_FLAG) { // Don't fragment flag set. + // Error out. + FreeIPPacket(Packet); + if (Options) + CTEFreeMem(Options); + if (SentCount == (int *)NULL) // No sent count is to be + // returned. + CTEFreeMem(BR); + IPSInfo.ipsi_fragfails++; + return IP_PACKET_TOO_BIG; + } + + StartOffset = Header->iph_offset & IP_OFFSET_MASK; + StartOffset = net_short(StartOffset) * 8; + + // If we have any options, copy the ones that need to be copied, and figure + // out the size of these new copied options. + + if (Options != (uchar *)NULL) { // We have options. + uchar *TempOptions = Options; + const uchar *EndOptions = (const uchar *)(Options+OptionSize); + + // Copy the options into the fragment options buffer. + CTEMemSet(FragmentOptions, IP_OPT_EOL, MAX_OPT_SIZE); + while ((TempOptions[IP_OPT_TYPE] != IP_OPT_EOL) && + (TempOptions < EndOptions)) { + if (TempOptions[IP_OPT_TYPE] & IP_OPT_COPIED) { // This option needs + // to be copied. + uint TempOptSize; + + TempOptSize = TempOptions[IP_OPT_LENGTH]; + CTEMemCopy(&FragmentOptions[FragOptSize], TempOptions, + TempOptSize); + FragOptSize += TempOptSize; + TempOptions += TempOptSize; + } else { // A non-copied option, just + // skip over it. + if (TempOptions[IP_OPT_TYPE] == IP_OPT_NOP) + TempOptions++; + else + TempOptions += TempOptions[IP_OPT_LENGTH]; + } + } + // Round the copied size up to a multiple of 4. + FragOptSize = ((FragOptSize & 3) ? ((FragOptSize & ~3) + 4) : FragOptSize); + } + + PContext->pc_common.pc_flags |= PACKET_FLAG_IPBUF; + + // Now, while we can build maximum size fragments, do so. + do { + if ((CurrentHeader = GetIPHeader(&CurrentPacket)) == (IPHeader *)NULL) { + // Couldn't get a buffer. Break out, since no point in sending others. + Error = TRUE; + break; + } + + // Copy the buffer into a new one, if we can. + CurrentBuffer = IPCopyBuffer(Buffer, Offset, MaxSend); + if (CurrentBuffer == NULL) { // No buffer, free resources and + // break. + FreeIPPacket(CurrentPacket); + Error = TRUE; + break; + } + + // Options for this send are set up when we get here, either from the + // entry from the loop, or from the allocation below. + + // We have all the pieces we need. Put the packet together and send it. + CurrentContext = (PacketContext *)CurrentPacket->ProtocolReserved; + *CurrentContext = *PContext; + *CurrentHeader = *Header; + CurrentContext->pc_common.pc_flags &= ~PACKET_FLAG_FW; + CurrentHeader->iph_verlen = IP_VERSION + + ((OptionSize + sizeof(IPHeader)) >> 2); + CurrentHeader->iph_length = net_short(MaxSend+OptionSize+sizeof(IPHeader)); + RealOffset = (StartOffset + Offset) >> 3; + CurrentHeader->iph_offset = net_short(RealOffset) | IP_MF_FLAG; + + SendStatus = SendIPPacket(DestIF, FirstHop, CurrentPacket, + CurrentBuffer, CurrentHeader, Options, OptionSize); + if (SendStatus == IP_PENDING) + PendingSends++; + + IPSInfo.ipsi_fragcreates++; + Offset += MaxSend; + DataSize -= MaxSend; + + // If we have any fragmented options, set up to use them next time. + if (FragOptSize) { + Options = CTEAllocMem(OptionSize = FragOptSize); + if (Options == (uchar *)NULL) { // Can't get an option + // buffer. + Error = TRUE; + break; + } + CTEMemCopy(Options, FragmentOptions, OptionSize); + } else { + Options = (uchar *)NULL; + OptionSize = 0; + } + } while (DataSize > MaxSend); + + // We've sent all of the previous fragments, now send the last one. We already + // have the packet and header buffer, as well as options if there are any - + // we need to copy the appropriate data. + if (!Error) { // Everything went OK above. + CurrentBuffer = IPCopyBuffer(Buffer, Offset, DataSize); + if (CurrentBuffer == NULL) { // No buffer, free resources + // and stop. + if (Options) + CTEFreeMem(Options); // Free the option buffer + FreeIPPacket(Packet); + IPSInfo.ipsi_outdiscards++; + } else { // Everything's OK, send it. + Header->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2); + Header->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader)); + RealOffset = (StartOffset + Offset) >> 3; + Header->iph_offset = net_short(RealOffset) | + (Header->iph_offset & IP_MF_FLAG); + SendStatus = SendIPPacket(DestIF, FirstHop, Packet, + CurrentBuffer, Header, Options, OptionSize); + if (SendStatus == IP_PENDING) + PendingSends++; + IPSInfo.ipsi_fragcreates++; + IPSInfo.ipsi_fragoks++; + } + } else { // We had some sort of error. + // Free resources. + FreeIPPacket(Packet); + if (Options) + CTEFreeMem(Options); + IPSInfo.ipsi_outdiscards++; + } + + + // Now, figure out what error code to return and whether or not we need to + // free the BufferReference. + + if (SentCount == (int *)NULL) { // No sent count is to be + // returned. + if (!ReferenceBuffer(BR, PendingSends)) { + CTEFreeMem(BR); + return IP_SUCCESS; + } + return IP_PENDING; + } else + *SentCount += PendingSends; + + return IP_PENDING; + +} + +//* UpdateRouteOption - Update a SR or RR options. +// +// Called by UpdateOptions when it needs to update a route option. +// +// Input: RTOption - Pointer to route option to be updated. +// Address - Address to update with. +// +// Returns: TRUE if we updated, FALSE if we didn't. +// +uchar +UpdateRouteOption(uchar *RTOption, IPAddr Address) +{ + uchar Pointer; // Pointer value of option. + + Pointer = RTOption[IP_OPT_PTR] - 1; + if (Pointer < RTOption[IP_OPT_LENGTH]) { + if ((RTOption[IP_OPT_LENGTH] - Pointer) < sizeof(IPAddr)) { + return FALSE; + } + *(IPAddr UNALIGNED *)&RTOption[Pointer] = Address; + RTOption[IP_OPT_PTR] += sizeof(IPAddr); + } + + return TRUE; + +} + +//* UpdateOptions - Update an options buffer. +// +// Called when we need to update an options buffer outgoing. We stamp the indicated +// options with our local address. +// +// Input: Options - Pointer to options buffer to be updated. +// Index - Pointer to information about which ones to update. +// Address - Local address with which to update the options. +// +// Returns: Index of option causing the error, or MAX_OPT_SIZE if all goes well. +// +uchar +UpdateOptions(uchar *Options, OptIndex *Index, IPAddr Address) +{ + uchar *LocalOption; + uchar LocalIndex; + + // If we have both options and an index, update the options. + if (Options != (uchar *)NULL && Index != (OptIndex *)NULL) { + + // If we have a source route to update, update it. If this fails return the index + // of the source route. + LocalIndex = Index->oi_srindex; + if (LocalIndex != MAX_OPT_SIZE) + if (!UpdateRouteOption(Options+LocalIndex, Address)) + return LocalIndex; + + // Do the same thing for any record route option. + LocalIndex = Index->oi_rrindex; + if (LocalIndex != MAX_OPT_SIZE) + if (!UpdateRouteOption(Options+LocalIndex, Address)) + return LocalIndex; + + // Now handle timestamp. + if ((LocalIndex = Index->oi_tsindex) != MAX_OPT_SIZE) { + uchar Flags, Length, Pointer; + + LocalOption = Options + LocalIndex; + Pointer = LocalOption[IP_OPT_PTR] - 1; + Flags = LocalOption[IP_TS_OVFLAGS] & IP_TS_FLMASK; + + // If we have room in the option, update it. + if (Pointer < (Length = LocalOption[IP_OPT_LENGTH])) { + ulong Now; + ulong UNALIGNED *TSPtr; + + // Get the current time as milliseconds from midnight GMT, mod the number + // of milliseconds in 24 hours. + Now = ((TimeStamp + CTESystemUpTime()) | TSFlag) % (24*3600*1000); + Now = net_long(Now); + TSPtr = (ulong UNALIGNED *)&LocalOption[Pointer]; + + switch (Flags) { + + // Just record the TS. If there is some room but not enough for an IP + // address we have an error. + case TS_REC_TS: + if ((Length - Pointer) < sizeof(IPAddr)) + return LocalIndex; // Error - not enough room. + *TSPtr = Now; + LocalOption[IP_OPT_PTR] += sizeof(ulong); + break; + + // Record only matching addresses. + case TS_REC_SPEC: + // If we're not the specified address, break out, else fall through + // to the record address case. + if (*(IPAddr UNALIGNED *)TSPtr != Address) + break; + + // Record an address and timestamp pair. If there is some room + // but not enough for the address/timestamp pait, we have an error, + // so bail out. + case TS_REC_ADDR: + if ((Length - Pointer) < (sizeof(IPAddr) + sizeof(ulong))) + return LocalIndex; // Not enough room. + *(IPAddr UNALIGNED *)TSPtr = Address; // Store the address. + TSPtr++; // Update to where to put TS. + *TSPtr = Now; // Store TS + LocalOption[IP_OPT_PTR] += (sizeof(ulong) + sizeof(IPAddr)); + break; + default: // Unknown flag type. Just ignore it. + break; + } + } else { // Have overflow. + // We have an overflow. If the overflow field isn't maxed, increment it. If + // it is maxed we have an error. + if ((LocalOption[IP_TS_OVFLAGS] & IP_TS_OVMASK) != IP_TS_MAXOV) // Not maxed. + LocalOption[IP_TS_OVFLAGS] += IP_TS_INC; // So increment it. + else + return LocalIndex; // Would have overflowed. + } + } + } + return MAX_OPT_SIZE; +} + + +typedef struct { + IPAddr bsl_addr; + Interface *bsl_if; + uint bsl_mtu; + ushort bsl_flags; +} BCastSendList; + +//** SendIPBcast - Send a local BCast IP packet. +// +// This routine is called when we need to send a bcast packet. This may +// involve sending on multiple interfaces. We figure out which interfaces +// to send on, then loop through sending on them. +// +// Some care is needed to avoid sending the packet onto the same physical media +// multiple times. What we do is loop through the NTE table, deciding in we +// should send on the interface. As we go through we build up a list of +// interfaces to send on. Then we loop through this list, sending on each +// interface. This is a little cumbersome, but allows us to localize the +// decision on where to send datagrams into one spot. If SendOnSource is FALSE +// coming in we assume we've already sent on the specified source NTE and +// initialize data structures accordingly. This feature is used in routing +// datagrams. +// +// Entry: SrcNTE - NTE for source of send (unused if SendOnSource == TRUE). +// Destination - Destination address +// Packet - Prebuilt packet to broadcast. +// IPH - Pointer to header buffer +// Buffer - Buffer of data to be sent. +// DataSize - Size of data to be sent. +// Options - Pointer to options buffer. +// OptionSize - Size in bytes of options. +// SendOnSource - Indicator of whether or not this should be sent on the source net. +// Index - Pointer to opt index array; may be NULL; +// +// Returns: Status of attempt to send. +// +IP_STATUS +SendIPBCast(NetTableEntry *SrcNTE, IPAddr Destination, PNDIS_PACKET Packet, + IPHeader *IPH, PNDIS_BUFFER Buffer, uint DataSize, uchar *Options, + uint OptionSize, uchar SendOnSource, OptIndex *Index) +{ + BufferReference *BR; // Buffer reference to use for this + // buffer. + PacketContext *PContext = (PacketContext *)Packet->ProtocolReserved; + NetTableEntry *TempNTE; + uint i, j; + uint NeedFragment; // TRUE if we think we'll need to + // fragment. + int Sent = 0; // Count of how many we've sent. + IP_STATUS Status; + uchar *NewOptions; // Options we'll use on each send. + IPHeader *NewHeader; + PNDIS_BUFFER NewUserBuffer; + PNDIS_PACKET NewPacket; + BCastSendList *SendList; + uint NetsToSend; + IPAddr SrcAddr; + Interface *SrcIF; + IPHeader *Temp; + FORWARD_ACTION Action; + + + SendList = CTEAllocMem(sizeof(BCastSendList) * NumNTE); + + if (SendList == NULL) { + return(IP_NO_RESOURCES); + } + + CTEMemSet(SendList, 0, sizeof(BCastSendList) * NumNTE); + + // If SendOnSource, initalize SrcAddr and SrcIF to be non-matching. + // Otherwise initialize them to the masked source address and source + // interface. + if (SendOnSource) { + SrcAddr = NULL_IP_ADDR; + SrcIF = NULL; + } else { + CTEAssert(SrcNTE != NULL); + SrcAddr = (SrcNTE->nte_addr & SrcNTE->nte_mask); + SrcIF = SrcNTE->nte_if; + } + + + NeedFragment = FALSE; + // Loop through the NTE table, making a list of interfaces and + // corresponding addresses to send on. + for (NetsToSend = 0, TempNTE = NetTableList; TempNTE != NULL; + TempNTE = TempNTE->nte_next) { + IPAddr TempAddr; + + // Don't send through invalid or the loopback NTE. + if (!(TempNTE->nte_flags & NTE_VALID) || TempNTE == LoopNTE) + continue; + + TempAddr = TempNTE->nte_addr & TempNTE->nte_mask; + + // If he matches the source address or SrcIF, skip him. + if (IP_ADDR_EQUAL(TempAddr, SrcAddr) || TempNTE->nte_if == SrcIF) + continue; + + // If the destination isn't a broadcast on this NTE, skip him. + if (!IS_BCAST_DEST(IsBCastOnNTE(Destination, TempNTE))) + continue; + + // if this NTE is P2P then always add him to bcast list. + if ((TempNTE->nte_if)->if_flags & IF_FLAGS_P2P) { + j = NetsToSend ; + } else { + // Go through the list we've already build, looking for a match. + for (j = 0; j < NetsToSend; j++) { + + // if P2P NTE then skip it - we want to send bcasts to all P2P interfaces in + // addition to 1 non P2P interface even if they are on the same subnet. + if ((SendList[j].bsl_if)->if_flags & IF_FLAGS_P2P) + continue ; + + if (IP_ADDR_EQUAL(SendList[j].bsl_addr & TempNTE->nte_mask, TempAddr) + || SendList[j].bsl_if == TempNTE->nte_if) { + + // He matches this send list element. Shrink the MSS if + // we need to, and then break out. + SendList[j].bsl_mtu = MIN(SendList[j].bsl_mtu, TempNTE->nte_mss); + if ((DataSize + OptionSize) > SendList[j].bsl_mtu) + NeedFragment = TRUE; + break; + } + } + } + + if (j == NetsToSend) { + // This is a new one. Fill him in, and bump NetsToSend. + + SendList[j].bsl_addr = TempNTE->nte_addr; + SendList[j].bsl_if = TempNTE->nte_if; + SendList[j].bsl_mtu = TempNTE->nte_mss; + SendList[j].bsl_flags = TempNTE->nte_flags; + if ((DataSize + OptionSize) > SendList[j].bsl_mtu) + NeedFragment = TRUE; + NetsToSend++; + } + + } + + if (NetsToSend == 0) { + CTEFreeMem(SendList); + return IP_SUCCESS; // Nothing to send on. + } + + // OK, we've got the list. If we've got more than one interface to send + // on or we need to fragment, get a BufferReference. + if (NetsToSend > 1 || NeedFragment) { + if ((BR = CTEAllocMem(sizeof(BufferReference))) == + (BufferReference *)NULL) { + CTEFreeMem(SendList); + return IP_NO_RESOURCES; + } + + BR->br_buffer = Buffer; + BR->br_refcount = 0; + CTEInitLock(&BR->br_lock); + PContext->pc_br = BR; + } else { + BR = NULL; + PContext->pc_br = NULL; + } + + // + // We need to pass up the options and IP hdr in a contiguous buffer. + // Allocate the buffer once and re-use later. + // + if (ForwardFilterPtr != NULL) { + if (Options == NULL) { +#if FWD_DBG + DbgPrint("Options==NULL\n"); +#endif + Temp = IPH; + } else { + Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize); + if (Temp == NULL) { + CTEFreeMem(SendList); + return IP_NO_RESOURCES; + } + + *Temp = *IPH; +#if FWD_DBG + DbgPrint("Options!=NULL : alloced temp @ %lx\n", Temp); +#endif + + // + // done later... + // CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize); + } + } + + // Now, loop through the list. For each entry, send. + + for (i = 0; i < NetsToSend; i++) { + + // For all nets except the last one we're going to send on we need + // to make a copy of the header, packet, buffers, and any options. + // On the last net we'll use the user provided information. + + if (i != (NetsToSend - 1)) { + if ((NewHeader = GetIPHeader(&NewPacket)) == (IPHeader *)NULL) { + IPSInfo.ipsi_outdiscards++; + continue; // Couldn't get a header, skip this + // send. + } + + NewUserBuffer = IPCopyBuffer(Buffer, 0, DataSize); + if (NewUserBuffer == NULL) { // Couldn't get user buffer + // copied. + FreeIPPacket(NewPacket); + IPSInfo.ipsi_outdiscards++; + continue; + } + + *(PacketContext *)NewPacket->ProtocolReserved = *PContext; + *NewHeader = *IPH; + (*(PacketContext*)NewPacket->ProtocolReserved).pc_common.pc_flags |= PACKET_FLAG_IPBUF; + (*(PacketContext*)NewPacket->ProtocolReserved).pc_common.pc_flags &= ~PACKET_FLAG_FW; + if (Options) { + // We have options, make a copy. + if ((NewOptions = CTEAllocMem(OptionSize)) == (uchar *)NULL) { + FreeIPBufferChain(NewUserBuffer); + FreeIPPacket(NewPacket); + IPSInfo.ipsi_outdiscards++; + continue; + } + CTEMemCopy(NewOptions, Options, OptionSize); + } + else { + NewOptions = NULL; + } + } else { + NewHeader = IPH; + NewPacket = Packet; + NewOptions = Options; + NewUserBuffer = Buffer; + } + + UpdateOptions(NewOptions, Index, SendList[i].bsl_addr); + + // See if we need to filter this packet. If we + // do, call the filter routine to see if it's + // OK to send it. + if (ForwardFilterPtr != NULL) { + // + // Copy over the options. + // + if (NewOptions) { + CTEMemCopy((uchar *)(Temp + 1), NewOptions, OptionSize); + } + + Action = (*ForwardFilterPtr)(Temp, + NdisBufferVirtualAddress(NewUserBuffer), + NdisBufferLength(NewUserBuffer), + NULL, SendList[i].bsl_if->if_filtercontext); + +#if FWD_DBG + DbgPrint("ForwardFilterPtr: %lx, FORWARD is %lx\n", Action, FORWARD); +#endif + + if (Action != FORWARD) { + if (i != (NetsToSend - 1)) { + FreeIPBufferChain(NewUserBuffer); + if (NewOptions) { + CTEFreeMem(NewOptions); + } + } + continue; + } + } + + if ((DataSize + OptionSize) > SendList[i].bsl_mtu) {// This is too big + // Don't need to update Sent when fragmenting, as IPFragment + // will update the br_refcount field itself. It will also free + // the option buffer. + Status = IPFragment(SendList[i].bsl_if, SendList[i].bsl_mtu, + Destination, NewPacket, NewHeader,NewUserBuffer, DataSize, + NewOptions, OptionSize, &Sent); + + // IPFragment is done with the descriptor chain, so if this is + // a locally allocated chain free it now. + if (i != (NetsToSend - 1)) + FreeIPBufferChain(NewUserBuffer); + } + else { + Status = SendIPPacket(SendList[i].bsl_if, Destination, NewPacket, + NewUserBuffer, NewHeader, NewOptions, OptionSize); + if (Status == IP_PENDING) + Sent++; + } + } + + if (ForwardFilterPtr && Options) { + CTEFreeMem(Temp); + } + + // Alright, we've sent everything we need to. We'll adjust the reference count + // by the number we've sent. IPFragment may also have put some references + // on it. If the reference count goes to 0, we're done and we'll free the + // BufferReference structure. + + if (BR != NULL) { + if (!ReferenceBuffer(BR, Sent)) { + CTEFreeMem(SendList); + CTEFreeMem(BR); // Reference is 0, free the BR structure. + return IP_SUCCESS; + } else { + CTEFreeMem(SendList); + return IP_PENDING; + } + } else { + // Had only one I/F to send on. Just return the status. + CTEFreeMem(SendList); + return Status; + } + +} + +//** IPTransmit - Transmit a packet. +// +// This is the main transmit routine called by the upper layer. Conceptually, +// we process any options, look up the route to the destination, fragment the +// packet if needed, and send it. In reality, we use an RCE to cache the best +// route, and we have special case code here for dealing with the common +// case of no options, with everything fitting into one buffer. +// +// Entry: Context - Pointer to ProtInfo struc for protocol. +// SendContext - User provided send context, passed back on send cmplt. +// Protocol - Protocol field for packet. +// Buffer - NDIS_BUFFER chain of data to be sent. +// DataSize - Size in bytes of data to be sent. +// OptInfo - Pointer to optinfo structure. +// Dest - Destination to send to. +// Source - Source address to use. +// RCE - Pointer to an RCE structure that caches info. about path. +// +// Returns: Status of transmit command. +// +IP_STATUS +IPTransmit(void *Context, void *SendContext, PNDIS_BUFFER Buffer, uint DataSize, + IPAddr Dest, IPAddr Source, IPOptInfo *OptInfo, RouteCacheEntry *RCE, + uchar Protocol) +{ + ProtInfo *PInfo = (ProtInfo *)Context; + PacketContext *pc; + Interface *DestIF; // Outgoing interface to use. + IPAddr FirstHop; // First hop address of + // destination. + uint MTU; // MTU of route. + NDIS_STATUS Status; + IPHeader *IPH; + PNDIS_PACKET Packet; + PNDIS_BUFFER HeaderBuffer; + CTELockHandle LockHandle; + uchar *Options; + uint OptionSize; + BufferReference *BR; + RouteTableEntry *RTE; + uchar DType; + IP_STATUS SendStatus; +#ifdef _PNP_POWER + Interface *RoutedIF; +#endif + + IPSInfo.ipsi_outrequests++; + + // Allocate a packet that we need for all cases, and fill + // in the common stuff. If everything goes well, we'll send it + // here. Otherwise we'll break out into special case code for + // broadcasts, fragments, etc. + if ((Packet = GetIPPacket()) != (PNDIS_PACKET)NULL) { // Got a packet. + pc = (PacketContext *)Packet->ProtocolReserved; + pc->pc_br = (BufferReference *)NULL; + pc->pc_pi = PInfo; + pc->pc_context = SendContext; +#ifdef _PNP_POWER + CTEAssert(pc->pc_if == NULL); +#endif + + // Make sure that we have an RCE, that it's valid, etc. + + if (RCE != NULL) { + // We have an RCE. Make sure it's valid. + CTEGetLock(&RCE->rce_lock, &LockHandle); + if (RCE->rce_flags == RCE_ALL_VALID) { + + // The RTE is valid. + CTEInterlockedIncrementLong(&RCE->rce_usecnt); + RTE = RCE->rce_rte; + FirstHop = ADDR_FROM_RTE(RTE, Dest); + DestIF = IF_FROM_RTE(RTE); + MTU = MTU_FROM_RTE(RTE); + + CTEFreeLock(&RCE->rce_lock, LockHandle); + + // Check that we have no options, this isn't a broadcast, and + // that everything will fit into one link level MTU. If this + // is the case, we'll send it in a hurry. + if (OptInfo->ioi_options == (uchar *)NULL) { + if (RCE->rce_dtype != DEST_BCAST) { + if (DataSize <= MTU) { + + + NdisBufferLength(Buffer) += sizeof(IPHeader); + NdisChainBufferAtBack(Packet, Buffer); + IPH = (IPHeader *)NdisBufferVirtualAddress(Buffer); + + IPH->iph_protocol = Protocol; + IPH->iph_xsum = 0; + IPH->iph_dest = Dest; + IPH->iph_src = Source; + IPH->iph_ttl = OptInfo->ioi_ttl; + IPH->iph_tos = OptInfo->ioi_tos; + IPH->iph_offset = + net_short(((OptInfo->ioi_flags & IP_FLAG_DF) + << 13)); + IPH->iph_id = + (ushort)CTEInterlockedExchangeAdd(&IPID, 1); + IPH->iph_verlen = DEFAULT_VERLEN; + IPH->iph_length = net_short(DataSize+sizeof(IPHeader)); + IPH->iph_xsum = ~xsum(IPH, sizeof(IPHeader)); + + // See if we need to filter this packet. If we + // do, call the filter routine to see if it's + // OK to send it. + + if (ForwardFilterPtr == NULL) { + Status = (*(DestIF->if_xmit))(DestIF->if_lcontext, + Packet, FirstHop, RCE); + + CTEInterlockedDecrementLong(&RCE->rce_usecnt); + + if (Status != NDIS_STATUS_PENDING) { + FreeIPPacket(Packet); + return IP_SUCCESS; // BUGBUG - should map error + // code. + } + return IP_PENDING; + + } else { + FORWARD_ACTION Action; + + Action = (*ForwardFilterPtr)(IPH, + (uchar *)(IPH + 1), + NdisBufferLength(Buffer) - + sizeof(IPHeader), + NULL, DestIF->if_filtercontext); + + if (Action == FORWARD) { + Status = (*(DestIF->if_xmit))( + DestIF->if_lcontext, + Packet, FirstHop, RCE); + } else { + Status = NDIS_STATUS_SUCCESS; + IPSInfo.ipsi_outdiscards++; + } + + CTEInterlockedDecrementLong(&RCE->rce_usecnt); + + if (Status != NDIS_STATUS_PENDING) { + FreeIPPacket(Packet); + return IP_SUCCESS; // BUGBUG - should map error + // code. + } + return IP_PENDING; + } + } + } + } + CTEInterlockedDecrementLong(&RCE->rce_usecnt); + DType = RCE->rce_dtype; + } else { + // We have an RCE, but there is no RTE for it. Call the + // routing code to fix this. + CTEFreeLock(&RCE->rce_lock, LockHandle); + if (!AttachRCEToRTE(RCE, PInfo->pi_protocol, + (uchar *)NdisBufferVirtualAddress(Buffer) + sizeof(IPHeader), + NdisBufferLength(Buffer))) { + IPSInfo.ipsi_outnoroutes++; + FreeIPPacket(Packet); + return IP_DEST_HOST_UNREACHABLE; + } + + // See if the RCE is now valid. + CTEGetLock(&RCE->rce_lock, &LockHandle); + if (RCE->rce_flags == RCE_ALL_VALID) { + + // The RCE is now valid, so use his info. + RTE = RCE->rce_rte; + FirstHop = ADDR_FROM_RTE(RTE, Dest); + DestIF = IF_FROM_RTE(RTE); + MTU = MTU_FROM_RTE(RTE); + DType = RCE->rce_dtype; + } else + FirstHop = NULL_IP_ADDR; + CTEFreeLock(&RCE->rce_lock, LockHandle); + } + } else { + // We had no RCE, so we'll have to look it up the hard way. + FirstHop = NULL_IP_ADDR; + } + + // We bailed out of the fast path for some reason. Allocate a header + // buffer, and copy the data in the first buffer forward. Then figure + // out why we're off the fast path, and deal with it. If we don't have + // the next hop info, look it up now. + + HeaderBuffer = GetIPHdrBuffer(); + if (HeaderBuffer == NULL) { + FreeIPPacket(Packet); + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } else { + uchar *Temp1, *Temp2; + + // Got a buffer, copy the upper layer data forward. + + Temp1 = (uchar *)NdisBufferVirtualAddress(Buffer); + Temp2 = Temp1 + sizeof(IPHeader); + CTEMemCopy(Temp1, Temp2, NdisBufferLength(Buffer)); + } + + NdisChainBufferAtBack(Packet, HeaderBuffer); + + IPH = (IPHeader *)NdisBufferVirtualAddress(HeaderBuffer); + IPH->iph_protocol = Protocol; + IPH->iph_xsum = 0; + IPH->iph_src = Source; + IPH->iph_ttl = OptInfo->ioi_ttl; + IPH->iph_tos = OptInfo->ioi_tos; + IPH->iph_offset = net_short(((OptInfo->ioi_flags & IP_FLAG_DF) << 13)); + IPH->iph_id = (ushort)CTEInterlockedExchangeAdd(&IPID, 1); + pc = (PacketContext *)Packet->ProtocolReserved; + pc->pc_common.pc_flags |= PACKET_FLAG_IPHDR; + + if (IP_ADDR_EQUAL(OptInfo->ioi_addr, NULL_IP_ADDR)) { + IPH->iph_dest = Dest; + } + else { + // + // We have a source route, so we need to redo the + // destination and first hop information. + // + Dest = OptInfo->ioi_addr; + IPH->iph_dest = Dest; + + if (RCE != NULL) { + // We have an RCE. Make sure it's valid. + CTEGetLock(&RCE->rce_lock, &LockHandle); + + if (RCE->rce_flags == RCE_ALL_VALID) { + + // The RTE is valid. + RTE = RCE->rce_rte; + FirstHop = ADDR_FROM_RTE(RTE, Dest); + DestIF = IF_FROM_RTE(RTE); + MTU = MTU_FROM_RTE(RTE); + } + else { + FirstHop = NULL_IP_ADDR; + } + + CTEFreeLock(&RCE->rce_lock, LockHandle); + } + } + + if (IP_ADDR_EQUAL(FirstHop, NULL_IP_ADDR)) { + DestIF = LookupNextHopWithBuffer(Dest, Source, &FirstHop, &MTU, + PInfo->pi_protocol, (uchar *)NdisBufferVirtualAddress(Buffer), + NdisBufferLength(Buffer)); +#ifdef _PNP_POWER + pc->pc_if = DestIF; + RoutedIF = DestIF; +#endif + if (DestIF == NULL) { + // Lookup failed. Return an error. + FreeIPPacket(Packet); + IPSInfo.ipsi_outnoroutes++; + return IP_DEST_HOST_UNREACHABLE; + } + + DType = GetAddrType(Dest); +#ifdef DEBUG + if (DType == DEST_INVALID) + DEBUGCHK; +#endif + } else { +#ifdef _PNP_POWER + RoutedIF = NULL; +#endif + } + + // See if we have any options. If we do, copy them now. + if (OptInfo->ioi_options != NULL) { + // If we have a SSRR, make sure that we're sending straight to the + // first hop. + if (OptInfo->ioi_flags & IP_FLAG_SSRR) { + if (!IP_ADDR_EQUAL(Dest, FirstHop)) { + FreeIPPacket(Packet); +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + IPSInfo.ipsi_outnoroutes++; + return IP_DEST_HOST_UNREACHABLE; + } + } + Options = CTEAllocMem(OptionSize = OptInfo->ioi_optlength); + if (Options == (uchar *)NULL) { + FreeIPPacket(Packet); +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } + CTEMemCopy(Options, OptInfo->ioi_options, OptionSize); + } else { + Options = (uchar *)NULL; + OptionSize = 0; + } + + // The options have been taken care of. Now see if it's some sort + // of broadcast. + IPH->iph_verlen = IP_VERSION + ((OptionSize + sizeof(IPHeader)) >> 2); + IPH->iph_length = net_short(DataSize+OptionSize+sizeof(IPHeader)); + + // See if we need to filter this packet. If we + // do, call the filter routine to see if it's + // OK to send it. + + if (ForwardFilterPtr != NULL) { + IPHeader *Temp; + FORWARD_ACTION Action; + + if (Options == NULL) { + Temp = IPH; + } else { + Temp = CTEAllocMem(sizeof(IPHeader) + OptionSize); + if (Temp == NULL) { + FreeIPPacket(Packet); +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + CTEFreeMem(Options); + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } + + *Temp = *IPH; + CTEMemCopy((uchar *)(Temp + 1), Options, OptionSize); + } + + Action = (*ForwardFilterPtr)(Temp, + NdisBufferVirtualAddress(Buffer), + NdisBufferLength(Buffer), + NULL, DestIF->if_filtercontext); + + if (Options != NULL) { + CTEFreeMem(Temp); + } + + if (Action != FORWARD) { + // + // If this is a bcast pkt, dont fail the send here since we might send this + // pkt over some other NTE; instead, let SendIPBCast deal with the Filtering + // for broadcast pkts. + // + // NOTE: We shd actually not call into ForwardFilterPtr here at all since we + // deal with it in BCast, but we do so in order to avoid a check above and hence + // take a double call hit in the bcast case. + // + if (DType != DEST_BCAST) { + + if (Options) + CTEFreeMem(Options); + FreeIPPacket(Packet); + +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + IPSInfo.ipsi_outdiscards++; + return IP_DEST_HOST_UNREACHABLE; + } +#if FWD_DBG + else { + DbgPrint("IPTransmit: ignoring return %lx\n", Action); + } +#endif + + } + } + + // If this is a broadcast address, call our broadcast send handler + // to deal with this. The broadcast address handler will free the + // option buffer for us, if needed. Otherwise if it's a fragment, call + // the fragmentation handler. + if (DType == DEST_BCAST) { + if (IP_ADDR_EQUAL(Source, NULL_IP_ADDR)) { + SendStatus = SendDHCPPacket(Dest, Packet, Buffer, IPH); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } else { + SendStatus= SendIPBCast(NULL, Dest, Packet, IPH, Buffer, DataSize, + Options, OptionSize, TRUE, NULL); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } + } + + // Not a broadcast. If it needs to be fragmented, call our + // fragmenter to do it. The fragmentation routine needs a + // BufferReference structure, so we'll need one of those first. + if ((DataSize + OptionSize) > MTU) { + BR = CTEAllocMem(sizeof(BufferReference)); + if (BR == (BufferReference *)NULL) { + // Couldn't get a BufferReference + if (Options) + CTEFreeMem(Options); + FreeIPPacket(Packet); + +#ifdef _PNP_POWER + if (RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; + } + BR->br_buffer = Buffer; + BR->br_refcount = 0; + CTEInitLock(&BR->br_lock); + pc->pc_br = BR; + SendStatus = IPFragment(DestIF, MTU, FirstHop, Packet, IPH, Buffer, + DataSize, Options, OptionSize, (int *)NULL); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } + + // If we've reached here, we aren't sending a broadcast and don't need to + // fragment anything. Presumably we got here because we have options. + // In any case, we're ready now. + + SendStatus = SendIPPacket(DestIF, FirstHop, Packet, Buffer, IPH, Options, + OptionSize); + +#ifdef _PNP_POWER + if (SendStatus != IP_PENDING && RoutedIF != NULL) { + DerefIF(RoutedIF); + } +#endif + + return SendStatus; + } + + // Couldn't get a buffer. Return 'no resources' + IPSInfo.ipsi_outdiscards++; + return IP_NO_RESOURCES; +} + + + diff --git a/private/ntos/tdi/tcpip/ip/ipxmit.h b/private/ntos/tdi/tcpip/ip/ipxmit.h new file mode 100644 index 000000000..65842967e --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ipxmit.h @@ -0,0 +1,34 @@ +/********************************************************************/ +/** Microsoft LAN Manager **/ +/** Copyright(c) Microsoft Corp., 1990-1992 **/ +/********************************************************************/ +/* :ts=4 */ + +//** IPXMIT.H - IP transmit definitions. +// +// This file contains all of the definitions for the transmit code visible +// to modules outside IPXMIT.C +extern IP_STATUS SendIPPacket(Interface *IF, IPAddr FirstHop, + PNDIS_PACKET Packet, PNDIS_BUFFER Buffer, + IPHeader *Header, uchar *Options, + uint OptionSize); +extern IP_STATUS IPFragment(Interface *DestIF, uint MTU, + IPAddr FirstHop, PNDIS_PACKET Packet, + IPHeader *Header, PNDIS_BUFFER Buffer, + uint DataSize, uchar *Options, + uint OptionSize, int *SentCount); +extern uchar UpdateOptions(uchar *Options, OptIndex *Index, + IPAddr Address); +extern IP_STATUS SendIPBCast(NetTableEntry *SrcNTE, IPAddr Destination, + PNDIS_PACKET Packet, IPHeader *IPH, + PNDIS_BUFFER Buffer, uint DataSize, + uchar *Options, uint OptionSize, + uchar SendOnSource, OptIndex *Index); +extern IP_STATUS IPTransmit(void *Context, void *SendContext, + PNDIS_BUFFER Buffer, uint DataSize, + IPAddr Dest, IPAddr Source, + IPOptInfo *OptInfo, RouteCacheEntry *RCE, + uchar Protocol); + + + diff --git a/private/ntos/tdi/tcpip/ip/mp/makefile b/private/ntos/tdi/tcpip/ip/mp/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/mp/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/tcpip/ip/mp/sources b/private/ntos/tdi/tcpip/ip/mp/sources new file mode 100644 index 000000000..301687c9a --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/mp/sources @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +!include ..\sources.inc diff --git a/private/ntos/tdi/tcpip/ip/ntip.c b/private/ntos/tdi/tcpip/ip/ntip.c new file mode 100644 index 000000000..040f025e3 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ntip.c @@ -0,0 +1,3361 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntip.c + +Abstract: + + NT specific routines for loading and configuring the IP driver. + +Author: + + Mike Massa (mikemas) Aug 13, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-13-93 created + +Notes: + +--*/ + +#define _CTYPE_DISABLE_MACROS + +#include +#include +#include +#include +#include "ipdef.h" +#include "ipinit.h" +#include +#include +#include + +// +// Debugging macros +// +#if DBG + +#define TCPTRACE(many_args) DbgPrint many_args + +#else // DBG + +#define TCPTRACE(many_args) DbgPrint many_args + +#endif // DBG + + +// +// definitions needed by inet_addr. +// +#define INADDR_NONE 0xffffffff +#define INADDR_ANY 0 +#define htonl(x) net_long(x) + +// +// Other local constants +// +#define WORK_BUFFER_SIZE 256 + +// +// Configuration defaults +// +#define DEFAULT_IGMP_LEVEL 2 +#define DEFAULT_IP_NETS 8 + + +// +// Local types +// +typedef struct _PerNetConfigInfo { + uint UseZeroBroadcast; + uint Mtu; + uint NumberOfGateways; + uint MaxForwardPending; // max routing packets pending +} PER_NET_CONFIG_INFO, *PPER_NET_CONFIG_INFO; + + +// +// Global variables. +// +PDRIVER_OBJECT IPDriverObject; +PDEVICE_OBJECT IPDeviceObject; +IPConfigInfo *IPConfiguration; +uint ArpUseEtherSnap = FALSE; +uint ArpAlwaysSourceRoute = FALSE; +uint IPAlwaysSourceRoute = TRUE; +uint ArpCacheLife = DEFAULT_ARP_CACHE_LIFE; +PWCHAR TempAdapterName; + +#ifndef _PNP_POWER + +NameMapping *AdptNameTable; +DriverRegMapping *DriverNameTable; +uint NumRegDrivers = 0; +uint NetConfigSize = DEFAULT_IP_NETS; + +#endif // _PNP_POWER + +// Used in the conversion of 100ns times to milliseconds. +static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; + + +// +// External variables +// +extern LIST_ENTRY PendingEchoList; // def needed for initialization +extern LIST_ENTRY PendingIPSetNTEAddrList; // def needed for initialization +extern IPSNMPInfo IPSInfo; +EXTERNAL_LOCK(RouteTableLock) + +// +// Macros +// + +//++ +// +// LARGE_INTEGER +// CTEConvertMillisecondsTo100ns( +// IN LARGE_INTEGER MsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// MsTime - Time in milliseconds. +// +// Return Value: +// +// Time in hundreds of nanoseconds. +// +//-- + +#define CTEConvertMillisecondsTo100ns(MsTime) \ + RtlExtendedIntegerMultiply(MsTime, 10000) + + +//++ +// +// LARGE_INTEGER +// CTEConvert100nsToMilliseconds( +// IN LARGE_INTEGER HnsTime +// ); +// +// Routine Description: +// +// Converts time expressed in hundreds of nanoseconds to milliseconds. +// +// Arguments: +// +// HnsTime - Time in hundreds of nanoseconds. +// +// Return Value: +// +// Time in milliseconds. +// +//-- + +#define SHIFT10000 13 +extern LARGE_INTEGER Magic10000; + +#define CTEConvert100nsToMilliseconds(HnsTime) \ + RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) + + +// +// External function prototypes +// +extern int +IPInit( + void + ); + +long +IPSetInfo( + TDIObjectID *ID, + void *Buffer, + uint Size + ); + +NTSTATUS +IPDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +OpenRegKey( + PHANDLE HandlePtr, + PWCHAR KeyName + ); + +NTSTATUS +GetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +GetRegSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData, + PULONG ValueType + ); + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ); + +NTSTATUS +InitRegDWORDParameter( + HANDLE RegKey, + PWCHAR ValueName, + ULONG *Value, + ULONG DefaultValue + ); + +uint +RTReadNext( + void *Context, + void *Buffer + ); + +uint +RTValidateContext( + void *Context, + uint *Valid + ); + +// +// Local funcion prototypes +// +NTSTATUS +IPDriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +IPProcessConfiguration( + VOID + ); + +NTSTATUS +IPProcessAdapterSection( + WCHAR *DeviceName, + WCHAR *AdapterName + ); + +#ifndef _PNP_POWER + +NTSTATUS +IPProcessIPAddressList( + HANDLE AdapterKey, + WCHAR *DeviceName, + WCHAR *AdapterName, + WCHAR *IpAddressList, + WCHAR *SubnetMaskList, + NDIS_STRING *LowerInterfaceString, + uint LowerInterfaceType, + PPER_NET_CONFIG_INFO PerNetConfigInfo + ); + +#else // _PNP_POWER + +uint +GetGeneralIFConfig( + IFGeneralConfig *ConfigInfo, + NDIS_HANDLE Handle + ); + +int +IsLLInterfaceValueNull( + NDIS_HANDLE Handle + ); + +IFAddrList * +GetIFAddrList( + UINT *NumAddr, + NDIS_HANDLE Handle + ); + +#endif // _PNP_POWER + +UINT +OpenIFConfig( + PNDIS_STRING ConfigName, + NDIS_HANDLE *Handle + ); + +VOID +CloseIFConfig( + NDIS_HANDLE Handle + ); + +IPConfigInfo * +IPGetConfig( + void + ); + +void +IPFreeConfig( + IPConfigInfo *ConfigInfo + ); + +ulong +GetGMTDelta( + void + ); + +ulong +GetTime( + void + ); + +BOOLEAN +IPConvertStringToAddress( + IN PWCHAR AddressString, + OUT PULONG IpAddress + ); + +uint +UseEtherSNAP( + PNDIS_STRING Name + ); + +void +GetAlwaysSourceRoute( + uint *pArpAlwaysSourceRoute, + uint *pIPAlwaysSourceRoute + ); + +uint +GetArpCacheLife( + void + ); + +ULONG +RouteMatch( + IN WCHAR *RouteString, + IN IPAddr Address, + IN IPMask Mask, + OUT IPAddr *DestVal, + OUT IPMask *DestMask, + OUT IPAddr *GateVal, + OUT ULONG *Metric + ); + +VOID +SetPersistentRoutesForNTE( + IPAddr Address, + IPMask Mask, + ULONG IFIndex + ); + +ULONG +GetCurrentRouteTable( + IPRouteEntry **ppRouteTable + ); + + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, IPDriverEntry) +#pragma alloc_text(INIT, IPProcessConfiguration) +#pragma alloc_text(INIT, IPProcessAdapterSection) +#pragma alloc_text(INIT, IPGetConfig) +#pragma alloc_text(INIT, IPFreeConfig) +#pragma alloc_text(INIT, GetGMTDelta) +#pragma alloc_text(INIT, GetTime) + +#ifndef _PNP_POWER + +#pragma alloc_text(INIT, IPProcessIPAddressList) +#pragma alloc_text(INIT, UseEtherSNAP) +#pragma alloc_text(INIT, GetAlwaysSourceRoute) +#pragma alloc_text(INIT, GetArpCacheLife) + +#else // _PNP_POWER + +#pragma alloc_text(PAGE, GetGeneralIFConfig) +#pragma alloc_text(PAGE, IsLLInterfaceValueNull) +#pragma alloc_text(PAGE, GetIFAddrList) +#pragma alloc_text(PAGE, UseEtherSNAP) +#pragma alloc_text(PAGE, GetAlwaysSourceRoute) +#pragma alloc_text(PAGE, GetArpCacheLife) + +#endif // _PNP_POWER + +#pragma alloc_text(PAGE, OpenIFConfig) +#pragma alloc_text(PAGE, CloseIFConfig) +#pragma alloc_text(PAGE, RouteMatch) +#pragma alloc_text(PAGE, SetPersistentRoutesForNTE) +#pragma alloc_text(PAGE, IPConvertStringToAddress) + +#endif // ALLOC_PRAGMA + +// +// Function definitions +// +NTSTATUS +IPDriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + Initialization routine for the IP driver. + +Arguments: + + DriverObject - Pointer to the IP driver object created by the system. + DeviceDescription - The name of IP's node in the registry. + +Return Value: + + The final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING deviceName; + + + IPDriverObject = DriverObject; + + // + // Create the device object. IoCreateDevice zeroes the memory + // occupied by the object. + // + + RtlInitUnicodeString(&deviceName, DD_IP_DEVICE_NAME); + + status = IoCreateDevice( + DriverObject, + 0, + &deviceName, + FILE_DEVICE_NETWORK, + 0, + FALSE, + &IPDeviceObject + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP initialization failed: Unable to create device object %ws, status %lx.", + DD_IP_DEVICE_NAME, + status + )); + + CTELogEvent( + DriverObject, + EVENT_TCPIP_CREATE_DEVICE_FAILED, + 1, + 1, + &deviceName.Buffer, + 0, + NULL + ); + + return(status); + } + + // + // Intialize the device object. + // + IPDeviceObject->Flags |= DO_DIRECT_IO; + + // + // Initialize the list of pending echo request IRPs. + // + InitializeListHead(&PendingEchoList); + + // + // Initialize the list of pending SetAddr request IRPs. + // + InitializeListHead(&PendingIPSetNTEAddrList); + + // + // Finally, read our configuration parameters from the registry. + // + status = IPProcessConfiguration(); + + if (status != STATUS_SUCCESS) { + IoDeleteDevice(IPDeviceObject); + } + + return(status); +} + +NTSTATUS +IPProcessConfiguration( + VOID + ) + +/*++ + +Routine Description: + + Reads the IP configuration information from the registry and constructs + the configuration structure expected by the IP driver. + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS or an error status if an operation fails. + +--*/ + +{ + NTSTATUS status; + HANDLE myRegKey = NULL; + UNICODE_STRING bindString; + WCHAR *aName, + *endOfString; + WCHAR IPParametersRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters"; + WCHAR IPLinkageRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Linkage"; + uint ArpTRSingleRoute; + + + bindString.Buffer = NULL; + + IPConfiguration = CTEAllocMem(sizeof(IPConfigInfo)); + + if (IPConfiguration == NULL) { + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_RESOURCES_FOR_INIT, + 1, + 0, + NULL, + 0, + NULL + ); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTEMemSet(IPConfiguration, 0, sizeof(IPConfigInfo)); + +#ifndef _PNP_POWER + + IPConfiguration->ici_netinfo = CTEAllocMem( + sizeof(NetConfigInfo) * DEFAULT_IP_NETS + ); + + if (IPConfiguration->ici_netinfo == NULL) { + + CTEFreeMem(IPConfiguration); + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_RESOURCES_FOR_INIT, + 2, + 0, + NULL, + 0, + NULL + ); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + CTEMemSet( + IPConfiguration->ici_netinfo, + 0, + sizeof(NetConfigInfo) * DEFAULT_IP_NETS + ); + +#endif // _PNP_POWER + + // + // Process the Ip\Parameters section of the registry + // + status = OpenRegKey(&myRegKey, IPParametersRegistryKey); + + if (NT_SUCCESS(status)) { + // + // Expected configuration values. We use reasonable defaults if they + // aren't available for some reason. + // + status = GetRegDWORDValue( + myRegKey, + L"IpEnableRouter", + &(IPConfiguration->ici_gateway) + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP: Unable to read IpEnableRouter value from the registry.\n" + " Routing will be disabled.\n" + )); + IPConfiguration->ici_gateway = 0; + } + + // + // Optional (hidden) values + // + (VOID)InitRegDWORDParameter( + myRegKey, + L"ForwardBufferMemory", + &(IPConfiguration->ici_fwbufsize), + DEFAULT_FW_BUFSIZE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"MaxForwardBufferMemory", + &(IPConfiguration->ici_maxfwbufsize), + DEFAULT_MAX_FW_BUFSIZE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"ForwardBroadcasts", + &(IPConfiguration->ici_fwbcast), + FALSE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"NumForwardPackets", + &(IPConfiguration->ici_fwpackets), + DEFAULT_FW_PACKETS + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"MaxNumForwardPackets", + &(IPConfiguration->ici_maxfwpackets), + DEFAULT_MAX_FW_PACKETS + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"IGMPLevel", + &(IPConfiguration->ici_igmplevel), + DEFAULT_IGMP_LEVEL + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"EnableDeadGWDetect", + &(IPConfiguration->ici_deadgwdetect), + TRUE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"EnablePMTUDiscovery", + &(IPConfiguration->ici_pmtudiscovery), + TRUE + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"DefaultTTL", + &(IPConfiguration->ici_ttl), + DEFAULT_TTL + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"DefaultTOS", + &(IPConfiguration->ici_tos), + DEFAULT_TOS + ); + + (VOID)InitRegDWORDParameter( + myRegKey, + L"ArpUseEtherSnap", + &ArpUseEtherSnap, + FALSE + ); + + // + // we check for the return status here because if this parameter was + // not defined, then we want the default behavior for both arp + // and ip broadcasts. For arp, the behavior is to not source route + // and source router alternately. For ip, it is to always source + // route. If the parameter is defined and is 0, then for arp the + // behavior does not change. For ip however, we do not source route + // at all. Ofcourse, when the parameter is set to a non-zero value, + // we always source route for both. + // + status = InitRegDWORDParameter( + myRegKey, + L"ArpAlwaysSourceRoute", + &ArpAlwaysSourceRoute, + FALSE + ); + + if (NT_SUCCESS(status)) + { + IPAlwaysSourceRoute = ArpAlwaysSourceRoute; + } + (VOID)InitRegDWORDParameter( + myRegKey, + L"ArpTRSingleRoute", + &ArpTRSingleRoute, + FALSE + ); + + if (ArpTRSingleRoute) { + TrRii = TR_RII_SINGLE; + } else { + TrRii = TR_RII_ALL; + } + + (VOID)InitRegDWORDParameter( + myRegKey, + L"ArpCacheLife", + &ArpCacheLife, + DEFAULT_ARP_CACHE_LIFE + ); + + ZwClose(myRegKey); + myRegKey = NULL; + } + else { + // + // Use reasonable defaults. + // + IPConfiguration->ici_fwbcast = 0; + IPConfiguration->ici_gateway = 0; + IPConfiguration->ici_fwbufsize = DEFAULT_FW_BUFSIZE; + IPConfiguration->ici_fwpackets = DEFAULT_FW_PACKETS; + IPConfiguration->ici_maxfwbufsize = DEFAULT_MAX_FW_BUFSIZE; + IPConfiguration->ici_maxfwpackets = DEFAULT_MAX_FW_PACKETS; + IPConfiguration->ici_igmplevel = DEFAULT_IGMP_LEVEL; + IPConfiguration->ici_deadgwdetect = FALSE; + IPConfiguration->ici_pmtudiscovery = FALSE; + IPConfiguration->ici_ttl = DEFAULT_TTL; + IPConfiguration->ici_tos = DEFAULT_TOS; + + TCPTRACE(( + "IP: Unable to open Tcpip\\Parameters registry key. Using defaults.\n" + )); + } + + + // + // Process the Ip\Linkage section of the registry + // + status = OpenRegKey(&myRegKey, IPLinkageRegistryKey); + + if (NT_SUCCESS(status)) { + + bindString.Buffer = CTEAllocMem(WORK_BUFFER_SIZE * sizeof(WCHAR)); + + if (bindString.Buffer == NULL) { + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_RESOURCES_FOR_INIT, + 3, + 0, + NULL, + 0, + NULL + ); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto error_exit; + } + + bindString.Buffer[0] = UNICODE_NULL; + bindString.Length = 0; + bindString.MaximumLength = WORK_BUFFER_SIZE * sizeof(WCHAR); + + status = GetRegMultiSZValue( + myRegKey, + L"Bind", + &bindString + ); + + if (NT_SUCCESS(status)) { + aName = bindString.Buffer; + + if (bindString.Length > 0) { + // + // bindString is a MULTI_SZ which is a series of strings separated + // by NULL's with a double NULL at the end. + // + while (*aName != UNICODE_NULL) { + PWCHAR deviceName; + + deviceName = aName; + + // + // Find the end of the current string in the MULTI_SZ. + // + while (*aName != UNICODE_NULL) { + aName++; + ASSERT( + aName < + (PWCHAR) ( ((PUCHAR)bindString.Buffer) + + bindString.MaximumLength + ) + ); + } + + endOfString = aName; + + // + // Backtrack to the first backslash. + // + while ((aName >= bindString.Buffer) && (*aName-- != L'\\')); + + aName += 2; + + status = IPProcessAdapterSection( + deviceName, + aName + ); + + aName = endOfString + 1; + } + } + } +#ifndef _PNP_POWER + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_BINDINGS, + 1, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open Tcpip\\Linkage\\Bind registry value.\n" + " Only the local loopback interface will be accessible.\n" + )); + + } +#endif _PNP_POWER + } + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_BINDINGS, + 2, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open registry key Tcpip\\Linkage.\n" + " Only the local loopback interface will be accessible.\n" + )); + } + + +#ifndef _PNP_POWER + + // + // Allocate the Driver and Adapter name tables + // + + DriverNameTable = (DriverRegMapping *) CTEAllocMem( + (IPConfiguration->ici_numnets + 1) * + sizeof(DriverRegMapping) + ); + + AdptNameTable = (NameMapping *) CTEAllocMem( + (IPConfiguration->ici_numnets + 1) * + sizeof(NameMapping) + ); + + if ((DriverNameTable != NULL) && (AdptNameTable != NULL)) { + CTEMemSet( + DriverNameTable, + 0, + sizeof(DriverRegMapping) * (IPConfiguration->ici_numnets + 1) + ); + CTEMemSet( + AdptNameTable, + 0, + sizeof(NameMapping) * (IPConfiguration->ici_numnets + 1) + ); + +#endif // _PNP_POWER + + if (!IPInit()) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_IP_INIT_FAILED, + 1, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(("IP initialization failed.\n")); + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + +#ifndef _PNP_POWER + + } + else { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_IP_INIT_FAILED, + 1, + 0, + NULL, + 0, + NULL + ); + + TCPTRACE(("IP initialization failed.\n")); + status = STATUS_UNSUCCESSFUL; + } + +#endif // _PNP_POWER + +error_exit: + + if (bindString.Buffer != NULL) { + CTEFreeMem(bindString.Buffer); + } + +#ifndef _PNP_POWER + + if (AdptNameTable != NULL) { + CTEFreeMem(AdptNameTable); + } + + if (DriverNameTable != NULL) { + CTEFreeMem(DriverNameTable); + } + +#endif // _PNP_POWER + + if (myRegKey != NULL) { + ZwClose(myRegKey); + } + + if (IPConfiguration != NULL) { + IPFreeConfig(IPConfiguration); + } + + return(status); +} + + +NTSTATUS +IPProcessAdapterSection( + WCHAR *DeviceName, + WCHAR *AdapterName + ) + +/*++ + +Routine Description: + + Reads all of the information needed under the Parameters\TCPIP section + of an adapter to which IP is bound. + +Arguments: + + DeviceName - The name of the IP device. + AdapterName - The registry key for the adapter for this IP net. + +Return Value: + + STATUS_SUCCESS or an error status if an operation fails. + +--*/ + +{ + HANDLE myRegKey; + UNICODE_STRING valueString; + NTSTATUS status; + ULONG valueType; + ulong invalidNetContext = 0xFFFF; + WCHAR TcpipParametersKey[] = L"\\Parameters\\TCPIP"; + WCHAR ServicesRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; +#ifndef _PNP_POWER + uint numberOfGateways = 0; + uint llInterfaceType; + WCHAR *ipAddressBuffer = NULL; + WCHAR *subnetMaskBuffer = NULL; + NDIS_STRING llInterfaceString; + PER_NET_CONFIG_INFO perNetConfigInfo; + NetConfigInfo *NetConfiguration; + PWCHAR temp; + + + RtlInitUnicodeString(&llInterfaceString, NULL); + + NetConfiguration = IPConfiguration->ici_netinfo + + IPConfiguration->ici_numnets; + +#endif // _PNP_POWER + + // + // Get the size of the AdapterName string the easy way. + // + RtlInitUnicodeString(&valueString, AdapterName); + + valueString.MaximumLength += sizeof(ServicesRegistryKey) + + sizeof(TcpipParametersKey); + + valueString.Buffer = CTEAllocMem(valueString.MaximumLength); + + if (valueString.Buffer == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for reg key name\n")); + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + valueString.Length = 0; + valueString.Buffer[0] = UNICODE_NULL; + + // + // Build the key name for the tcpip parameters section and open key. + // + status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append services name to key string\n")); + + goto exit2; + } + + status = RtlAppendUnicodeToString(&valueString, AdapterName); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 2, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append adapter name to key string\n")); + + goto exit2; + } + + status = RtlAppendUnicodeToString(&valueString, TcpipParametersKey); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 3, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append parameters name to key string\n")); + + goto exit2; + } + + status = OpenRegKey(&myRegKey, valueString.Buffer); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open adapter registry key %ws\n", + valueString.Buffer + )); + + goto exit2; + } + + // + // Invalidate the interface context for DHCP. + // When the first net is successfully configured, we'll write in the + // proper values. + // + status = SetRegDWORDValue( + myRegKey, + L"IPInterfaceContext", + &(invalidNetContext) + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_DHCP_INIT_FAILED, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to Invalidate IPInterfaceContext value for adapter %ws.\n" + " DHCP may fail on this adapter.\n", + AdapterName + )); + + goto exit1; + } + +#ifndef _PNP_POWER + + // + // Process the gateway MultiSZ. The end is signified by a double NULL. + // This list currently only applies to the first IP address configured + // on this interface. + // + status = GetRegMultiSZValue( + myRegKey, + L"DefaultGateway", + &valueString + ); + + if (NT_SUCCESS(status)) { + PWCHAR addressString = valueString.Buffer; + + while (*addressString != UNICODE_NULL) { + IPAddr addressValue; + BOOLEAN conversionStatus; + + if (numberOfGateways >= MAX_DEFAULT_GWS) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_TOO_MANY_GATEWAYS, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + break; + } + + conversionStatus = IPConvertStringToAddress( + addressString, + &addressValue + ); + + if (conversionStatus && (addressValue != 0xFFFFFFFF)) { + if (addressValue != INADDR_ANY) { + NetConfiguration->nci_gw[numberOfGateways++] = addressValue; + } + } + else { + PWCHAR stringList[2]; + + stringList[0] = addressString; + stringList[1] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_DEFAULT_GATEWAY, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid default gateway address %ws specified for adapter %ws.\n" + " Remote networks may not be reachable as a result.\n", + addressString, + AdapterName + )); + } + + // + // Walk over the entry we just processed. + // + while (*addressString++ != UNICODE_NULL); + } + } + else { + TCPTRACE(( + "IP: Unable to read DefaultGateway value for adapter %ws.\n" + " Initialization will continue.\n", + AdapterName + )); + } + + perNetConfigInfo.NumberOfGateways = numberOfGateways; + + // + // Figure out which lower layer driver to bind. + // + status = GetRegSZValue( + myRegKey, + L"LLInterface", + &valueString, + &valueType + ); + + if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) { + llInterfaceType = NET_TYPE_WAN; + + if (!CTEAllocateString( + &llInterfaceString, + CTELengthString(&valueString) + ) + ) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP initialization failure: Unable to allocate memory " + "for LLInterface string for adapter %ws.\n", + AdapterName + )); + status = STATUS_INSUFFICIENT_RESOURCES; + goto exit1; + } + + CTECopyString( + &llInterfaceString, + &valueString + ); + } + else { + // + // If the key isn't present or is empty, we use ARP + // + llInterfaceType = NET_TYPE_LAN; + } + + // + // Are we using zeros broadcasts? + // + status = GetRegDWORDValue( + myRegKey, + L"UseZeroBroadcast", + &(perNetConfigInfo.UseZeroBroadcast) + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n" + " All-nets broadcasts will be addressed to 255.255.255.255.\n", + AdapterName + )); + perNetConfigInfo.UseZeroBroadcast = FALSE; // default to off + } + + // + // Has anyone specified an MTU? + // + status = GetRegDWORDValue( + myRegKey, + L"MTU", + &(perNetConfigInfo.Mtu) + ); + + if (!NT_SUCCESS(status)) { + perNetConfigInfo.Mtu = 0xFFFFFFF; // The stack will pick one. + } + + // + // Have we been configured for more routing packets? + // + status = GetRegDWORDValue( + myRegKey, + L"MaxForwardPending", + &(perNetConfigInfo.MaxForwardPending) + ); + + if (!NT_SUCCESS(status)) { + perNetConfigInfo.MaxForwardPending = DEFAULT_MAX_PENDING; + } + + // + // Read the IP address and Subnet Mask lists + // + status = GetRegMultiSZValue( + myRegKey, + L"IpAddress", + &valueString + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADDRESS_LIST, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the IP address list for adapter %ws.\n" + " IP will not be operational on this adapter\n", + AdapterName + )); + goto exit1; + } + + ipAddressBuffer = ExAllocatePool(NonPagedPool, valueString.Length); + + if (ipAddressBuffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list\n")); + goto exit1; + } + + RtlCopyMemory(ipAddressBuffer, valueString.Buffer, valueString.Length); + + status = GetRegMultiSZValue( + myRegKey, + L"Subnetmask", + &valueString + ); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_MASK_LIST, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the subnet mask list for adapter %ws.\n" + " IP will not be operational on this adapter.\n", + AdapterName + )); + goto exit1; + } + + subnetMaskBuffer = ExAllocatePool(NonPagedPool, valueString.Length); + + if (subnetMaskBuffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 3, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for subnet mask list\n")); + goto exit1; + } + + RtlCopyMemory(subnetMaskBuffer, valueString.Buffer, valueString.Length); + + // + // Initialize each net in the list + // + status = IPProcessIPAddressList( + myRegKey, + DeviceName, + AdapterName, + ipAddressBuffer, + subnetMaskBuffer, + &llInterfaceString, + llInterfaceType, + &perNetConfigInfo + ); + + if (status == STATUS_SUCCESS) { + // + // We leave the registry key open. It will be closed when + // initialization is completed. + // + goto exit2; + } + +#endif // ndef _PNP_POWER + + +exit1: + + ZwClose(myRegKey); + +exit2: + + if (valueString.Buffer != NULL) { + CTEFreeMem(valueString.Buffer); + } + +#ifndef _PNP_POWER + + if (ipAddressBuffer != NULL) { + ExFreePool(ipAddressBuffer); + } + + if (subnetMaskBuffer != NULL) { + ExFreePool(subnetMaskBuffer); + } + + if (llInterfaceString.Buffer != NULL) { + CTEFreeString(&llInterfaceString); + } + +#endif // _PNP_POWER + + return(status); +} + +#ifndef _PNP_POWER + +NTSTATUS +IPProcessIPAddressList( + HANDLE AdapterKey, + WCHAR *DeviceName, + WCHAR *AdapterName, + WCHAR *IpAddressList, + WCHAR *SubnetMaskList, + NDIS_STRING *LowerInterfaceString, + uint LowerInterfaceType, + PPER_NET_CONFIG_INFO PerNetConfigInfo + ) + +/*++ + +Routine Description: + + Processes the IP address string for an adapter and creates entries + in the IP configuration structure for each interface. + +Arguments: + + AdapterKey - The registry key for the adapter for this IP net. + DeviceName - The name of the IP device. + AdapterName - The name of the adapter being configured. + IpAddressList - The REG_MULTI_SZ list of IP address strings for + this adapter. + SubnetMaskList - The REG_MULTI_SZ list of subnet masks to match the + the addresses in IpAddressList. + LowerInterfaceString - The name of the link layer interface driver + supporting this adapter. + LowerInterfaceType - The type of link layer interface (LAN, WAN, etc). + PerNetConfigInfo - Miscellaneous information that applies to all + network interfaces on an adapter. + +Return Value: + + An error status if an error occurs which prevents configuration + from continuing, else STATUS_SUCCESS. Events will be logged for + non-fatal errors. + +--*/ + +{ + IPAddr addressValue; + BOOLEAN firstTime = TRUE; + BOOLEAN configuredOne = FALSE; + NetConfigInfo *NetConfiguration; + UNICODE_STRING adapterString; + UNICODE_STRING configString; + PWCHAR configName = L"\\Parameters\\Tcpip"; + + + while (*IpAddressList != UNICODE_NULL) { + BOOLEAN conversionStatus; + + + if (IPConfiguration->ici_numnets >= ((int) (NetConfigSize - 1))) { + NetConfigInfo *NewInfo; + + NewInfo = CTEAllocMem( + (NetConfigSize + DEFAULT_IP_NETS) * + sizeof(NetConfigInfo) + ); + + if (NewInfo == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_TOO_MANY_NETS, + 1, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: bound to too many nets. Further bindings, starting with\n" + " network %ws on adapter %ws cannot be made\n", + IpAddressList, + AdapterName + )); + + break; + } + + CTEMemCopy( + NewInfo, + IPConfiguration->ici_netinfo, + NetConfigSize * sizeof(NetConfigInfo) + ); + + CTEMemSet( + (NewInfo + NetConfigSize), + 0, + DEFAULT_IP_NETS + ); + + CTEFreeMem(IPConfiguration->ici_netinfo); + IPConfiguration->ici_netinfo = NewInfo; + NetConfigSize += DEFAULT_IP_NETS; + } + + NetConfiguration = IPConfiguration->ici_netinfo + + IPConfiguration->ici_numnets; + + if (*SubnetMaskList == UNICODE_NULL) { + PWCHAR stringList[2]; + + stringList[0] = IpAddressList; + stringList[1] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_MASK, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: No subnet specified for IP address %ws and all\n" + " subsequent IP addresses on adapter %ws. These\n" + " interfaces will not be initialized.\n", + IpAddressList, + AdapterName + )); + + break; + } + + conversionStatus = IPConvertStringToAddress( + IpAddressList, + &addressValue + ); + + if (!conversionStatus || (addressValue == 0xFFFFFFFF)) { + PWCHAR stringList[2]; + + stringList[0] = IpAddressList; + stringList[1] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_ADDRESS, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid IP address %ws specified for adapter %ws.\n" + " This interface will not be initialized.\n", + IpAddressList, + AdapterName + )); + firstTime = FALSE; + goto next_entry; + } + + NetConfiguration->nci_addr = addressValue; + + conversionStatus = IPConvertStringToAddress( + SubnetMaskList, + &addressValue + ); + + if (!conversionStatus || (addressValue == 0xFFFFFFFF)) { + PWCHAR stringList[3]; + + stringList[0] = SubnetMaskList; + stringList[1] = IpAddressList; + stringList[2] = AdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_MASK, + 1, + 3, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid subnet Mask %ws specified for IP address %ws " + "on adapter %ws\n" + " This interface will not be initialized\n", + SubnetMaskList, + IpAddressList, + AdapterName + )); + firstTime = FALSE; + goto next_entry; + } + + NetConfiguration->nci_mask = addressValue; + + NetConfiguration->nci_mtu = PerNetConfigInfo->Mtu; + NetConfiguration->nci_maxpending = PerNetConfigInfo->MaxForwardPending; + NetConfiguration->nci_zerobcast = PerNetConfigInfo->UseZeroBroadcast; + NetConfiguration->nci_type = LowerInterfaceType; + + NetConfiguration->nci_numgws = PerNetConfigInfo->NumberOfGateways; + PerNetConfigInfo->NumberOfGateways = 0; + // this only applies to the first interface. + + NetConfiguration->nci_type = LowerInterfaceType; + + RtlInitUnicodeString( + &(NetConfiguration->nci_name), + DeviceName + ); + + RtlInitUnicodeString(&configString, configName); + RtlInitUnicodeString(&adapterString, AdapterName); + + if (!CTEAllocateString( + &(NetConfiguration->nci_configname), + (adapterString.Length + configString.Length) + ) + ) + { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to allocate ConfigName string for interface\n" + " %ws on adapter %ws. This interface and all subsequent\n" + " interfaces on this adapter will be unavailable.\n", + IpAddressList, + AdapterName + )); + break; + } + + CTECopyString( + &(NetConfiguration->nci_configname), + &adapterString + ); + + RtlAppendUnicodeStringToString( + &(NetConfiguration->nci_configname), + &configString + ); + + if (LowerInterfaceType != NET_TYPE_LAN) { + + if (!CTEAllocateString( + &(NetConfiguration->nci_driver), + CTELengthString(LowerInterfaceString) + ) + ) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &AdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to allocate LLInterface string for interface\n" + " %ws on adapter %ws. This interface and all subsequent\n" + " interfaces on this adapter will be unavailable.\n", + IpAddressList, + AdapterName + )); + break; + } + + CTECopyString( + &(NetConfiguration->nci_driver), + LowerInterfaceString + ); + } + else { + RtlInitUnicodeString(&(NetConfiguration->nci_driver), NULL); + } + + if (firstTime) { + firstTime = FALSE; + NetConfiguration->nci_reghandle = AdapterKey; + } + else { + NetConfiguration->nci_reghandle = NULL; + } + + IPConfiguration->ici_numnets++; + configuredOne = TRUE; + +next_entry: + + while(*IpAddressList++ != UNICODE_NULL); + while(*SubnetMaskList++ != UNICODE_NULL); + } + + if (configuredOne == FALSE) { + ZwClose(AdapterKey); + } + + return(STATUS_SUCCESS); +} + + +#else // ndef _PNP_POWER + + +uint +GetGeneralIFConfig( + IFGeneralConfig *ConfigInfo, + NDIS_HANDLE Handle + ) + +/*++ + + Routine Description: + + A routine to get the general per-interface config info, such as MTU, + type of broadcast, etc. The caller gives us a structure to be filled in + and a handle, and we fill in the structure if we can. + + Arguments: + ConfigInfo - Structure to be filled in. + Handle - Config handle from OpenIFConfig(). + + Return Value: + TRUE if we got all the required info, FALSE otherwise. + +--*/ + +{ + UNICODE_STRING valueString; + NTSTATUS status; + UINT numberOfGateways = 0; + UCHAR TempBuffer[WORK_BUFFER_SIZE]; + ULONG ulAddGateway,ulTemp; + + PAGED_CODE(); + + // + // Process the gateway MultiSZ. The end is signified by a double NULL. + // This list currently only applies to the first IP address configured + // on this interface. + // + + ConfigInfo->igc_numgws = 0; + + ulAddGateway = TRUE; + + CTEMemSet(ConfigInfo->igc_gw, 0, sizeof(IPAddr) * MAX_DEFAULT_GWS); + + valueString.Length = 0; + valueString.MaximumLength = WORK_BUFFER_SIZE; + valueString.Buffer = (PWCHAR)TempBuffer; + + ulTemp = 0; + + status = GetRegDWORDValue(Handle, + L"DontAddDefaultGateway", + &ulTemp); + + if(NT_SUCCESS(status)) + { + if(ulTemp == 1) + { + ulAddGateway = FALSE; + } + } + + if(ulAddGateway) + { + status = GetRegMultiSZValue( + Handle, + L"DefaultGateway", + &valueString + ); + + if (NT_SUCCESS(status)) { + PWCHAR addressString = valueString.Buffer; + + while (*addressString != UNICODE_NULL) { + IPAddr addressValue; + BOOLEAN conversionStatus; + + if (numberOfGateways >= MAX_DEFAULT_GWS) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_TOO_MANY_GATEWAYS, + 1, + 1, + &TempAdapterName, + 0, + NULL + ); + + break; + } + + conversionStatus = IPConvertStringToAddress( + addressString, + &addressValue + ); + + if (conversionStatus && (addressValue != 0xFFFFFFFF)) { + if (addressValue != INADDR_ANY) { + ConfigInfo->igc_gw[numberOfGateways++] = addressValue; + } + } + else { + PWCHAR stringList[2]; + + stringList[0] = addressString; + stringList[1] = TempAdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_DEFAULT_GATEWAY, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid default gateway address %ws specified for adapter %ws.\n" + " Remote networks may not be reachable as a result.\n", + addressString, + TempAdapterName + )); + } + + // + // Walk over the entry we just processed. + // + while (*addressString++ != UNICODE_NULL); + } + } + else { + TCPTRACE(( + "IP: Unable to read DefaultGateway value for adapter %ws.\n" + " Initialization will continue.\n", + TempAdapterName + )); + } + + ConfigInfo->igc_numgws = numberOfGateways; + } + + // + // Are we using zeros broadcasts? + // + status = GetRegDWORDValue( + Handle, + L"UseZeroBroadcast", + &(ConfigInfo->igc_zerobcast) + ); + + if (!NT_SUCCESS(status)) { + TCPTRACE(( + "IP: Unable to read UseZeroBroadcast value for adapter %ws.\n" + " All-nets broadcasts will be addressed to 255.255.255.255.\n", + TempAdapterName + )); + ConfigInfo->igc_zerobcast = FALSE; // default to off + } + + // + // Has anyone specified an MTU? + // + status = GetRegDWORDValue( + Handle, + L"MTU", + &(ConfigInfo->igc_mtu) + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_mtu = 0xFFFFFFF; // The stack will pick one. + } + + // + // Have we been configured for more routing packets? + // + status = GetRegDWORDValue( + Handle, + L"MaxForwardPending", + &(ConfigInfo->igc_maxpending) + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_maxpending = DEFAULT_MAX_PENDING; + } + // + // Has Router Discovery been configured? + // + + status = GetRegDWORDValue( + Handle, + L"PerformRouterDiscovery", + &(ConfigInfo->igc_rtrdiscovery) + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_rtrdiscovery = 1; + } + + // + // [BUGBUG] Only for 4.0 sp2 Turn off ICMP rtr discovery. + // + ConfigInfo->igc_rtrdiscovery = 0; + + // + // Has Router Discovery Address been configured? + // + + status = GetRegDWORDValue( + Handle, + L"SolicitationAddressBCast", + &ulTemp + ); + + if (!NT_SUCCESS(status)) { + ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST; + } else { + if (ulTemp == 1) { + ConfigInfo->igc_rtrdiscaddr = 0xffffffff; + } else { + ConfigInfo->igc_rtrdiscaddr = ALL_ROUTER_MCAST; + } + } + + return TRUE; +} + + +int +IsLLInterfaceValueNull( + NDIS_HANDLE Handle + ) +/*++ + + Routine Description: + + Called to see if the LLInterface value in the registry key for which the + handle is provided, is NULL or not. + + Arguments: + Handle - Handle to use for reading config. + + Return Value: + + FALSE if value is not null + TRUE if it is null + +--*/ +{ + UNICODE_STRING valueString ; + ULONG valueType ; + NTSTATUS status ; + + + PAGED_CODE(); + + valueString.MaximumLength = 200 ; + valueString.Buffer = CTEAllocMem(valueString.MaximumLength) ; + + status = GetRegSZValue( + Handle, + L"LLInterface", + &valueString, + &valueType + ); + + if (NT_SUCCESS(status) && (*(valueString.Buffer) != UNICODE_NULL)) { + CTEFreeMem (valueString.Buffer) ; + return FALSE ; + } else { + CTEFreeMem (valueString.Buffer) ; + return TRUE ; + } +} + + +IFAddrList * +GetIFAddrList( + UINT *NumAddr, + NDIS_HANDLE Handle + ) +/*++ + + Routine Description: + + Called to read the list of IF addresses and masks for an interface. + We'll get the address pointer first, then walk the list counting + to find out how many addresses we have. Then we allocate memory for the + list, and walk down the list converting them. After that we'll get + the mask list and convert it. + + Arguments: + NumAddr - Where to return number of address we have. + Handle - Handle to use for reading config. + + Return Value: + + Pointer to IF address list if we get one, or NULL otherwise. + +--*/ +{ + UNICODE_STRING ValueString; + NTSTATUS Status; + UINT AddressCount = 0; + UINT GoodAddresses = 0; + PWCHAR CurrentAddress; + PWCHAR CurrentMask; + PWCHAR AddressString; + PWCHAR MaskString; + IFAddrList *AddressList; + UINT i; + BOOLEAN ConversionStatus; + IPAddr AddressValue; + IPAddr MaskValue; + UCHAR TempBuffer[WORK_BUFFER_SIZE]; + + + PAGED_CODE(); + + ValueString.Length = 0; + ValueString.MaximumLength = WORK_BUFFER_SIZE; + ValueString.Buffer = (PWCHAR)CTEAllocMem(WORK_BUFFER_SIZE); + + if (ValueString.Buffer == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list WB\n")); + return NULL; + } + + // First, try to read the IpAddress string. + + Status = GetRegMultiSZValue( + Handle, + L"IpAddress", + &ValueString + ); + + if (!NT_SUCCESS(Status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADDRESS_LIST, + 1, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the IP address list for adapter %ws.\n" + " IP will not be operational on this adapter\n", + TempAdapterName + )); + ExFreePool(ValueString.Buffer); + return NULL; + } + + AddressString = ExAllocatePool(NonPagedPool, ValueString.MaximumLength); + + if (AddressString == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list\n")); + ExFreePool(ValueString.Buffer); + return NULL; + } + + RtlCopyMemory(AddressString, ValueString.Buffer, ValueString.MaximumLength); + + Status = GetRegMultiSZValue( + Handle, + L"Subnetmask", + &ValueString + ); + + if (!NT_SUCCESS(Status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_MASK_LIST, + 1, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to read the subnet mask list for adapter %ws.\n" + " IP will not be operational on this adapter.\n", + TempAdapterName + )); + + ExFreePool(AddressString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + MaskString = ExAllocatePool(NonPagedPool, ValueString.MaximumLength); + + if (MaskString == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 3, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for subnet mask list\n")); + ExFreePool(AddressString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + RtlCopyMemory(MaskString, ValueString.Buffer, ValueString.MaximumLength); + + + CurrentAddress = AddressString; + CurrentMask = MaskString; + + while (*CurrentAddress != UNICODE_NULL && + *CurrentMask != UNICODE_NULL) { + + // We have a potential IP address. + + AddressCount++; + + // Skip this one. + while (*CurrentAddress++ != UNICODE_NULL); + while (*CurrentMask++ != UNICODE_NULL); + } + + if (AddressCount == 0) { + + ExFreePool(AddressString); + ExFreePool(MaskString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + // Allocate memory. + AddressList = CTEAllocMem(sizeof(IFAddrList) * AddressCount); + + if (AddressList == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 2, + 1, + &TempAdapterName, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for IP address list\n")); + ExFreePool(AddressString); + ExFreePool(MaskString); + ExFreePool(ValueString.Buffer); + return NULL; + } + + // Walk the list again, converting each address. + + CurrentAddress = AddressString; + CurrentMask = MaskString; + + for (i = 0; i < AddressCount; i++) { + ConversionStatus = IPConvertStringToAddress( + CurrentAddress, + &AddressValue + ); + + if (!ConversionStatus || (AddressValue == 0xFFFFFFFF)) { + PWCHAR stringList[2]; + + stringList[0] = CurrentAddress; + stringList[1] = TempAdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_ADDRESS, + 1, + 2, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid IP address %ws specified for adapter %ws.\n" + " This interface will not be initialized.\n", + CurrentAddress, + TempAdapterName + )); + + goto nextone; + + } + + // Now do the current mask. + + ConversionStatus = IPConvertStringToAddress( + CurrentMask, + &MaskValue + ); + + if (!ConversionStatus) { + PWCHAR stringList[3]; + + stringList[0] = CurrentMask; + stringList[1] = CurrentAddress; + stringList[2] = TempAdapterName; + + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_INVALID_MASK, + 1, + 3, + stringList, + 0, + NULL + ); + + TCPTRACE(( + "IP: Invalid subnet Mask %ws specified for IP address %ws " + "on adapter %ws\n" + " This interface will not be initialized\n", + CurrentMask, + CurrentAddress, + TempAdapterName + )); + } else { + AddressList[GoodAddresses].ial_addr = AddressValue; + AddressList[GoodAddresses].ial_mask = MaskValue; + GoodAddresses++; + } + +nextone: + while(*CurrentAddress++ != UNICODE_NULL); + while(*CurrentMask++ != UNICODE_NULL); + + } + + ExFreePool(AddressString); + ExFreePool(MaskString); + ExFreePool(ValueString.Buffer); + + *NumAddr = GoodAddresses; + + if (GoodAddresses == 0) { + ExFreePool(AddressList); + AddressList = NULL; + } + + return AddressList; +} + +#endif // PNP_POWER + + +UINT +OpenIFConfig( + PNDIS_STRING ConfigName, + NDIS_HANDLE *Handle + ) + +/*++ + + Routine Description: + + Called when we want to open our per-info config info. We do so if we can, + otherwise we fail the request. + + Arguments: + ConfigName - Name of interface to open. + Handle - Where to return the handle. + + Return Value: + TRUE if we succeed, FALSE if we don't. + + +--*/ + +{ + NTSTATUS status; + HANDLE myRegKey; + UNICODE_STRING valueString; + WCHAR ServicesRegistryKey[] = + L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"; + UINT RetStatus = FALSE; + + + PAGED_CODE(); + + TempAdapterName = ConfigName->Buffer; + + // + // Get the size of the ConfigName string the easy way. + // + RtlInitUnicodeString(&valueString, (PWCHAR)ConfigName->Buffer); + + valueString.MaximumLength += sizeof(ServicesRegistryKey); + + valueString.Buffer = ExAllocatePool( + NonPagedPool, + valueString.MaximumLength + ); + + if (valueString.Buffer == NULL) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_NO_ADAPTER_RESOURCES, + 4, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to allocate memory for reg key name\n")); + + return(FALSE); + } + + valueString.Length = 0; + valueString.Buffer[0] = UNICODE_NULL; + + // + // Build the key name for the tcpip parameters section and open key. + // + status = RtlAppendUnicodeToString(&valueString, ServicesRegistryKey); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 1, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append services name to key string\n")); + + goto done; + } + + status = RtlAppendUnicodeToString(&valueString, ConfigName->Buffer); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 2, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(("IP: Unable to append adapter name to key string\n")); + + goto done; + } + + status = OpenRegKey(&myRegKey, valueString.Buffer); + + if (!NT_SUCCESS(status)) { + CTELogEvent( + IPDriverObject, + EVENT_TCPIP_ADAPTER_REG_FAILURE, + 4, + 1, + &ConfigName->Buffer, + 0, + NULL + ); + + TCPTRACE(( + "IP: Unable to open adapter registry key %ws\n", + valueString.Buffer + )); + + } else { + RetStatus = TRUE; + *Handle = myRegKey; + } + +done: + ExFreePool(valueString.Buffer); + + return RetStatus; +} + + +VOID +CloseIFConfig( + NDIS_HANDLE Handle + ) + +/*++ + + Routine Description: + + Close a per-interface config handle opened via OpenIFConfig(). + + Arguments: + Handle - Handle to be closed. + + Return Value: + + +--*/ + +{ + PAGED_CODE(); + + ZwClose(Handle); +} + + +IPConfigInfo * +IPGetConfig( + void + ) + +/*++ + +Routine Description: + + Provides IP configuration information for the NT environment. + +Arguments: + + None + +Return Value: + + A pointer to a structure containing the configuration information. + +--*/ + +{ + return(IPConfiguration); +} + + +void +IPFreeConfig( + IPConfigInfo *ConfigInfo + ) + +/*++ + +Routine Description: + + Frees the IP configuration structure allocated by IPGetConfig. + +Arguments: + + ConfigInfo - Pointer to the IP configuration information structure to free. + +Return Value: + + None. + +--*/ + +{ + int i; + +#ifndef _PNP_POWER + + NetConfigInfo *netConfiguration; + + + if (IPConfiguration != NULL) { + for (i = 0; i < IPConfiguration->ici_numnets; i++ ) { + netConfiguration = &(IPConfiguration->ici_netinfo[i]); + + if (netConfiguration->nci_driver.Buffer != NULL) { + CTEFreeString(&(netConfiguration->nci_driver)); + } + + if (netConfiguration->nci_configname.Buffer != NULL) { + CTEFreeString(&(netConfiguration->nci_configname)); + } + + if (netConfiguration->nci_reghandle != NULL) { + ZwClose(netConfiguration->nci_reghandle); + } + } + + CTEFreeMem(IPConfiguration->ici_netinfo); + CTEFreeMem(IPConfiguration); + } + +#else // _PNP_POWER + + if (IPConfiguration != NULL) { + CTEFreeMem(IPConfiguration); + } + +#endif // _PNP_POWER + + IPConfiguration = NULL; + + return; +} + +ulong +GetGMTDelta( + void + ) + +/*++ + +Routine Description: + + Returns the offset in milliseconds of the time zone of this machine + from GMT. + +Arguments: + + None. + +Return Value: + + Time in milliseconds between this time zone and GMT. + +--*/ + +{ + LARGE_INTEGER localTime, systemTime; + + // + // Get time zone bias in 100ns. + // + localTime.LowPart = 0; + localTime.HighPart = 0; + ExLocalTimeToSystemTime(&localTime, &systemTime); + + if ((localTime.LowPart != 0) || (localTime.HighPart != 0)) { + localTime = CTEConvert100nsToMilliseconds(systemTime); + } + + ASSERT(localTime.HighPart == 0); + + return(localTime.LowPart); +} + + +ulong +GetTime( + void + ) + +/*++ + +Routine Description: + + Returns the time in milliseconds since midnight. + +Arguments: + + None. + +Return Value: + + Time in milliseconds since midnight. + +--*/ + +{ + LARGE_INTEGER ntTime; + TIME_FIELDS breakdownTime; + ulong returnValue; + + KeQuerySystemTime(&ntTime); + RtlTimeToTimeFields(&ntTime, &breakdownTime); + + returnValue = breakdownTime.Hour * 60; + returnValue = (returnValue + breakdownTime.Minute) * 60; + returnValue = (returnValue + breakdownTime.Second) * 1000; + returnValue = returnValue + breakdownTime.Milliseconds; + + return(returnValue); +} + + +ulong +GetUnique32BitValue( + void + ) + +/*++ + +Routine Description: + + Returns a reasonably unique 32-bit number based on the system clock. + In NT, we take the current system time, convert it to milliseconds, + and return the low 32 bits. + +Arguments: + + None. + +Return Value: + + A reasonably unique 32-bit value. + +--*/ + +{ + LARGE_INTEGER ntTime, tmpTime; + + KeQuerySystemTime(&ntTime); + + tmpTime = CTEConvert100nsToMilliseconds(ntTime); + + return(tmpTime.LowPart); +} + + +uint +UseEtherSNAP( + PNDIS_STRING Name + ) + +/*++ + +Routine Description: + + Determines whether the EtherSNAP protocol should be used on an interface. + +Arguments: + + Name - The device name of the interface in question. + +Return Value: + + Nonzero if SNAP is to be used on the interface. Zero otherwise. + +--*/ + +{ + UNREFERENCED_PARAMETER(Name); + + // + // We currently set this on a global basis. + // + return(ArpUseEtherSnap); +} + + +void +GetAlwaysSourceRoute( + uint *pArpAlwaysSourceRoute, + uint *pIPAlwaysSourceRoute + ) + +/*++ + +Routine Description: + + Determines whether ARP should always turn on source routing in queries. + +Arguments: + + None. + +Return Value: + + Nonzero if source routing is always to be used. Zero otherwise. + +--*/ + +{ + // + // We currently set this on a global basis. + // + *pArpAlwaysSourceRoute = ArpAlwaysSourceRoute; + *pIPAlwaysSourceRoute = IPAlwaysSourceRoute; + return; +} + + +uint +GetArpCacheLife( + void + ) + +/*++ + +Routine Description: + + Get ArpCacheLife in seconds. + +Arguments: + + None. + +Return Value: + + Set to default if not found. + +--*/ + +{ + // + // We currently set this on a global basis. + // + return(ArpCacheLife); +} + + +#define IP_ADDRESS_STRING_LENGTH (16+2) // +2 for double NULL on MULTI_SZ + + +BOOLEAN +IPConvertStringToAddress( + IN PWCHAR AddressString, + OUT PULONG IpAddress + ) + +/*++ + +Routine Description + + This function converts an Internet standard 4-octet dotted decimal + IP address string into a numeric IP address. Unlike inet_addr(), this + routine does not support address strings of less than 4 octets nor does + it support octal and hexadecimal octets. + +Arguments + + AddressString - IP address in dotted decimal notation + IpAddress - Pointer to a variable to hold the resulting address + +Return Value: + + TRUE if the address string was converted. FALSE otherwise. + +--*/ + +{ + UNICODE_STRING unicodeString; + STRING aString; + UCHAR dataBuffer[IP_ADDRESS_STRING_LENGTH]; + NTSTATUS status; + PUCHAR addressPtr, cp, startPointer, endPointer; + ULONG digit, multiplier; + int i; + + + PAGED_CODE(); + + aString.Length = 0; + aString.MaximumLength = IP_ADDRESS_STRING_LENGTH; + aString.Buffer = dataBuffer; + + RtlInitUnicodeString(&unicodeString, AddressString); + + status = RtlUnicodeStringToAnsiString( + &aString, + &unicodeString, + FALSE + ); + + if (!NT_SUCCESS(status)) { + return(FALSE); + } + + *IpAddress = 0; + addressPtr = (PUCHAR) IpAddress; + startPointer = dataBuffer; + endPointer = dataBuffer; + i = 3; + + while (i >= 0) { + // + // Collect the characters up to a '.' or the end of the string. + // + while ((*endPointer != '.') && (*endPointer != '\0')) { + endPointer++; + } + + if (startPointer == endPointer) { + return(FALSE); + } + + // + // Convert the number. + // + + for ( cp = (endPointer - 1), multiplier = 1, digit = 0; + cp >= startPointer; + cp--, multiplier *= 10 + ) { + + if ((*cp < '0') || (*cp > '9') || (multiplier > 100)) { + return(FALSE); + } + + digit += (multiplier * ((ULONG) (*cp - '0'))); + } + + if (digit > 255) { + return(FALSE); + } + + addressPtr[i] = (UCHAR) digit; + + // + // We are finished if we have found and converted 4 octets and have + // no other characters left in the string. + // + if ( (i-- == 0) && + ((*endPointer == '\0') || (*endPointer == ' ')) + ) { + return(TRUE); + } + + if (*endPointer == '\0') { + return(FALSE); + } + + startPointer = ++endPointer; + } + + return(FALSE); +} + + +ULONG +RouteMatch( + IN WCHAR *RouteString, + IN IPAddr Address, + IN IPMask Mask, + OUT IPAddr *DestVal, + OUT IPMask *DestMask, + OUT IPAddr *GateVal, + OUT ULONG *Metric + ) + +/*++ + +Routine Description + + This function checks if a perisitent route should be assigned to + a given interface based on the interface address & mask. + +Arguments + + RouteString - A NULL-terminated route laid out as Dest,Mask,Gate. + Address - The IP address of the interface being processed. + Mask - The subnet mask of the interface being processed. + DestVal - A pointer to the decoded destination IP address. + DestVal - A pointer to the decoded destination subnet mask. + DestVal - A pointer to the decoded destination first hop gateway. + Metric - A pointer to the decoded route metric. + +Return Value: + + The route type, IRE_TYPE_DIRECT or IRE_TYPE_INDIRECT, if the route + should be added to the interface, IRE_TYPE_INVALID otherwise. + +--*/ + +{ +#define ROUTE_SEPARATOR L',' + + WCHAR *labelPtr; + WCHAR *indexPtr = RouteString; + ULONG i; + UNICODE_STRING ustring; + NTSTATUS status; + BOOLEAN noMetric = FALSE; + + + PAGED_CODE(); + + // + // The route is laid out in the string as "Dest,Mask,Gateway,Metric". + // The metric may not be there if this system was upgraded from + // NT 3.51. + // + // Parse the string and convert each label. + // + + for (i=0; i<4; i++) { + + labelPtr = indexPtr; + + while (1) { + + if (*indexPtr == UNICODE_NULL) { + if ((i < 2) || (indexPtr == labelPtr)) { + return(IRE_TYPE_INVALID); + } + + if (i == 2) { + // + // Old route - no metric. + // + noMetric = TRUE; + } + + break; + } + + if (*indexPtr == ROUTE_SEPARATOR) { + *indexPtr = UNICODE_NULL; + break; + } + + indexPtr++; + } + + switch(i) { + case 0: + if (!IPConvertStringToAddress(labelPtr, DestVal)) { + return(IRE_TYPE_INVALID); + } + break; + + case 1: + if (!IPConvertStringToAddress(labelPtr, DestMask)) { + return(IRE_TYPE_INVALID); + } + break; + + case 2: + if (!IPConvertStringToAddress(labelPtr, GateVal)) { + return(IRE_TYPE_INVALID); + } + break; + + case 3: + RtlInitUnicodeString(&ustring, labelPtr); + + status = RtlUnicodeStringToInteger( + &ustring, + 0, + Metric + ); + + if (!NT_SUCCESS(status)) { + return(IRE_TYPE_INVALID); + } + + break; + + default: + ASSERT(0); + return(IRE_TYPE_INVALID); + } + + if (noMetric) { + // + // Default to 1. + // + *Metric = 1; + break; + } + + indexPtr++; + } + + if (IP_ADDR_EQUAL(*GateVal, Address)) { + return(IRE_TYPE_DIRECT); + } + + if ( IP_ADDR_EQUAL((*GateVal & Mask), (Address & Mask)) ) { + return(IRE_TYPE_INDIRECT); + } + + return(IRE_TYPE_INVALID); +} + +ULONG +GetCurrentRouteTable( + IPRouteEntry **ppRouteTable + ) +/*++ + Routine Description + Allocates memory from non paged pool and fills it with the current route table + The caller must free the memory to non-paged pool + + Arguments + ppRouteTable Pointer to pointer to array of routes + + Return Value: + Count of routes in the table. If memory is allocated, *ppRouteTable will be non NULL + +--*/ +{ + ULONG ulTableCount,ulRouteCount; + uint uiValid,uiDataLeft; + IPRouteEntry routeEntry; + CTELockHandle Handle; + UCHAR ucContext[CONTEXT_SIZE]; + +#define ROUTE_TABLE_OVERFLOW 20 // This MUST NOT be zero + + ulTableCount = IPSInfo.ipsi_numroutes + ROUTE_TABLE_OVERFLOW; + + *ppRouteTable = ExAllocatePoolWithTag(NonPagedPool, + ulTableCount * sizeof(IPRouteEntry), + 'pI'); + ulRouteCount = 0; + + if(*ppRouteTable == NULL) + { + TCPTRACE(("IP: Couldnt allocate memory for route table\n")); + } + else + { + CTEGetLock(&RouteTableLock, &Handle); + + RtlZeroMemory((PVOID)ucContext,CONTEXT_SIZE); + + uiDataLeft = RTValidateContext((PVOID)ucContext, &uiValid); + + if(!uiValid) + { + CTEFreeLock(&RouteTableLock, Handle); + } + else + { + while(uiDataLeft) + { + if(ulRouteCount < ulTableCount) + { + uiDataLeft = RTReadNext((PVOID)ucContext, &routeEntry); + + (*ppRouteTable)[ulRouteCount++] = routeEntry; + } + else + { + TCPTRACE(("IP: Couldnt read out all routes. Increase ROUTE_TABLE_OVERFLOW\n")); + } + } + + CTEFreeLock(&RouteTableLock, Handle); + } + } + + return ulRouteCount; +} + + +VOID +SetPersistentRoutesForNTE( + IPAddr Address, + IPMask Mask, + ULONG IFIndex + ) + +/*++ + +Routine Description + + Adds persistent routes that match an interface. The routes are read + from a list in the registry. + +Arguments + + Address - The address of the new interface + Mask - The subnet mask of the new interface. + IFIndex - The index of the new interface. + +Return Value: + + None. + +--*/ + +{ +#define ROUTE_DATA_STRING_SIZE (51 * sizeof(WCHAR)) +#define BASIC_INFO_SIZE ( sizeof(KEY_VALUE_BASIC_INFORMATION) - \ + sizeof(WCHAR) + ROUTE_DATA_STRING_SIZE ) + IPAddr destVal; + IPMask destMask; + IPAddr gateVal; + ULONG metric; + ULONG enumIndex = 0; + UCHAR workbuf[BASIC_INFO_SIZE]; + PKEY_VALUE_BASIC_INFORMATION basicInfo = + (PKEY_VALUE_BASIC_INFORMATION) workbuf; + ULONG resultLength; + HANDLE regKey; + WCHAR IPRoutesRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters\\PersistentRoutes"; + TDIObjectID id; + IPRouteEntry routeEntry,*pRouteTable; + ULONG ulRouteCount,i; + NTSTATUS status; + + PAGED_CODE(); + + pRouteTable = NULL; + + ulRouteCount = GetCurrentRouteTable(&pRouteTable); + + id.toi_entity.tei_entity = CL_NL_ENTITY; + id.toi_entity.tei_instance = 0; + id.toi_class = INFO_CLASS_PROTOCOL; + id.toi_type = INFO_TYPE_PROVIDER; + id.toi_id = IP_MIB_RTTABLE_ENTRY_ID; + + routeEntry.ire_index = IFIndex; + routeEntry.ire_metric2 = (ULONG) -1; + routeEntry.ire_metric3 = (ULONG) -1; + routeEntry.ire_metric4 = (ULONG) -1; + routeEntry.ire_proto = IRE_PROTO_LOCAL; + routeEntry.ire_age = 0; + routeEntry.ire_metric5 = (ULONG) -1; + + status = OpenRegKey(®Key, IPRoutesRegistryKey); + + if (NT_SUCCESS(status)) { + ULONG type; + + do { + status = ZwEnumerateValueKey( + regKey, + enumIndex, + KeyValueBasicInformation, + basicInfo, + BASIC_INFO_SIZE - sizeof(WCHAR), + &resultLength + ); + + if (!NT_SUCCESS(status)) { + if (status == STATUS_BUFFER_OVERFLOW) { + continue; + } + + break; + } + + if (basicInfo->Type != REG_SZ) { + continue; + } + + // + // Ensure NULL termination + // + basicInfo->Name[basicInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL; + basicInfo->NameLength += sizeof(WCHAR); + + type = RouteMatch( + basicInfo->Name, + Address, + Mask, + &destVal, + &destMask, + &gateVal, + &metric + ); + + if (type != IRE_TYPE_INVALID) + { + long setStatus; + ULONG ulFound; + + routeEntry.ire_dest = net_long(destVal), + routeEntry.ire_mask = net_long(destMask), + routeEntry.ire_nexthop = net_long(gateVal); + routeEntry.ire_type = type; + routeEntry.ire_metric1 = metric; + + ulFound = FALSE; + + for(i = 0; i < ulRouteCount; i++) + { + if((routeEntry.ire_dest == pRouteTable[i].ire_dest) && + (routeEntry.ire_mask == pRouteTable[i].ire_mask)) + { + ulFound = TRUE; + + break; + } + } + + if(!ulFound) + { + + setStatus = IPSetInfo( + &id, + &routeEntry, + sizeof(IPRouteEntry) + ); +#if DBG + if (setStatus != IP_SUCCESS) + { + TCPTRACE(( + "IP: set of sticky route [%x, %x, %x] failed, status %d\n", + destVal, + destMask, + gateVal, + metric, + setStatus + )); + } +#endif // DBG + } + + } + + } while (++enumIndex); + + ZwClose(regKey); + } + + if(pRouteTable) + { + ExFreePool(pRouteTable); + } +} + diff --git a/private/ntos/tdi/tcpip/ip/ntirp.c b/private/ntos/tdi/tcpip/ip/ntirp.c new file mode 100644 index 000000000..3dd34565b --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ntirp.c @@ -0,0 +1,1458 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntirp.c + +Abstract: + + NT specific routines for dispatching and handling IRPs. + +Author: + + Mike Massa (mikemas) Aug 13, 1993 + +Revision History: + + Who When What + -------- -------- ---------------------------------------------- + mikemas 08-13-93 created + +Notes: + +--*/ + +#include +#include +#include +#include +#include "ipdef.h" +#include "ipinit.h" +#include "icmp.h" +#include +#include +#include + + +// +// Local structures. +// +typedef struct pending_irp { + LIST_ENTRY Linkage; + PIRP Irp; + PFILE_OBJECT FileObject; + PVOID Context; +} PENDING_IRP, *PPENDING_IRP; + + +// +// Global variables +// +LIST_ENTRY PendingEchoList; +LIST_ENTRY PendingIPSetNTEAddrList; + + +// +// External prototypes +// +IP_STATUS +ICMPEchoRequest( + void *InputBuffer, + uint InputBufferLength, + EchoControl *ControlBlock, + EchoRtn Callback + ); + +ulong +ICMPEchoComplete( + EchoControl *ControlBlock, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ); + +IP_STATUS +IPSetNTEAddr( + uint Index, + IPAddr Addr, + IPMask Mask, + SetAddrControl *ControlBlock, + SetAddrRtn Callback + ); + +uint +IPAddDynamicNTE( + ushort InterfaceContext, + IPAddr NewAddr, + IPMask NewMask, + ushort *NTEContext, + ulong *NTEInstance + ); + +uint +IPDeleteDynamicNTE( + ushort NTEContext + ); + +uint +IPGetNTEInfo( + ushort NTEContext, + ulong *NTEInstance, + IPAddr *Address, + IPMask *SubnetMask, + ushort *NTEFlags + ); + +uint +SetDHCPNTE( + uint Context + ); + +// +// Local prototypes +// +NTSTATUS +IPDispatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +IPDispatchDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPDispatchInternalDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPCreate( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPCleanup( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +IPClose( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +DispatchEchoRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +void +CompleteEchoRequest( + void *Context, + IP_STATUS Status, + void *Data, + uint DataSize, + struct IPOptInfo *OptionInfo + ); + +NTSTATUS +DispatchIPSetNTEAddrRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +void +CompleteIPSetNTEAddrRequest( + void *Context, + IP_STATUS Status + ); + + +#ifdef _PNP_POWER +extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNPContext, void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo) ; +extern void IPDelInterface(void *Context) ; +#endif + + +// +// All of this code is pageable. +// +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, IPDispatch) +#pragma alloc_text(PAGE, IPDispatchDeviceControl) +#pragma alloc_text(PAGE, IPDispatchInternalDeviceControl) +#pragma alloc_text(PAGE, IPCreate) +#pragma alloc_text(PAGE, IPClose) +#pragma alloc_text(PAGE, DispatchEchoRequest) + +#endif // ALLOC_PRAGMA + + +// +// Dispatch function definitions +// +NTSTATUS +IPDispatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for IP. + +Arguments: + + DeviceObject - Pointer to device object for target device + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + NTSTATUS status; + + + UNREFERENCED_PARAMETER(DeviceObject); + PAGED_CODE(); + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + switch (irpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + return IPDispatchDeviceControl(Irp, irpSp); + + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + return IPDispatchDeviceControl(Irp, irpSp); + + case IRP_MJ_CREATE: + status = IPCreate(Irp, irpSp); + break; + + case IRP_MJ_CLEANUP: + status = IPCleanup(Irp, irpSp); + break; + + case IRP_MJ_CLOSE: + status = IPClose(Irp, irpSp); + break; + + default: + CTEPrint("IPDispatch: Invalid major function "); + CTEPrintNum(irpSp->MajorFunction ); + CTEPrintCRLF(); + status = STATUS_NOT_IMPLEMENTED; + break; + } + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(status); + +} // IPDispatch + + +NTSTATUS +IPDispatchDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + ULONG code; + + + PAGED_CODE(); + + Irp->IoStatus.Information = 0; + + code = IrpSp->Parameters.DeviceIoControl.IoControlCode; + + switch(code) { + + case IOCTL_ICMP_ECHO_REQUEST: + return(DispatchEchoRequest(Irp, IrpSp)); + + case IOCTL_IP_SET_ADDRESS: + return(DispatchIPSetNTEAddrRequest(Irp, IrpSp)); + + case IOCTL_IP_ADD_NTE: + { + PIP_ADD_NTE_REQUEST request; + PIP_ADD_NTE_RESPONSE response; + BOOLEAN retval; + + + request = Irp->AssociatedIrp.SystemBuffer; + response = (PIP_ADD_NTE_RESPONSE) request; + + // + // Validate input parameters + // + if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(IP_ADD_NTE_REQUEST) + ) + && + (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(IP_ADD_NTE_RESPONSE)) + + ) + { + retval = IPAddDynamicNTE( + request->InterfaceContext, + request->Address, + request->SubnetMask, + &(response->Context), + &(response->Instance) + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + Irp->IoStatus.Information = sizeof(IP_ADD_NTE_RESPONSE); + status = STATUS_SUCCESS; + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + } + break; + + case IOCTL_IP_DELETE_NTE: + { + PIP_DELETE_NTE_REQUEST request; + BOOLEAN retval; + + + request = Irp->AssociatedIrp.SystemBuffer; + + // + // Validate input parameters + // + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(IP_DELETE_NTE_REQUEST) + ) + { + retval = IPDeleteDynamicNTE( + request->Context + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + } + break; + + case IOCTL_IP_GET_NTE_INFO: + { + PIP_GET_NTE_INFO_REQUEST request; + PIP_GET_NTE_INFO_RESPONSE response; + BOOLEAN retval; + ushort nteFlags; + + + request = Irp->AssociatedIrp.SystemBuffer; + response = (PIP_GET_NTE_INFO_RESPONSE) request; + + // + // Validate input parameters + // + if ( (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(IP_GET_NTE_INFO_REQUEST) + ) + && + (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(IP_GET_NTE_INFO_RESPONSE)) + + ) + { + retval = IPGetNTEInfo( + request->Context, + &(response->Instance), + &(response->Address), + &(response->SubnetMask), + &nteFlags + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + Irp->IoStatus.Information = + sizeof(IP_GET_NTE_INFO_RESPONSE); + response->Flags = 0; + + if (nteFlags & NTE_DYNAMIC) { + response->Flags |= IP_NTE_DYNAMIC; + } + } + } + else { + status = STATUS_INVALID_PARAMETER; + } + } + break; + + case IOCTL_IP_SET_DHCP_INTERFACE: + { + PIP_SET_DHCP_INTERFACE_REQUEST request; + BOOLEAN retval; + + request = Irp->AssociatedIrp.SystemBuffer; + retval = SetDHCPNTE( + request->Context + ); + + if (retval == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + + case IOCTL_IP_SET_IF_CONTEXT: + { + PIP_SET_IF_CONTEXT_INFO info; + + + info = Irp->AssociatedIrp.SystemBuffer; + status = (NTSTATUS) SetIFContext(info->Index, info->Context); + + if (status != IP_SUCCESS) { + ASSERT(status != IP_PENDING); + // + // Map status + // + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + + case IOCTL_IP_SET_FILTER_POINTER: + { + PIP_SET_FILTER_HOOK_INFO info; + + if (Irp->RequestorMode != KernelMode) { + status = STATUS_ACCESS_DENIED; + break; + } + + info = Irp->AssociatedIrp.SystemBuffer; + status = (NTSTATUS) SetFilterPtr(info->FilterPtr); + + if (status != IP_SUCCESS) { + ASSERT(status != IP_PENDING); + // + // Map status + // + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + + case IOCTL_IP_SET_MAP_ROUTE_POINTER: + { + PIP_SET_MAP_ROUTE_HOOK_INFO info; + + if (Irp->RequestorMode != KernelMode) { + status = STATUS_ACCESS_DENIED; + break; + } + + info = Irp->AssociatedIrp.SystemBuffer; + status = (NTSTATUS) SetMapRoutePtr(info->MapRoutePtr); + + if (status != IP_SUCCESS) { + ASSERT(status != IP_PENDING); + // + // Map status + // + status = STATUS_UNSUCCESSFUL; + } + else { + status = STATUS_SUCCESS; + } + } + break; + +#ifdef _PNP_POWER + + case IOCTL_IP_GET_PNP_ARP_POINTERS: + { + PIP_GET_PNP_ARP_POINTERS info = (PIP_GET_PNP_ARP_POINTERS) Irp->AssociatedIrp.SystemBuffer; + + if (Irp->RequestorMode != KernelMode) { + status = STATUS_ACCESS_DENIED; + break; + } + + info->IPAddInterface = (IPAddInterfacePtr)IPAddInterface ; + info->IPDelInterface = (IPDelInterfacePtr)IPDelInterface ; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(IP_GET_PNP_ARP_POINTERS); + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + return STATUS_SUCCESS;; + + } + break; +#endif + + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + if (status != IP_PENDING) { + Irp->IoStatus.Status = status; + // Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + return status; + +} // IPDispatchDeviceControl + +NTSTATUS +IPDispatchInternalDeviceControl( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + + + PAGED_CODE(); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return status; + +} // IPDispatchDeviceControl + + +NTSTATUS +IPCreate( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAGED_CODE(); + + return(STATUS_SUCCESS); + +} // IPCreate + + +NTSTATUS +IPCleanup( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PPENDING_IRP pendingIrp; + PLIST_ENTRY entry, nextEntry; + KIRQL oldIrql; + LIST_ENTRY completeList; + PIRP cancelledIrp; + + + InitializeListHead(&completeList); + + // + // Collect all of the pending IRPs on this file object. + // + IoAcquireCancelSpinLock(&oldIrql); + + entry = PendingEchoList.Flink; + + while ( entry != &PendingEchoList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + + if (pendingIrp->FileObject == IrpSp->FileObject) { + nextEntry = entry->Flink; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + InsertTailList(&completeList, &(pendingIrp->Linkage)); + entry = nextEntry; + } + else { + entry = entry->Flink; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + // + // Complete them. + // + entry = completeList.Flink; + + while ( entry != &completeList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + cancelledIrp = pendingIrp->Irp; + entry = entry->Flink; + + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + cancelledIrp->IoStatus.Information = 0; + cancelledIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT); + } + + InitializeListHead(&completeList); + + // + // Collect all of the pending IRPs on this file object. + // + IoAcquireCancelSpinLock(&oldIrql); + + entry = PendingIPSetNTEAddrList.Flink; + + while ( entry != &PendingIPSetNTEAddrList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + + if (pendingIrp->FileObject == IrpSp->FileObject) { + nextEntry = entry->Flink; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + InsertTailList(&completeList, &(pendingIrp->Linkage)); + entry = nextEntry; + } + else { + entry = entry->Flink; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + // + // Complete them. + // + entry = completeList.Flink; + + while ( entry != &completeList ) { + pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + cancelledIrp = pendingIrp->Irp; + entry = entry->Flink; + + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + cancelledIrp->IoStatus.Information = 0; + cancelledIrp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT); + } + + return(STATUS_SUCCESS); + +} // IPCleanup + + +NTSTATUS +IPClose( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAGED_CODE(); + + return(STATUS_SUCCESS); + +} // IPClose + + +// +// ICMP Echo function definitions +// +VOID +CancelEchoRequest( + IN PDEVICE_OBJECT Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels an outstanding Echo request Irp. + +Arguments: + + Device - The device on which the request was issued. + Irp - Pointer to I/O request packet to cancel. + +Return Value: + + None. + +Notes: + + This function is called with cancel spinlock held. It must be + released before the function returns. + + The echo control block associated with this request cannot be + freed until the request completes. The completion routine will + free it. + +--*/ + +{ + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + + + for ( entry = PendingEchoList.Flink; + entry != &PendingEchoList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Irp == Irp) { + pendingIrp = item; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + break; + } + } + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + if (pendingIrp != NULL) { + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + return; + +} // CancelEchoRequest + +// +// IP Set Addr function definitions +// +VOID +CancelIPSetNTEAddrRequest( + IN PDEVICE_OBJECT Device, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels an outstanding IP Set Addr request Irp. + +Arguments: + + Device - The device on which the request was issued. + Irp - Pointer to I/O request packet to cancel. + +Return Value: + + None. + +Notes: + + This function is called with cancel spinlock held. It must be + released before the function returns. + + The IP Set Addr control block associated with this request cannot be + freed until the request completes. The completion routine will + free it. + +--*/ + +{ + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + + + for ( entry = PendingIPSetNTEAddrList.Flink; + entry != &PendingIPSetNTEAddrList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Irp == Irp) { + pendingIrp = item; + RemoveEntryList(entry); + IoSetCancelRoutine(pendingIrp->Irp, NULL); + break; + } + } + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + if (pendingIrp != NULL) { + // + // Free the PENDING_IRP structure. The control block will be freed + // when the request completes. + // + CTEFreeMem(pendingIrp); + + // + // Complete the IRP. + // + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + return; + +} // CancelIPSetNTEAddrRequest + + +void +CompleteEchoRequest( + void *Context, + IP_STATUS Status, + void *Data, OPTIONAL + uint DataSize, + struct IPOptInfo *OptionInfo OPTIONAL + ) + +/*++ + +Routine Description: + + Handles the completion of an ICMP Echo request + +Arguments: + + Context - Pointer to the EchoControl structure for this request. + Status - The IP status of the transmission. + Data - A pointer to data returned in the echo reply. + DataSize - The length of the returned data. + OptionInfo - A pointer to the IP options in the echo reply. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PIRP irp; + PIO_STACK_LOCATION irpSp; + EchoControl *controlBlock; + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + ULONG bytesReturned; + + + controlBlock = (EchoControl *) Context; + + // + // Find the echo request IRP on the pending list. + // + IoAcquireCancelSpinLock(&oldIrql); + + for ( entry = PendingEchoList.Flink; + entry != &PendingEchoList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Context == controlBlock) { + pendingIrp = item; + irp = pendingIrp->Irp; + IoSetCancelRoutine(irp, NULL); + RemoveEntryList(entry); + break; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + if (pendingIrp == NULL) { + // + // IRP must have been cancelled. PENDING_IRP struct + // was freed by cancel routine. Free control block. + // + CTEFreeMem(controlBlock); + return; + } + + irpSp = IoGetCurrentIrpStackLocation(irp); + + bytesReturned = ICMPEchoComplete( + controlBlock, + Status, + Data, + DataSize, + OptionInfo + ); + + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + // + // Complete the IRP. + // + irp->IoStatus.Information = (ULONG) bytesReturned; + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NETWORK_INCREMENT); + return; + +} // CompleteEchoRequest + +void +CompleteIPSetNTEAddrRequest( + void *Context, + IP_STATUS Status + ) + +/*++ + +Routine Description: + + Handles the completion of an IP Set Addr request + +Arguments: + + Context - Pointer to the SetAddrControl structure for this request. + Status - The IP status of the transmission. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PIRP irp; + PIO_STACK_LOCATION irpSp; + SetAddrControl *controlBlock; + PPENDING_IRP pendingIrp = NULL; + PPENDING_IRP item; + PLIST_ENTRY entry; + ULONG bytesReturned; + + + controlBlock = (SetAddrControl *) Context; + + // + // Find the echo request IRP on the pending list. + // + IoAcquireCancelSpinLock(&oldIrql); + + for ( entry = PendingIPSetNTEAddrList.Flink; + entry != &PendingIPSetNTEAddrList; + entry = entry->Flink + ) { + item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage); + if (item->Context == controlBlock) { + pendingIrp = item; + irp = pendingIrp->Irp; + IoSetCancelRoutine(irp, NULL); + RemoveEntryList(entry); + break; + } + } + + IoReleaseCancelSpinLock(oldIrql); + + if (pendingIrp == NULL) { + // + // IRP must have been cancelled. PENDING_IRP struct + // was freed by cancel routine. Free control block. + // + CTEFreeMem(controlBlock); + return; + } + + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + // + // Complete the IRP. + // + irp->IoStatus.Information = 0; + if (Status == IP_SUCCESS) { + irp->IoStatus.Status = STATUS_SUCCESS; + } else { + irp->IoStatus.Status = STATUS_UNSUCCESSFUL; + } + IoCompleteRequest(irp, IO_NETWORK_INCREMENT); + return; + +} // CompleteIPSetNTEAddrRequest + + +BOOLEAN +PrepareEchoIrpForCancel( + PIRP Irp, + PPENDING_IRP PendingIrp + ) +/*++ + +Routine Description: + + Prepares an Echo IRP for cancellation. + +Arguments: + + Irp - Pointer to I/O request packet to initialize for cancellation. + PendingIrp - Pointer to the PENDING_IRP structure for this IRP. + +Return Value: + + TRUE if the IRP was cancelled before this routine was called. + FALSE otherwise. + +--*/ + +{ + BOOLEAN cancelled = TRUE; + KIRQL oldIrql; + + + IoAcquireCancelSpinLock(&oldIrql); + + ASSERT(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + IoSetCancelRoutine(Irp, CancelEchoRequest); + InsertTailList(&PendingEchoList, &(PendingIrp->Linkage)); + cancelled = FALSE; + } + + IoReleaseCancelSpinLock(oldIrql); + + return(cancelled); + +} // PrepareEchoIrpForCancel + +BOOLEAN +PrepareIPSetNTEAddrIrpForCancel( + PIRP Irp, + PPENDING_IRP PendingIrp + ) +/*++ + +Routine Description: + + Prepares an IPSetNTEAddr IRP for cancellation. + +Arguments: + + Irp - Pointer to I/O request packet to initialize for cancellation. + PendingIrp - Pointer to the PENDING_IRP structure for this IRP. + +Return Value: + + TRUE if the IRP was cancelled before this routine was called. + FALSE otherwise. + +--*/ + +{ + BOOLEAN cancelled = TRUE; + KIRQL oldIrql; + + + IoAcquireCancelSpinLock(&oldIrql); + + ASSERT(Irp->CancelRoutine == NULL); + + if (!Irp->Cancel) { + IoSetCancelRoutine(Irp, CancelIPSetNTEAddrRequest); + InsertTailList(&PendingIPSetNTEAddrList, &(PendingIrp->Linkage)); + cancelled = FALSE; + } + + IoReleaseCancelSpinLock(oldIrql); + + return(cancelled); + +} // PrepareIPSetNTEAddrIrpForCancel + + +NTSTATUS +DispatchEchoRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes an ICMP request. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether NT-specific processing of the request was + successful. The status of the actual request is returned in + the request buffers. + +--*/ + +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + IP_STATUS ipStatus; + PPENDING_IRP pendingIrp; + EchoControl *controlBlock; + PICMP_ECHO_REPLY replyBuffer; + BOOLEAN cancelled; + + + PAGED_CODE(); + + pendingIrp = CTEAllocMem(sizeof(PENDING_IRP)); + + if (pendingIrp == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto echo_error; + } + + controlBlock = CTEAllocMem(sizeof(EchoControl)); + + if (controlBlock == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + CTEFreeMem(pendingIrp); + goto echo_error; + } + + pendingIrp->Irp = Irp; + pendingIrp->FileObject = IrpSp->FileObject; + pendingIrp->Context = controlBlock; + + controlBlock->ec_starttime = CTESystemUpTime(); + controlBlock->ec_replybuf = Irp->AssociatedIrp.SystemBuffer; + controlBlock->ec_replybuflen = + IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + IoMarkIrpPending(Irp); + + cancelled = PrepareEchoIrpForCancel(Irp, pendingIrp); + + if (!cancelled) { + ipStatus = ICMPEchoRequest( + Irp->AssociatedIrp.SystemBuffer, // request buf + IrpSp->Parameters.DeviceIoControl.InputBufferLength, // request len + controlBlock, // echo ctrl + CompleteEchoRequest // cmplt rtn + ); + + if (ipStatus == IP_PENDING) { + ntStatus = STATUS_PENDING; + } + else { + ASSERT(ipStatus != IP_SUCCESS); + + // + // An internal error of some kind occurred. Complete the + // request. + // + CompleteEchoRequest( + controlBlock, + ipStatus, + NULL, + 0, + NULL + ); + + // + // The NT ioctl was successful, even if the request failed. The + // request status was passed back in the first reply block. + // + ntStatus = STATUS_SUCCESS; + } + + return(ntStatus); + } + + // + // Irp has already been cancelled. + // + ntStatus = STATUS_CANCELLED; + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + +echo_error: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(ntStatus); + +} // DispatchEchoRequest + + +NTSTATUS +DispatchIPSetNTEAddrRequest( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Processes an IP Set Addr request. + +Arguments: + + Irp - Pointer to I/O request packet + IrpSp - Pointer to the current stack location in the Irp. + +Return Value: + + NTSTATUS -- Indicates whether NT-specific processing of the request was + successful. The status of the actual request is returned in + the request buffers. + +--*/ + +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + IP_STATUS ipStatus; + PPENDING_IRP pendingIrp; + SetAddrControl *controlBlock; + BOOLEAN cancelled; + + + PAGED_CODE(); + + pendingIrp = CTEAllocMem(sizeof(PENDING_IRP)); + + if (pendingIrp == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto setnteaddr_error; + } + + controlBlock = CTEAllocMem(sizeof(SetAddrControl)); + + if (controlBlock == NULL) { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + CTEFreeMem(pendingIrp); + goto setnteaddr_error; + } + + pendingIrp->Irp = Irp; + pendingIrp->FileObject = IrpSp->FileObject; + pendingIrp->Context = controlBlock; + + IoMarkIrpPending(Irp); + + cancelled = PrepareIPSetNTEAddrIrpForCancel(Irp, pendingIrp); + + if (!cancelled) { + + PIP_SET_ADDRESS_REQUEST request; + + request = Irp->AssociatedIrp.SystemBuffer; + ipStatus = IPSetNTEAddr( + request->Context, + request->Address, + request->SubnetMask, + controlBlock, + CompleteIPSetNTEAddrRequest + ); + + if (ipStatus == IP_PENDING) { + ntStatus = STATUS_PENDING; + } + else { + + // + // A request completed which did not pend. + // + CompleteIPSetNTEAddrRequest( + controlBlock, + ipStatus + ); + + // + // The NT ioctl was successful, even if the request failed. The + // request status was passed back in the first reply block. + // + ntStatus = STATUS_SUCCESS; + } + + return(ntStatus); + } + + // + // Irp has already been cancelled. + // + ntStatus = STATUS_CANCELLED; + CTEFreeMem(pendingIrp); + CTEFreeMem(controlBlock); + + +setnteaddr_error: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = ntStatus; + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return(ntStatus); + +} // DispatchIPSetNTEAddrRequest diff --git a/private/ntos/tdi/tcpip/ip/ntreg.c b/private/ntos/tdi/tcpip/ip/ntreg.c new file mode 100644 index 000000000..e9c53c208 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/ntreg.c @@ -0,0 +1,672 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ntreg.c + +Abstract: + + This source file contains the routines to to access the NT Registry for + configuration info. + + +Author: + + Mike Massa (mikemas) September 3, 1993 + + (taken from routines by jballard) + +Revision History: + +--*/ + + +#include +#include +#include + + +#define WORK_BUFFER_SIZE 512 + + +// +// Local function prototypes +// +NTSTATUS +OpenRegKey( + PHANDLE HandlePtr, + PWCHAR KeyName + ); + +NTSTATUS +GetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ); + +NTSTATUS +GetRegStringValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PKEY_VALUE_PARTIAL_INFORMATION *ValueData, + PUSHORT ValueSize + ); + +NTSTATUS +GetRegSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData, + PULONG ValueType + ); + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ); + +VOID +InitRegDWORDParameter( + HANDLE RegKey, + PWCHAR ValueName, + ULONG *Value, + ULONG DefaultValue + ); + + +#ifdef ALLOC_PRAGMA +// +// All of the init code can be discarded +// + +#ifndef _PNP_POWER + +#pragma alloc_text(INIT, GetRegDWORDValue) +#pragma alloc_text(INIT, SetRegDWORDValue) +#pragma alloc_text(INIT, InitRegDWORDParameter) + +#else // _PNP_POWER + +#pragma alloc_text(PAGE, GetRegDWORDValue) +#pragma alloc_text(PAGE, SetRegDWORDValue) +#pragma alloc_text(PAGE, InitRegDWORDParameter) + +#endif // _PNP_POWER + +// +// This code is pagable. +// +#pragma alloc_text(PAGE, OpenRegKey) +#pragma alloc_text(PAGE, GetRegStringValue) +#pragma alloc_text(PAGE, GetRegSZValue) +#pragma alloc_text(PAGE, GetRegMultiSZValue) + + +#endif // ALLOC_PRAGMA + + + +// +// Function definitions +// +NTSTATUS +OpenRegKey( + PHANDLE HandlePtr, + PWCHAR KeyName + ) + +/*++ + +Routine Description: + + Opens a Registry key and returns a handle to it. + +Arguments: + + HandlePtr - The varible into which to write the opened handle. + KeyName - The name of the Registry key to open. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING UKeyName; + + + PAGED_CODE(); + + RtlInitUnicodeString(&UKeyName, KeyName); + + memset(&ObjectAttributes, 0, sizeof(OBJECT_ATTRIBUTES)); + InitializeObjectAttributes(&ObjectAttributes, + &UKeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenKey(HandlePtr, + KEY_READ, + &ObjectAttributes); + + return Status; +} + + + +NTSTATUS +GetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ) + +/*++ + +Routine Description: + + Reads a REG_DWORD value from the registry into the supplied variable. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - The variable into which to read the data. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_FULL_INFORMATION keyValueFullInformation; + UCHAR keybuf[WORK_BUFFER_SIZE]; + UNICODE_STRING UValueName; + + +#ifdef _PNP_POWER + PAGED_CODE(); +#endif // _PNP_POWER + + RtlInitUnicodeString(&UValueName, ValueName); + + keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf; + RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation)); + + + status = ZwQueryValueKey(KeyHandle, + &UValueName, + KeyValueFullInformation, + keyValueFullInformation, + WORK_BUFFER_SIZE, + &resultLength); + + if (NT_SUCCESS(status)) { + if (keyValueFullInformation->Type != REG_DWORD) { + status = STATUS_INVALID_PARAMETER_MIX; + } else { + *ValueData = *((ULONG UNALIGNED *)((PCHAR)keyValueFullInformation + + keyValueFullInformation->DataOffset)); + } + } + + return status; +} + + + +NTSTATUS +SetRegDWORDValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PULONG ValueData + ) + +/*++ + +Routine Description: + + Writes the contents of a variable to a REG_DWORD value. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to write. + ValueName - The name of the value to write. + ValueData - The variable from which to write the data. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING UValueName; + + +#ifdef _PNP_POWER + PAGED_CODE(); +#endif // _PNP_POWER + + RtlInitUnicodeString(&UValueName, ValueName); + + status = ZwSetValueKey(KeyHandle, + &UValueName, + 0, + REG_DWORD, + ValueData, + sizeof(ULONG)); + + return status; +} + + + +NTSTATUS +GetRegStringValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PKEY_VALUE_PARTIAL_INFORMATION *ValueData, + PUSHORT ValueSize + ) + +/*++ + +Routine Description: + + Reads a REG_*_SZ string value from the Registry into the supplied + key value buffer. If the buffer string buffer is not large enough, + it is reallocated. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - Destination for the read data. + ValueSize - Size of the ValueData buffer. Updated on output. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING UValueName; + + + PAGED_CODE(); + + RtlInitUnicodeString(&UValueName, ValueName); + + status = ZwQueryValueKey( + KeyHandle, + &UValueName, + KeyValuePartialInformation, + *ValueData, + (ULONG) *ValueSize, + &resultLength + ); + + if ( (status == STATUS_BUFFER_OVERFLOW) || + (status == STATUS_BUFFER_TOO_SMALL) + ) + { + PVOID temp; + + // + // Free the old buffer and allocate a new one of the + // appropriate size. + // + + ASSERT(resultLength > (ULONG) *ValueSize); + + if (resultLength <= 0xFFFF) { + + temp = CTEAllocMem(resultLength); + + if (temp != NULL) { + + if (*ValueData != NULL) { + CTEFreeMem(*ValueData); + } + + *ValueData = temp; + *ValueSize = (USHORT) resultLength; + + status = ZwQueryValueKey(KeyHandle, + &UValueName, + KeyValuePartialInformation, + *ValueData, + *ValueSize, + &resultLength + ); + + ASSERT( (status != STATUS_BUFFER_OVERFLOW) && + (status != STATUS_BUFFER_TOO_SMALL) + ); + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + return(status); +} + + + +NTSTATUS +GetRegMultiSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData + ) + +/*++ + +Routine Description: + + Reads a REG_MULTI_SZ string value from the Registry into the supplied + Unicode string. If the Unicode string buffer is not large enough, + it is reallocated. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - Destination Unicode string for the value data. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation; + UNICODE_STRING UValueName; + + + PAGED_CODE(); + + ValueData->Length = 0; + + status = GetRegStringValue( + KeyHandle, + ValueName, + (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer), + &(ValueData->MaximumLength) + ); + + if (NT_SUCCESS(status)) { + + keyValuePartialInformation = + (PKEY_VALUE_PARTIAL_INFORMATION) ValueData->Buffer; + + if (keyValuePartialInformation->Type == REG_MULTI_SZ) { + + ValueData->Length = (USHORT) + keyValuePartialInformation->DataLength; + + RtlCopyMemory( + ValueData->Buffer, + &(keyValuePartialInformation->Data), + ValueData->Length + ); + } + else { + status = STATUS_INVALID_PARAMETER_MIX; + } + } + + return status; + +} // GetRegMultiSZValue + + + +NTSTATUS +GetRegSZValue( + HANDLE KeyHandle, + PWCHAR ValueName, + PUNICODE_STRING ValueData, + PULONG ValueType + ) + +/*++ + +Routine Description: + + Reads a REG_SZ string value from the Registry into the supplied + Unicode string. If the Unicode string buffer is not large enough, + it is reallocated. + +Arguments: + + KeyHandle - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + ValueData - Destination Unicode string for the value data. + ValueType - On return, contains the Registry type of the value read. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + ULONG resultLength; + PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation; + UNICODE_STRING UValueName; + + + PAGED_CODE(); + + ValueData->Length = 0; + + status = GetRegStringValue( + KeyHandle, + ValueName, + (PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer), + &(ValueData->MaximumLength) + ); + + if (NT_SUCCESS(status)) { + + keyValuePartialInformation = + (PKEY_VALUE_PARTIAL_INFORMATION)ValueData->Buffer; + + if ((keyValuePartialInformation->Type == REG_SZ) || + (keyValuePartialInformation->Type == REG_EXPAND_SZ) + ) + { + WCHAR *src; + WCHAR *dst; + ULONG dataLength; + + + *ValueType = keyValuePartialInformation->Type; + dataLength = keyValuePartialInformation->DataLength; + + ASSERT(dataLength <= ValueData->MaximumLength); + + dst = ValueData->Buffer; + src = (PWCHAR) &(keyValuePartialInformation->Data); + + while (ValueData->Length <= dataLength) { + + if ( (*dst++ = *src++) == UNICODE_NULL ) { + break; + } + + ValueData->Length += sizeof(WCHAR); + } + + if (ValueData->Length < (ValueData->MaximumLength - 1)) { + ValueData->Buffer[ValueData->Length/sizeof(WCHAR)] = + UNICODE_NULL; + } + } + else { + status = STATUS_INVALID_PARAMETER_MIX; + } + } + + return status; +} + + + +VOID +InitRegDWORDParameter( + HANDLE RegKey, + PWCHAR ValueName, + ULONG *Value, + ULONG DefaultValue + ) + +/*++ + +Routine Description: + + Reads a REG_DWORD parameter from the Registry into a variable. If the + read fails, the variable is initialized to a default. + +Arguments: + + RegKey - Open handle to the parent key of the value to read. + ValueName - The name of the value to read. + Value - Destination variable into which to read the data. + DefaultValue - Default to assign if the read fails. + +Return Value: + + STATUS_SUCCESS or an appropriate failure code. + +--*/ + +{ + NTSTATUS status; + + +#ifdef _PNP_POWER + PAGED_CODE(); +#endif // _PNP_POWER + + status = GetRegDWORDValue( + RegKey, + ValueName, + Value + ); + + if (!NT_SUCCESS(status)) { + // + // These registry parameters override the defaults, so their + // absence is not an error. + // + *Value = DefaultValue; + } + + return; +} + + +PWCHAR +EnumRegMultiSz( + IN PWCHAR MszString, + IN ULONG MszStringLength, + IN ULONG StringIndex + ) +/*++ + + Routine Description: + + Parses a REG_MULTI_SZ string and returns the specified substring. + + Arguments: + + MszString - A pointer to the REG_MULTI_SZ string. + + MszStringLength - The length of the REG_MULTI_SZ string, including the + terminating null character. + + StringIndex - Index number of the substring to return. Specifiying + index 0 retrieves the first substring. + + Return Value: + + A pointer to the specified substring. + + Notes: + + This code is called at raised IRQL. It is not pageable. + +--*/ +{ + PWCHAR string = MszString; + + if ( MszStringLength < (2 * sizeof(WCHAR)) ) { + return(NULL); + } + + // + // Find the start of the desired string. + // + while (StringIndex) { + + while (MszStringLength >= sizeof(WCHAR)) { + MszStringLength -= sizeof(WCHAR); + + if (*string++ == UNICODE_NULL) { + break; + } + } + + // + // Check for index out of range. + // + if ( MszStringLength < (2 * sizeof(UNICODE_NULL)) ) { + return(NULL); + } + + StringIndex--; + } + + if ( MszStringLength < (2 * sizeof(UNICODE_NULL)) ) { + return(NULL); + } + + return(string); +} + + diff --git a/private/ntos/tdi/tcpip/ip/sources.inc b/private/ntos/tdi/tcpip/ip/sources.inc new file mode 100644 index 000000000..87a099a83 --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/sources.inc @@ -0,0 +1,59 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=ip + +NTPROFILEINPUT=yes + +TARGETNAME=ip +TARGETPATH=obj +TARGETTYPE=LIBRARY + +TARGETLIBS= + +INCLUDES=..\..\..\..\inc;..\..\..\..\..\inc;..\..\h + +C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ -D_PNP_POWER -DSECFLTR + +!IFDEF BUILD_FOR_3_51 +C_DEFINES= $(C_DEFINES) -D_NTIFS_ +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES= \ + ..\arp.c \ + ..\icmp.c \ + ..\igmp.c \ + ..\info.c \ + ..\init.c \ + ..\iploop.c \ + ..\iprcv.c \ + ..\iproute.c \ + ..\ipstatus.c \ + ..\ipxmit.c \ + ..\ntip.c \ + ..\ntirp.c \ + ..\ntreg.c diff --git a/private/ntos/tdi/tcpip/ip/up/makefile b/private/ntos/tdi/tcpip/ip/up/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/up/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/tcpip/ip/up/sources b/private/ntos/tdi/tcpip/ip/up/sources new file mode 100644 index 000000000..91036a15a --- /dev/null +++ b/private/ntos/tdi/tcpip/ip/up/sources @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +UP_DRIVER=yes + +!include ..\sources.inc -- cgit v1.2.3