summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/tcpip/ip
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/tcpip/ip
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/tcpip/ip/arp.c4839
-rw-r--r--private/ntos/tdi/tcpip/ip/arp.h21
-rw-r--r--private/ntos/tdi/tcpip/ip/arpdef.h340
-rw-r--r--private/ntos/tdi/tcpip/ip/dirs22
-rw-r--r--private/ntos/tdi/tcpip/ip/icmp.c1698
-rw-r--r--private/ntos/tdi/tcpip/ip/icmp.h70
-rw-r--r--private/ntos/tdi/tcpip/ip/igmp.c799
-rw-r--r--private/ntos/tdi/tcpip/ip/igmp.h42
-rw-r--r--private/ntos/tdi/tcpip/ip/info.c609
-rw-r--r--private/ntos/tdi/tcpip/ip/info.h22
-rw-r--r--private/ntos/tdi/tcpip/ip/init.c3509
-rw-r--r--private/ntos/tdi/tcpip/ip/ipdef.h371
-rw-r--r--private/ntos/tdi/tcpip/ip/ipinit.h155
-rw-r--r--private/ntos/tdi/tcpip/ip/iploop.c665
-rw-r--r--private/ntos/tdi/tcpip/ip/iprcv.c1145
-rw-r--r--private/ntos/tdi/tcpip/ip/iproute.c4703
-rw-r--r--private/ntos/tdi/tcpip/ip/iproute.h107
-rw-r--r--private/ntos/tdi/tcpip/ip/iprtdef.h135
-rw-r--r--private/ntos/tdi/tcpip/ip/ipstatus.c205
-rw-r--r--private/ntos/tdi/tcpip/ip/ipxmit.c1949
-rw-r--r--private/ntos/tdi/tcpip/ip/ipxmit.h34
-rw-r--r--private/ntos/tdi/tcpip/ip/mp/makefile6
-rw-r--r--private/ntos/tdi/tcpip/ip/mp/sources27
-rw-r--r--private/ntos/tdi/tcpip/ip/ntip.c3361
-rw-r--r--private/ntos/tdi/tcpip/ip/ntirp.c1458
-rw-r--r--private/ntos/tdi/tcpip/ip/ntreg.c672
-rw-r--r--private/ntos/tdi/tcpip/ip/sources.inc59
-rw-r--r--private/ntos/tdi/tcpip/ip/up/makefile6
-rw-r--r--private/ntos/tdi/tcpip/ip/up/sources27
29 files changed, 27056 insertions, 0 deletions
diff --git a/private/ntos/tdi/tcpip/ip/arp.c b/private/ntos/tdi/tcpip/ip/arp.c
new file mode 100644
index 000000000..3c8253566
--- /dev/null
+++ b/private/ntos/tdi/tcpip/ip/arp.c
@@ -0,0 +1,4839 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1990-1992 **/
+/********************************************************************/
+/* :ts=4 */
+
+//*** arp.c - ARP VxD routines.
+//
+// This file containes all of the ARP related routines, including
+// table lookup, registration, etc.
+//
+// ARP is architected to support multiple protocols, but for now
+// it in only implemented to take one protocol (IP). This is done
+// for simplicity and ease of implementation. In the future we may
+// split ARP out into a seperate driver.
+
+#include "oscfg.h"
+#ifdef VXD
+#include <string.h>
+#endif
+#include "ndis.h"
+#include "cxport.h"
+#include "ip.h"
+#include "ipdef.h"
+#include "llipif.h"
+#include "arp.h"
+#include "arpdef.h"
+#include "tdiinfo.h"
+#include "ipinfo.h"
+#include "llinfo.h"
+#include "tdistat.h"
+#include "iproute.h"
+#include "iprtdef.h"
+#include "arpinfo.h"
+#include "ipinit.h"
+
+#ifndef CHICAGO
+#ifndef _PNP_POWER
+#define NDIS_MAJOR_VERSION 0x03
+#define NDIS_MINOR_VERSION 0
+#else
+#define NDIS_MAJOR_VERSION 0x04
+#define NDIS_MINOR_VERSION 0
+#endif
+#endif
+
+#ifndef NDIS_API
+#define NDIS_API
+#endif
+
+
+static ulong ARPLookahead = LOOKAHEAD_SIZE;
+
+static uchar ENetBcst[] = "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x06";
+static uchar TRBcst[] = "\x10\x40\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x82\x70";
+static uchar FDDIBcst[] = "\x57\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00";
+static uchar ARCBcst[] = "\x00\x00\xd5";
+
+static uchar ENetMcst[] = "\x01\x00\x5E\x00\x00\x00";
+static uchar FDDIMcst[] = "\x57\x01\x00\x5E\x00\x00\x00";
+static uchar ARPSNAP[] = "\xAA\xAA\x03\x00\x00\x00\x08\x06";
+
+#ifdef NT
+static WCHAR ARPName[] = TCP_NAME;
+#else // NT
+static uchar ARPName[] = TCP_NAME;
+#endif // NT
+
+NDIS_HANDLE ARPHandle; // Our NDIS protocol handle.
+
+uint ArpCacheLife;
+uint sArpAlwaysSourceRoute; // True if we always send ARP requests
+ // with source route info on token ring.
+uint sIPAlwaysSourceRoute;
+extern uchar TrRii;
+
+extern PDRIVER_OBJECT IPDriverObject;
+
+extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint);
+extern void IPTDComplete(void *, PNDIS_PACKET, NDIS_STATUS, uint);
+extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS);
+extern void IPStatus(void *, NDIS_STATUS, void *, uint);
+extern void IPRcvComplete(void);
+extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
+ uint *StartOffset);
+
+extern void NDIS_API ARPSendComplete(NDIS_HANDLE, PNDIS_PACKET, NDIS_STATUS);
+extern void IPULUnloadNotify(void);
+
+#ifdef _PNP_POWER
+extern IP_STATUS IPAddInterface(PNDIS_STRING ConfigName, void *PNP,
+ void *Context, LLIPRegRtn RegRtn, LLIPBindInfo *BindInfo);
+extern void IPDelInterface(void *Context);
+
+extern void NotifyOfUnload(void);
+
+
+extern uint OpenIFConfig(PNDIS_STRING ConfigName, NDIS_HANDLE *Handle);
+extern int IsLLInterfaceValueNull (NDIS_HANDLE Handle) ;
+extern void CloseIFConfig(NDIS_HANDLE Handle);
+
+#endif
+
+
+// Tables for bitswapping.
+
+uchar SwapTableLo[] = {
+ 0, // 0
+ 0x08, // 1
+ 0x04, // 2
+ 0x0c, // 3
+ 0x02, // 4
+ 0x0a, // 5,
+ 0x06, // 6,
+ 0x0e, // 7,
+ 0x01, // 8,
+ 0x09, // 9,
+ 0x05, // 10,
+ 0x0d, // 11,
+ 0x03, // 12,
+ 0x0b, // 13,
+ 0x07, // 14,
+ 0x0f // 15
+};
+
+uchar SwapTableHi[] = {
+ 0, // 0
+ 0x80, // 1
+ 0x40, // 2
+ 0xc0, // 3
+ 0x20, // 4
+ 0xa0, // 5,
+ 0x60, // 6,
+ 0xe0, // 7,
+ 0x10, // 8,
+ 0x90, // 9,
+ 0x50, // 10,
+ 0xd0, // 11,
+ 0x30, // 12,
+ 0xb0, // 13,
+ 0x70, // 14,
+ 0xf0 // 15
+};
+
+// Table of source route maximum I-field lengths for token ring.
+ushort IFieldSize[] = {
+ 516,
+ 1500,
+ 2052,
+ 4472,
+ 8191
+};
+
+#define LF_BIT_SHIFT 4
+#define MAX_LF_BITS 4
+
+#ifdef NT
+#ifdef ALLOC_PRAGMA
+//
+// Disposable init code.
+//
+void FreeARPInterface(ARPInterface *Interface);
+void ARPOpen(void *Context);
+
+#pragma alloc_text(INIT, ARPInit)
+#ifndef _PNP_POWER
+#pragma alloc_text(INIT, FreeARPInterface)
+#pragma alloc_text(INIT, ARPOpen)
+#pragma alloc_text(INIT, ARPRegister)
+#else
+#pragma alloc_text(PAGE, ARPOpen)
+#pragma alloc_text(PAGE, ARPRegister)
+
+#endif
+
+//
+// Paged code
+//
+void NotifyConflictProc(CTEEvent *Event, void *Context);
+
+#pragma alloc_text(PAGE, NotifyConflictProc)
+
+#endif // ALLOC_PRAGMA
+#endif // NT
+
+#ifdef VXD
+extern void EnableInts(void);
+#endif
+
+//* DoNDISRequest - Submit a request to an NDIS driver.
+//
+// This is a utility routine to submit a general request to an NDIS
+// driver. The caller specifes the request code (OID), a buffer and
+// a length. This routine allocates a request structure,
+// fills it in, and submits the request.
+//
+// Entry:
+// Adapter - A pointer to the ARPInterface adapter structure.
+// Request - Type of request to be done (Set or Query)
+// OID - Value to be set/queried.
+// Info - A pointer to the buffer to be passed.
+// Length - Length of data in the buffer.
+// Needed - On return, filled in with bytes needed in buffer.
+//
+// Exit:
+//
+NDIS_STATUS
+DoNDISRequest(ARPInterface *Adapter, NDIS_REQUEST_TYPE RT, NDIS_OID OID,
+ void *Info, uint Length, uint *Needed)
+{
+ NDIS_REQUEST Request; // Request structure we'll use.
+ NDIS_STATUS Status;
+
+ // Now fill it in.
+ Request.RequestType = RT;
+ if (RT == NdisRequestSetInformation) {
+ Request.DATA.SET_INFORMATION.Oid = OID;
+ Request.DATA.SET_INFORMATION.InformationBuffer = Info;
+ Request.DATA.SET_INFORMATION.InformationBufferLength = Length;
+ } else {
+ Request.DATA.QUERY_INFORMATION.Oid = OID;
+ Request.DATA.QUERY_INFORMATION.InformationBuffer = Info;
+ Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length;
+ }
+
+ // Initialize the block structure.
+ CTEInitBlockStruc(&Adapter->ai_block);
+#ifdef VXD
+ EnableInts();
+#endif
+
+ // Submit the request.
+ NdisRequest(&Status, Adapter->ai_handle, &Request);
+
+ // Wait for it to finish
+ if (Status == NDIS_STATUS_PENDING)
+ Status = (NDIS_STATUS)CTEBlock(&Adapter->ai_block);
+
+ if (Needed != NULL)
+ *Needed = Request.DATA.QUERY_INFORMATION.BytesNeeded;
+
+ return Status;
+}
+//* FreeARPBuffer - Free a header and buffer descriptor pair.
+//
+// Called when we're done with a buffer. We'll free the buffer and the
+// buffer descriptor pack to the interface.
+//
+// Entry: Interface - Interface buffer/bd came frome.
+// Buffer - NDIS_BUFFER to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeARPBuffer(ARPInterface *Interface, PNDIS_BUFFER Buffer)
+{
+ CTELockHandle lhandle;
+ uchar **Header; // header buffer to be freed.
+ uint Size;
+
+ Size = NdisBufferLength(Buffer);
+
+ if (Size <= Interface->ai_sbsize) {
+#ifdef VXD
+ // A small buffer, put him on the list.
+ NDIS_BUFFER_LINKAGE(Buffer) = Interface->ai_sblist;
+ Interface->ai_sblist = Buffer;
+#else
+ ExInterlockedPushEntrySList(
+ &Interface->ai_sblist,
+ STRUCT_OF(SINGLE_LIST_ENTRY, &(Buffer->Next), Next),
+ &Interface->ai_lock
+ );
+
+#endif
+
+ return;
+ } else {
+ // A big buffer. Get the buffer pointer, link it on, and free the
+ // NDIS buffer.
+ Header = (uchar **)NdisBufferVirtualAddress(Buffer);
+
+ CTEGetLock(&Interface->ai_lock, &lhandle);
+ *Header = Interface->ai_bblist;
+ Interface->ai_bblist = (uchar *)Header;
+ CTEFreeLock(&Interface->ai_lock, lhandle);
+
+ NdisFreeBuffer(Buffer);
+ }
+}
+
+//* GrowARPHeaders - Grow the ARP header buffer list.
+//
+// Called when we need to grow the ARP header buffer list. Called with the
+// interface lock held.
+//
+// Input: Interface - Interface on which to grow.
+//
+// Returns: Pointer to newly allocated buffer, or NULL.
+//
+PNDIS_BUFFER
+GrowARPHeaders(ARPInterface *Interface)
+{
+ ARPBufferTracker *NewTracker;
+ PNDIS_BUFFER Buffer, ReturnBuffer;
+ uchar *Header;
+ uint i;
+ NDIS_STATUS Status;
+ CTELockHandle Handle;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ // Make sure we're allowed to allocate.
+ if (Interface->ai_curhdrs >= Interface->ai_maxhdrs)
+ goto failure;
+
+ NewTracker = CTEAllocMem(sizeof(ARPBufferTracker));
+ if (NewTracker == NULL)
+ goto failure; // We're out of memory.
+
+ NdisAllocateBufferPool(&Status, &NewTracker->abt_handle,
+ ARP_HDRBUF_GROW_SIZE);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEFreeMem(NewTracker);
+ goto failure;
+ }
+
+ Header = CTEAllocMem((uint)Interface->ai_sbsize * ARP_HDRBUF_GROW_SIZE);
+ if (Header == NULL) {
+ NdisFreeBufferPool(NewTracker->abt_handle);
+ CTEFreeMem(NewTracker);
+ goto failure;
+ }
+
+ // Got the resources we need, allocate the buffers.
+ NewTracker->abt_buffer = Header;
+ NewTracker->abt_next = Interface->ai_buflist;
+ Interface->ai_buflist = NewTracker;
+ ReturnBuffer = NULL;
+ Interface->ai_curhdrs += ARP_HDRBUF_GROW_SIZE;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ for (i = 0; i < ARP_HDRBUF_GROW_SIZE; i++) {
+ NdisAllocateBuffer(&Status, &Buffer, NewTracker->abt_handle,
+ Header + (i * Interface->ai_sbsize), Interface->ai_sbsize);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ CTEAssert(FALSE);
+ break;
+ }
+ if (i != 0) {
+ FreeARPBuffer(Interface, Buffer);
+ } else
+ ReturnBuffer = Buffer;
+ }
+
+ // Update for what we didn't allocate, if any.
+ CTEInterlockedAddUlong(&Interface->ai_curhdrs, i - ARP_HDRBUF_GROW_SIZE,
+ &Interface->ai_lock);
+
+ return ReturnBuffer;
+
+failure:
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return NULL;
+}
+
+//* GetARPBuffer - Get a buffer and descriptor
+//
+// Returns a pointer to an NDIS_BUFFER and a pointer to a buffer
+// of the specified size.
+//
+// Entry: Interface - Pointer to ARPInterface structure to allocate buffer from.
+// BufPtr - Pointer to where to return buf address.
+// Size - Size in bytes of buffer needed.
+//
+// Returns: Pointer to NDIS_BUFFER if successfull, NULL if not
+//
+PNDIS_BUFFER
+GetARPBuffer(ARPInterface *Interface, uchar **BufPtr, uchar size)
+{
+ CTELockHandle lhandle; // Lock handle
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer; // NDIS buffer allocated.
+
+ if (size <= Interface->ai_sbsize) {
+#ifdef VXD
+ Buffer = Interface->ai_sblist;
+ if (Buffer != NULL) {
+ Interface->ai_sblist = NDIS_BUFFER_LINKAGE(Buffer);
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+ NdisBufferLength(Buffer) = size;
+ *BufPtr = NdisBufferVirtualAddress(Buffer);
+ return Buffer;
+#else
+ PSINGLE_LIST_ENTRY BufferLink;
+
+ BufferLink = ExInterlockedPopEntrySList(
+ &Interface->ai_sblist,
+ &Interface->ai_lock
+ );
+ if (BufferLink != NULL) {
+ Buffer = STRUCT_OF(NDIS_BUFFER, BufferLink, Next);
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+ NdisBufferLength(Buffer) = size;
+ *BufPtr = NdisBufferVirtualAddress(Buffer);
+ return Buffer;
+#endif
+
+ } else {
+ Buffer = GrowARPHeaders(Interface);
+ if (Buffer != NULL) {
+ NDIS_BUFFER_LINKAGE(Buffer) = NULL;
+ NdisBufferLength(Buffer) = size;
+ *BufPtr = NdisBufferVirtualAddress(Buffer);
+ }
+ return Buffer;
+ }
+ } else {
+ // Need a 'big' buffer.
+ CTEGetLock(&Interface->ai_lock, &lhandle);
+ if ((*BufPtr = Interface->ai_bblist) != (uchar *)NULL) {
+ Interface->ai_bblist = *(uchar **)*BufPtr;
+ CTEFreeLock(&Interface->ai_lock, lhandle); // Got a buffer.
+ NdisAllocateBuffer(&Status, &Buffer, Interface->ai_bpool, *BufPtr,
+ size);
+ if (Status == NDIS_STATUS_SUCCESS)
+ return Buffer;
+ else { // Couldn't get NDIS buffer, free our buffer.
+ CTEGetLock(&Interface->ai_lock, &lhandle);
+ *(uchar **)&**BufPtr = Interface->ai_bblist;
+ Interface->ai_bblist = *BufPtr;
+ CTEFreeLock(&Interface->ai_lock, lhandle);
+ return (PNDIS_BUFFER)NULL;
+ }
+ }
+
+ // Couldn't get a header buffer, free lock and return NULL.
+ CTEFreeLock(&Interface->ai_lock, lhandle);
+ return (PNDIS_BUFFER)NULL;
+ }
+}
+
+
+//* BitSwap - Bit swap two strings.
+//
+// A routine to bitswap two strings.
+//
+// Input: Dest - Destination of swap.
+// Src - Src string to be swapped.
+// Length - Length in bytes to swap.
+//
+// Returns: Nothing.
+//
+void
+BitSwap(uchar *Dest, uchar *Src, uint Length)
+{
+ uint i;
+ uchar Temp, TempSrc;
+
+ for (i = 0; i < Length; i++, Dest++, Src++) {
+ TempSrc = *Src;
+ Temp = SwapTableLo[TempSrc >> 4] | SwapTableHi[TempSrc & 0x0f];
+ *Dest = Temp;
+ }
+
+}
+
+
+//* SendARPPacket - Build a header, and send a packet.
+//
+// A utility routine to build and ARP header and send a packet. We assume
+// the media specific header has been built.
+//
+// Entry: Interface - Interface for NDIS drive.
+// Packet - Pointer to packet to be sent
+// Header - Pointer to header to fill in.
+// Opcode - Opcode for packet.
+// Address - Source HW address.
+// SrcAddr - Address to use as our source h/w address.
+// Destination - Destination IP address.
+// Src - Source IP address.
+// HWType - Hardware type.
+// CheckIF - TRUE iff we are to check the I/F status before
+// sending.
+//
+// Returns: NDIS_STATUS of send.
+//
+NDIS_STATUS
+SendARPPacket(ARPInterface *Interface, PNDIS_PACKET Packet, ARPHeader *Header, ushort Opcode,
+ uchar *Address, uchar *SrcAddr, IPAddr Destination, IPAddr Src,
+ ushort HWType, uint CheckIF)
+{
+ NDIS_STATUS Status;
+ PNDIS_BUFFER Buffer;
+ uint PacketDone;
+ uchar *AddrPtr;
+
+ Header->ah_hw = HWType;
+ Header->ah_pro = net_short(ARP_ETYPE_IP);
+ Header->ah_hlen = Interface->ai_addrlen;
+ Header->ah_plen = sizeof(IPAddr);
+ Header->ah_opcode = Opcode;
+ AddrPtr = Header->ah_shaddr;
+
+ if (SrcAddr == NULL)
+ SrcAddr = Interface->ai_addr;
+
+ CTEMemCopy(AddrPtr, SrcAddr, Interface->ai_addrlen);
+
+ AddrPtr += Interface->ai_addrlen;
+ *(IPAddr UNALIGNED *)AddrPtr = Src;
+ AddrPtr += sizeof(IPAddr);
+
+ if (Address != (uchar *)NULL)
+ CTEMemCopy(AddrPtr, Address, Interface->ai_addrlen);
+ else
+ CTEMemSet(AddrPtr, 0, Interface->ai_addrlen);
+
+ AddrPtr += Interface->ai_addrlen;
+ *(IPAddr UNALIGNED *)AddrPtr = Destination;
+
+ PacketDone = FALSE;
+
+ if (!CheckIF || Interface->ai_state == INTERFACE_UP) {
+
+ Interface->ai_qlen++;
+ NdisSend(&Status, Interface->ai_handle, Packet);
+
+ if (Status != NDIS_STATUS_PENDING) {
+ PacketDone = TRUE;
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+ if (Status == NDIS_STATUS_SUCCESS)
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ else {
+ if (Status == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+ }
+ } else {
+ PacketDone = TRUE;
+ Status = NDIS_STATUS_ADAPTER_NOT_READY;
+ }
+
+ if (PacketDone) {
+ NdisUnchainBufferAtFront(Packet, &Buffer);
+ FreeARPBuffer(Interface, Buffer);
+ NdisFreePacket(Packet);
+ }
+ return Status;
+}
+
+//* SendARPRequest - Send an ARP packet
+//
+// Called when we need to ARP an IP address, or respond to a request. We'll send out
+// the packet, and the receiving routines will process the response.
+//
+// Entry: Interface - Interface to send the request on.
+// Destination - The IP address to be ARPed.
+// Type - Either RESOLVING_GLOBAL or RESOLVING_LOCAL
+// SrcAddr - NULL if we're sending from ourselves, the value
+// to use otherwise.
+// CheckIF - Flag passed through to SendARPPacket().
+//
+// Returns: Status of attempt to send ARP request.
+//
+NDIS_STATUS
+SendARPRequest(ARPInterface *Interface, IPAddr Destination, uchar Type,
+ uchar *SrcAddr, uint CheckIF)
+{
+ uchar *MHeader; // Pointer to media header.
+ PNDIS_BUFFER Buffer; // NDIS buffer descriptor.
+ uchar MHeaderSize; // Size of media header.
+ uchar *MAddr; // Pointer to media address structure.
+ uint SAddrOffset; // Offset into media address of source address.
+ uchar SRFlag = 0; // Source routing flag.
+ uchar SNAPLength = 0;
+ uchar *SNAPAddr; // Address of SNAP header.
+ PNDIS_PACKET Packet; // Packet for sending.
+ NDIS_STATUS Status;
+ ushort HWType;
+ IPAddr Src;
+ CTELockHandle Handle;
+ ARPIPAddr *Addr;
+
+ // First, get a source address we can use.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ Addr = &Interface->ai_ipaddr;
+ Src = NULL_IP_ADDR;
+ do {
+ if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
+ //
+ // This is a valid address. See if it is the same as the
+ // target address - i.e. arp'ing for ourselves. If it is,
+ // we want to use that as our source address.
+ //
+ if (IP_ADDR_EQUAL(Addr->aia_addr, Destination)) {
+ Src = Addr->aia_addr;
+ break;
+ }
+
+ // See if the target is on this subnet.
+ if (IP_ADDR_EQUAL(
+ Addr->aia_addr & Addr->aia_mask,
+ Destination & Addr->aia_mask
+ ))
+ {
+ //
+ // See if we've already found a suitable candidate on the
+ // same subnet. If we haven't, we'll use this one.
+ //
+ if (!IP_ADDR_EQUAL(
+ Addr->aia_addr & Addr->aia_mask,
+ Src & Addr->aia_mask
+ ))
+ {
+ Src = Addr->aia_addr;
+ }
+ }
+ else {
+ // He's not on our subnet. If we haven't already found a valid
+ // address save this one in case we don't find a match for the
+ // subnet.
+ if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR)) {
+ Src = Addr->aia_addr;
+ }
+ }
+ }
+
+ Addr = Addr->aia_next;
+
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ // If we didn't find a source address, give up.
+ if (IP_ADDR_EQUAL(Src, NULL_IP_ADDR))
+ return NDIS_STATUS_SUCCESS;
+
+ NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ Interface->ai_outdiscards++;
+ return Status;
+ }
+
+ ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
+ (Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
+
+ // Figure out what type of media this is, and do the appropriate thing.
+ switch (Interface->ai_media) {
+ case NdisMedium802_3:
+ MHeaderSize = ARP_MAX_MEDIA_ENET;
+ MAddr = ENetBcst;
+ if (Interface->ai_snapsize == 0) {
+ SNAPAddr = (uchar *)NULL;
+ HWType = net_short(ARP_HW_ENET);
+ } else {
+ SNAPLength = sizeof(SNAPHeader);
+ SNAPAddr = ARPSNAP;
+ HWType = net_short(ARP_HW_802);
+ }
+
+ SAddrOffset = offsetof(struct ENetHeader, eh_saddr);
+ break;
+ case NdisMedium802_5:
+ // Token ring. We have logic for dealing with the second transmit
+ // of an arp request.
+ MAddr = TRBcst;
+ SAddrOffset = offsetof(struct TRHeader, tr_saddr);
+ SNAPLength = sizeof(SNAPHeader);
+ SNAPAddr = ARPSNAP;
+ MHeaderSize = sizeof(TRHeader);
+ HWType = net_short(ARP_HW_802);
+ if (Type == ARP_RESOLVING_GLOBAL) {
+ MHeaderSize += sizeof(RC);
+ SRFlag = TR_RII;
+ }
+ break;
+ case NdisMediumFddi:
+ MHeaderSize = sizeof(FDDIHeader);
+ MAddr = FDDIBcst;
+ SNAPAddr = ARPSNAP;
+ SNAPLength = sizeof(SNAPHeader);
+ SAddrOffset = offsetof(struct FDDIHeader, fh_saddr);
+ HWType = net_short(ARP_HW_ENET);
+ break;
+ case NdisMediumArcnet878_2:
+ MHeaderSize = ARP_MAX_MEDIA_ARC;
+ MAddr = ARCBcst;
+ SNAPAddr = (uchar *)NULL;
+ SAddrOffset = offsetof(struct ARCNetHeader, ah_saddr);
+ HWType = net_short(ARP_HW_ARCNET);
+ break;
+ default:
+ DEBUGCHK;
+ Interface->ai_outerrors++;
+ return NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+
+
+ if ((Buffer = GetARPBuffer(Interface, &MHeader,
+ (uchar)(sizeof(ARPHeader) + MHeaderSize + SNAPLength))) == (PNDIS_BUFFER)NULL) {
+ NdisFreePacket(Packet);
+ Interface->ai_outdiscards++;
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ if (Interface->ai_media == NdisMediumArcnet878_2)
+ NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT;
+
+ // Copy broadcast address into packet.
+ CTEMemCopy(MHeader, MAddr, MHeaderSize);
+ // Fill in source address.
+ if (SrcAddr == NULL) {
+ SrcAddr = Interface->ai_addr;
+ }
+
+ if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize != 0) {
+ ENetHeader *Hdr = (ENetHeader *)MHeader;
+
+ // Using SNAP on ethernet. Adjust the etype to a length.
+ Hdr->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader));
+ }
+
+ CTEMemCopy(&MHeader[SAddrOffset], SrcAddr, Interface->ai_addrlen);
+ if ((Interface->ai_media == NdisMedium802_5) && (Type == ARP_RESOLVING_GLOBAL)) {
+ // Turn on source routing.
+ MHeader[SAddrOffset] |= SRFlag;
+ MHeader[SAddrOffset + Interface->ai_addrlen] |= TrRii;
+ }
+ // Copy in SNAP header, if any.
+ CTEMemCopy(&MHeader[MHeaderSize], SNAPAddr, SNAPLength);
+
+ // Media header is filled in. Now do ARP packet itself.
+ NdisChainBufferAtFront(Packet, Buffer);
+ return SendARPPacket(Interface, Packet,(ARPHeader *)&MHeader[MHeaderSize + SNAPLength],
+ net_short(ARP_REQUEST), (uchar *)NULL, SrcAddr, Destination, Src,
+ HWType, CheckIF);
+}
+
+//* SendARPReply - Reply to an ARP request.
+//
+// Called by our receive packet handler when we need to reply. We build a packet
+// and buffer and call SendARPPacket to send it.
+//
+// Entry: Interface - Pointer to interface to reply on.
+// Destination - IPAddress to reply to.
+// Src - Source address to reply from.
+// HWAddress - Hardware address to reply to.
+// SourceRoute - Source Routing information, if any.
+// SourceRouteSize - Size in bytes of soure routing.
+// UseSNAP - Whether or not to use SNAP for this reply.
+//
+// Returns: Nothing.
+//
+void
+SendARPReply(ARPInterface *Interface, IPAddr Destination, IPAddr Src, uchar *HWAddress,
+ RC UNALIGNED *SourceRoute, uint SourceRouteSize, uint UseSNAP)
+{
+ PNDIS_PACKET Packet; // Buffer and packet to be used.
+ PNDIS_BUFFER Buffer;
+ uchar *Header; // Pointer to media header.
+ NDIS_STATUS Status;
+ uchar Size = 0; // Size of media header buffer.
+ ushort HWType;
+ ENetHeader *EH;
+ FDDIHeader *FH;
+ ARCNetHeader *AH;
+ TRHeader *TRH;
+
+ // Allocate a packet for this.
+ NdisAllocatePacket(&Status, &Packet, Interface->ai_ppool);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ Interface->ai_outdiscards++;
+ return;
+ }
+
+ ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_owner = PACKET_OWNER_LINK;
+ (Interface->ai_outpcount[AI_UCAST_INDEX])++;
+
+ Size = Interface->ai_hdrsize;
+
+ if (UseSNAP)
+ Size += Interface->ai_snapsize;
+
+ if (Interface->ai_media == NdisMedium802_5)
+ Size += SourceRouteSize;
+
+ if ((Buffer = GetARPBuffer(Interface, &Header, (uchar)(Size + sizeof(ARPHeader)))) ==
+ (PNDIS_BUFFER)NULL) {
+ Interface->ai_outdiscards++;
+ NdisFreePacket(Packet);
+ return;
+ }
+
+ // Decide how to build the header based on the media type.
+ switch (Interface->ai_media) {
+ case NdisMedium802_3:
+ EH = (ENetHeader *)Header;
+ CTEMemCopy(EH->eh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(EH->eh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
+ if (!UseSNAP) {
+ EH->eh_type = net_short(ARP_ETYPE_ARP);
+ HWType = net_short(ARP_HW_ENET);
+ } else {
+ // Using SNAP on ethernet.
+ EH->eh_type = net_short(sizeof(ARPHeader) + sizeof(SNAPHeader));
+ HWType = net_short(ARP_HW_802);
+ CTEMemCopy(Header + sizeof(ENetHeader), ARPSNAP,
+ sizeof(SNAPHeader));
+ }
+ break;
+ case NdisMedium802_5:
+ TRH = (TRHeader *)Header;
+ TRH->tr_ac = ARP_AC;
+ TRH->tr_fc = ARP_FC;
+ CTEMemCopy(TRH->tr_daddr, HWAddress, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(TRH->tr_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
+ if (SourceRouteSize) {// If we have source route info, deal with
+ // it.
+ CTEMemCopy(Header + sizeof(TRHeader), SourceRoute,
+ SourceRouteSize);
+ // Convert to directed response.
+ ((RC *)&Header[sizeof(TRHeader)])->rc_blen &= RC_LENMASK;
+
+ ((RC *)&Header[sizeof(TRHeader)])->rc_dlf ^= RC_DIR;
+ TRH->tr_saddr[0] |= TR_RII;
+ }
+ CTEMemCopy(Header + sizeof(TRHeader) + SourceRouteSize, ARPSNAP,
+ sizeof(SNAPHeader));
+ HWType = net_short(ARP_HW_802);
+ break;
+ case NdisMediumFddi:
+ FH = (FDDIHeader *)Header;
+ FH->fh_pri = ARP_FDDI_PRI;
+ CTEMemCopy(FH->fh_daddr, HWAddress, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(FH->fh_saddr, Interface->ai_addr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(Header + sizeof(FDDIHeader), ARPSNAP, sizeof(SNAPHeader));
+ HWType = net_short(ARP_HW_ENET);
+ break;
+ case NdisMediumArcnet878_2:
+ AH = (ARCNetHeader *)Header;
+ AH->ah_saddr = Interface->ai_addr[0];
+ AH->ah_daddr = *HWAddress;
+ AH->ah_prot = ARP_ARCPROT_ARP;
+ NdisBufferLength(Buffer) -= ARCNET_ARPHEADER_ADJUSTMENT;
+ HWType = net_short(ARP_HW_ARCNET);
+ break;
+ default:
+ DEBUGCHK;
+ Interface->ai_outerrors++;
+ FreeARPBuffer(Interface, Buffer);
+ NdisFreePacket(Packet);
+ return;
+ }
+
+ NdisChainBufferAtFront(Packet, Buffer);
+ SendARPPacket(Interface, Packet,(ARPHeader *)(Header + Size), net_short(ARP_RESPONSE),
+ HWAddress, NULL, Destination, Src, HWType, TRUE);
+}
+
+
+//* ARPRemoveRCE - Remove an RCE from the ATE list.
+//
+// This funtion removes a specified RCE from a given ATE. It assumes the ate_lock
+// is held by the caller.
+//
+// Entry: ATE - ATE from which RCE is to be removed.
+// RCE - RCE to be removed.
+//
+// Returns: Nothing
+//
+void
+ARPRemoveRCE(ARPTableEntry *ATE, RouteCacheEntry *RCE)
+{
+ ARPContext *CurrentAC; // Current ARP Context being checked.
+#ifdef DEBUG
+ uint Found = FALSE;
+#endif
+
+ CurrentAC = (ARPContext *)(((char *)&ATE->ate_rce) -
+ offsetof(struct ARPContext, ac_next));
+
+ while (CurrentAC->ac_next != (RouteCacheEntry *)NULL)
+ if (CurrentAC->ac_next == RCE) {
+ ARPContext *DummyAC = (ARPContext *)RCE->rce_context;
+ CurrentAC->ac_next = DummyAC->ac_next;
+ DummyAC->ac_ate = (ARPTableEntry *)NULL;
+#ifdef DEBUG
+ Found = TRUE;
+#endif
+ break;
+ }
+ else
+ CurrentAC = (ARPContext *)CurrentAC->ac_next->rce_context;
+
+ CTEAssert(Found);
+}
+//* ARPLookup - Look up an entry in the ARP table.
+//
+// Called to look up an entry in an interface's ARP table. If we find it, we'll
+// lock the entry and return a pointer to it, otherwise we return NULL. We
+// assume that the caller has the ARP table locked when we are called.
+//
+// The ARP table entry is structured as a hash table of pointers to
+// ARPTableEntrys.After hashing on the IP address, a linear search is done to
+// lookup the entry.
+//
+// If we find the entry, we lock it for the caller. If we don't find
+// the entry, we leave the ARP table locked so that the caller may atomically
+// insert a new entry without worrying about a duplicate being inserted between
+// the time the table was checked and the time the caller went to insert the
+// entry.
+//
+// Entry: Interface - The interface to be searched upon.
+// Address - The IP address we're looking up.
+// Handle - Pointer to lock handle to be used to lock entry.
+//
+// Returns: Pointer to ARPTableEntry if found, or NULL if not.
+//
+ARPTableEntry *
+ARPLookup(ARPInterface *Interface, IPAddr Address, CTELockHandle *Handle)
+{
+ int i = ARP_HASH(Address); // Index into hash table.
+ ARPTableEntry *Current; // Current ARP Table entry being
+ // examined.
+
+ Current = (*Interface->ai_ARPTbl)[i];
+
+ while (Current != (ARPTableEntry *)NULL) {
+ CTEGetLock(&Current->ate_lock, Handle);
+ if (IP_ADDR_EQUAL(Current->ate_dest, Address)) { // Found a match.
+ return Current;
+ }
+ CTEFreeLock(&Current->ate_lock, *Handle);
+ Current = Current->ate_next;
+ }
+ // If we got here, we didn't find the entry. Leave the table locked and
+ // return the handle.
+ return (ARPTableEntry *)NULL;
+}
+
+//* IsBCastOnIF- See it an address is a broadcast address on an interface.
+//
+// Called to see if a particular address is a broadcast address on an
+// interface. We'll check the global, net, and subnet broadcasts. We assume
+// the caller holds the lock on the interface.
+//
+// Entry: Interface - Interface to check.
+// Addr - Address to check.
+//
+// Returns: TRUE if it it a broadcast, FALSE otherwise.
+//
+uint
+IsBCastOnIF(ARPInterface *Interface, IPAddr Addr)
+{
+ IPAddr BCast;
+ IPMask Mask;
+ ARPIPAddr *ARPAddr;
+ IPAddr LocalAddr;
+
+ // First get the interface broadcast address.
+ BCast = Interface->ai_bcast;
+
+ // First check for global broadcast.
+ if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr))
+ return TRUE;
+
+ // Now walk the local addresses, and check for net/subnet bcast on each
+ // one.
+ ARPAddr = &Interface->ai_ipaddr;
+ do {
+ // See if this one is valid.
+ LocalAddr = ARPAddr->aia_addr;
+ if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
+ // He's valid.
+ Mask = ARPAddr->aia_mask;
+
+ // First check for subnet bcast.
+ if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
+ return TRUE;
+
+ // Now check all nets broadcast.
+ Mask = IPNetMask(LocalAddr);
+ if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
+ return TRUE;
+ }
+
+ ARPAddr = ARPAddr->aia_next;
+
+ } while (ARPAddr != NULL);
+
+ // If we're here, it's not a broadcast.
+ return FALSE;
+
+}
+
+
+//* ARPSendBCast - See if this is a bcast or mcast frame, and send it.
+//
+// Called when we have a packet to send and we want to see if it's a broadcast
+// or multicast frame on this interface. We'll search the local addresses and
+// see if we can determine if it is. If it is, we'll send it here. Otherwise
+// we return FALSE, and the caller will try to resolve the address.
+//
+// Entry: Interface - A pointer to an AI structure.
+// Dest - Destination of datagram.
+// Packet - Packet to be sent.
+// Status - Place to return status of send attempt.
+//
+// Returns: TRUE if is was a bcast or mcast send, FALSE otherwise.
+//
+uint
+ARPSendBCast(ARPInterface *Interface, IPAddr Dest, PNDIS_PACKET Packet,
+ PNDIS_STATUS Status)
+{
+ uint IsBCast;
+ CTELockHandle Handle;
+ PNDIS_BUFFER ARPBuffer; // ARP Header buffer.
+ uchar *BufAddr; // Address of NDIS buffer
+ NDIS_STATUS MyStatus;
+ ENetHeader *Hdr;
+ FDDIHeader *FHdr;
+ TRHeader *TRHdr;
+ SNAPHeader UNALIGNED *SNAPPtr;
+ RC UNALIGNED *RCPtr;
+ ARCNetHeader *AHdr;
+ uint DataLength;
+
+ // Get the lock, and see if it's a broadcast.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ IsBCast = IsBCastOnIF(Interface, Dest);
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ if (IsBCast) {
+ if (Interface->ai_state == INTERFACE_UP) {
+ uchar Size;
+
+ Size = Interface->ai_hdrsize + Interface->ai_snapsize;
+
+ if (Interface->ai_media == NdisMedium802_5)
+ Size += sizeof(RC);
+
+ ARPBuffer = GetARPBuffer(Interface, &BufAddr, Size);
+ if (ARPBuffer != NULL) {
+ uint UNALIGNED *Temp;
+
+ // Got the buffer we need.
+ switch (Interface->ai_media) {
+
+ case NdisMedium802_3:
+
+ Hdr = (ENetHeader *)BufAddr;
+ if (!CLASSD_ADDR(Dest))
+ CTEMemCopy(Hdr, ENetBcst, ARP_802_ADDR_LENGTH);
+ else {
+ CTEMemCopy(Hdr, ENetMcst, ARP_802_ADDR_LENGTH);
+ Temp = (uint UNALIGNED *)&Hdr->eh_daddr[2];
+ *Temp |= (Dest & ARP_MCAST_MASK);
+ }
+
+ CTEMemCopy(Hdr->eh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ if (Interface->ai_snapsize == 0) {
+ // No snap on this interface, so just use ETypr.
+ Hdr->eh_type = net_short(ARP_ETYPE_IP);
+ } else {
+ ushort ShortDataLength;
+
+ // We're using SNAP. Find the size of the packet.
+ NdisQueryPacket(Packet, NULL, NULL, NULL,
+ &DataLength);
+ ShortDataLength = (ushort)(DataLength +
+ sizeof(SNAPHeader));
+ Hdr->eh_type = net_short(ShortDataLength);
+ SNAPPtr = (SNAPHeader UNALIGNED *)
+ (BufAddr + sizeof(ENetHeader));
+ CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
+ SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
+ }
+
+ break;
+
+ case NdisMedium802_5:
+
+ // This is token ring. We'll have to screw around with
+ // source routing.
+
+ // BUGBUG Need to support 'real' TR functional address
+ // for multicast - see RFC 1469.
+
+ TRHdr = (TRHeader *)BufAddr;
+
+ CTEMemCopy(TRHdr, TRBcst, offsetof(TRHeader, tr_saddr));
+ CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ if (sIPAlwaysSourceRoute)
+ {
+ TRHdr->tr_saddr[0] |= TR_RII;
+
+ RCPtr = (RC UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader));
+ RCPtr->rc_blen = TrRii | RC_LEN;
+ RCPtr->rc_dlf = RC_BCST_LEN;
+ SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)RCPtr + sizeof(RC));
+ }
+ else
+ {
+
+ //
+ // Adjust the size of the buffer to account for the
+ // fact that we don't have the RC field.
+ //
+ NdisAdjustBufferLength(ARPBuffer,(Size - sizeof(RC)));
+ SNAPPtr = (SNAPHeader UNALIGNED *)((uchar *)TRHdr + sizeof(TRHeader));
+ }
+ CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
+ SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
+
+ break;
+ case NdisMediumFddi:
+ FHdr = (FDDIHeader *)BufAddr;
+
+ if (!CLASSD_ADDR(Dest))
+ CTEMemCopy(FHdr, FDDIBcst,
+ offsetof(FDDIHeader, fh_saddr));
+ else {
+ CTEMemCopy(FHdr, FDDIMcst,
+ offsetof(FDDIHeader, fh_saddr));
+ Temp = (uint UNALIGNED *)&FHdr->fh_daddr[2];
+ *Temp |= (Dest & ARP_MCAST_MASK);
+ }
+
+ CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ SNAPPtr = (SNAPHeader UNALIGNED *)(BufAddr + sizeof(FDDIHeader));
+ CTEMemCopy(SNAPPtr, ARPSNAP, sizeof(SNAPHeader));
+ SNAPPtr->sh_etype = net_short(ARP_ETYPE_IP);
+
+ break;
+ case NdisMediumArcnet878_2:
+ AHdr = (ARCNetHeader *)BufAddr;
+ AHdr->ah_saddr = Interface->ai_addr[0];
+ AHdr->ah_daddr = 0;
+ AHdr->ah_prot = ARP_ARCPROT_IP;
+ break;
+ default:
+ DEBUGCHK;
+ *Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+ FreeARPBuffer(Interface, ARPBuffer);
+ return FALSE;
+
+ }
+
+ (Interface->ai_outpcount[AI_NONUCAST_INDEX])++;
+ Interface->ai_qlen++;
+ NdisChainBufferAtFront(Packet, ARPBuffer);
+ NdisSend(&MyStatus, Interface->ai_handle, Packet);
+
+ *Status = MyStatus;
+
+ if (MyStatus != NDIS_STATUS_PENDING) { // Send finished
+ // immediately.
+ if (MyStatus == NDIS_STATUS_SUCCESS) {
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ } else {
+ if (MyStatus == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+ NdisUnchainBufferAtFront(Packet, &ARPBuffer);
+ FreeARPBuffer(Interface, ARPBuffer);
+ }
+ } else
+ *Status = NDIS_STATUS_RESOURCES;
+ } else
+ *Status = NDIS_STATUS_ADAPTER_NOT_READY;
+
+ return TRUE;
+
+ } else
+ return FALSE;
+}
+
+//* ARPSendData - Send a frame to a specific destination address.
+//
+// Called when we need to send a frame to a particular address, after the
+// ATE has been looked up. We take in an ATE and a packet, validate the state of the
+// ATE, and either send or ARP for the address if it's not done resolving. We assume
+// the lock on the ATE is held where we're called, and we'll free it before returning.
+//
+// Entry: Interface - A pointer to the AI structure.
+// Packet - A pointer to the BufDesc chain to be sent.
+// entry - A pointer to the ATE for the send.
+// lhandle - Pointer to a lock handle for the ATE.
+//
+// Returns: Status of the transmit - success, an error, or pending.
+//
+NDIS_STATUS
+ARPSendData(ARPInterface *Interface, PNDIS_PACKET Packet, ARPTableEntry *entry,
+ CTELockHandle lhandle)
+{
+ PNDIS_BUFFER ARPBuffer; // ARP Header buffer.
+ uchar *BufAddr; // Address of NDIS buffer
+ NDIS_STATUS Status; // Status of send.
+
+ if (Interface->ai_state == INTERFACE_UP) {
+
+ if (entry->ate_state == ARP_GOOD) { // Entry is valid
+
+ entry->ate_useticks = ArpCacheLife;
+ if ((ARPBuffer = GetARPBuffer(Interface, &BufAddr,
+ entry->ate_addrlength)) != (PNDIS_BUFFER)NULL) {
+
+ // Everything's in good shape, copy header and send packet.
+
+ (Interface->ai_outpcount[AI_UCAST_INDEX])++;
+ Interface->ai_qlen++;
+ CTEMemCopy(BufAddr, entry->ate_addr, entry->ate_addrlength);
+
+ // If we're on Ethernet, see if we're using SNAP here.
+ if (Interface->ai_media == NdisMedium802_3 &&
+ entry->ate_addrlength != sizeof(ENetHeader)) {
+ ENetHeader *Header;
+ uint DataSize;
+ ushort ShortDataSize;
+
+ // We're apparently using SNAP on Ethernet. Query the
+ // packet for the size, and set the length properly.
+ NdisQueryPacket(Packet, NULL, NULL, NULL, &DataSize);
+ ShortDataSize = (ushort)(DataSize + sizeof(SNAPHeader));
+ Header = (ENetHeader *)BufAddr;
+ Header->eh_type = net_short(ShortDataSize);
+ }
+
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ NdisChainBufferAtFront(Packet, ARPBuffer);
+ NdisSend(&Status, Interface->ai_handle, Packet);
+ if (Status != NDIS_STATUS_PENDING) { // Send finished
+ // immediately.
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ } else {
+ if (Status == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+ NdisUnchainBufferAtFront(Packet, &ARPBuffer);
+ FreeARPBuffer(Interface, ARPBuffer);
+ }
+ return Status;
+ } else { // No buffer, free lock and return.
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ Interface->ai_outdiscards++;
+ return NDIS_STATUS_RESOURCES;
+ }
+ }
+ // The IP addresses match, but the state of the ARP entry indicates
+ // it's not valid. If the address is marked as resolving, we'll replace
+ // the current cached packet with this one. If it's been more than
+ // ARP_FLOOD_RATE ms. since we last sent an ARP request, we'll send
+ // another one now.
+ if (entry->ate_state <= ARP_RESOLVING) {
+ PNDIS_PACKET OldPacket = entry->ate_packet;
+ ulong Now = CTESystemUpTime();
+ entry->ate_packet = Packet;
+ if ((Now - entry->ate_valid) > ARP_FLOOD_RATE) {
+ IPAddr Dest = entry->ate_dest;
+
+ entry->ate_valid = Now;
+ entry->ate_state = ARP_RESOLVING_GLOBAL; // We're done this
+ // at least once.
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE); // Send a request.
+ } else
+ CTEFreeLock(&entry->ate_lock, lhandle);
+
+ if (OldPacket)
+ IPSendComplete(Interface->ai_context, OldPacket,
+ NDIS_STATUS_SUCCESS);
+
+ return NDIS_STATUS_PENDING;
+ } else {
+ DEBUGCHK;
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ Interface->ai_outerrors++;
+ return NDIS_STATUS_INVALID_PACKET;
+ }
+ } else {
+ // Adapter is down. Just return the error.
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ return NDIS_STATUS_ADAPTER_NOT_READY;
+ }
+}
+
+//* CreateARPTableEntry - Create a new entry in the ARP table.
+//
+// A function to put an entry into the ARP table. We allocate memory if we
+// need to.
+//
+// The first thing to do is get the lock on the ARP table, and see if the
+// entry already exists. If it does, we're done. Otherwise we need to allocate
+// memory and create a new entry.
+//
+// Entry: Interface - Interface for ARP table.
+// Destination - Destination address to be mapped.
+// Handle - Pointer to lock handle for entry.
+//
+// Returns: Pointer to newly created entry.
+//
+ARPTableEntry *
+CreateARPTableEntry(ARPInterface *Interface, IPAddr Destination,
+ CTELockHandle *Handle)
+{
+ ARPTableEntry *NewEntry, *Entry;
+ CTELockHandle TableHandle;
+ int i = ARP_HASH(Destination);
+ int Size;
+
+ // First look for it, and if we don't find it return try to create one.
+ CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
+ if ((Entry = ARPLookup(Interface, Destination, Handle)) !=
+ (ARPTableEntry *)NULL) {
+ CTEFreeLock(&Interface->ai_ARPTblLock, *Handle);
+ *Handle = TableHandle;
+ return Entry;
+ }
+
+ // Allocate memory for the entry. If we can't, fail the request.
+ Size = sizeof(ARPTableEntry) - 1 +
+ (Interface->ai_media == NdisMedium802_5 ?
+ ARP_MAX_MEDIA_TR : (Interface->ai_hdrsize +
+ Interface->ai_snapsize));
+
+ if ((NewEntry = CTEAllocMem(Size)) == (ARPTableEntry *)NULL) {
+ CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
+ return (ARPTableEntry *)NULL;
+ }
+
+ CTEMemSet(NewEntry, 0, Size);
+ NewEntry->ate_dest = Destination;
+ if (Interface->ai_media != NdisMedium802_5 || sArpAlwaysSourceRoute)
+ NewEntry->ate_state = ARP_RESOLVING_GLOBAL;
+ else
+ NewEntry->ate_state = ARP_RESOLVING_LOCAL;
+
+ NewEntry->ate_rce = NULL;
+
+ NewEntry->ate_valid = CTESystemUpTime();
+ NewEntry->ate_useticks = ArpCacheLife;
+ CTEInitLock(&NewEntry->ate_lock);
+
+ // Entry does not exist. Insert the new entry into the table at the appropriate spot.
+ // ARPLookup returns with the table lock held if it fails.
+ NewEntry->ate_next = (*Interface->ai_ARPTbl)[i];
+ (*Interface->ai_ARPTbl)[i] = NewEntry;
+ Interface->ai_count++;
+ CTEGetLock(&NewEntry->ate_lock, Handle);
+ CTEFreeLock(&Interface->ai_ARPTblLock, *Handle);
+ *Handle = TableHandle;
+ return NewEntry;
+}
+
+
+//* ARPTransmit - Send a frame.
+//
+// The main ARP transmit routine, called by the upper layer. This routine
+// takes as input a buf desc chain, RCE, and size. We validate the cached
+// information in the RCE. If it is valid, we use it to send the frame. Otherwise
+// we do a table lookup. If we find it in the table, we'll update the RCE and continue.
+// Otherwise we'll queue the packet and start an ARP resolution.
+//
+// Entry: Context - A pointer to the AI structure.
+// Packet - A pointer to the BufDesc chain to be sent.
+// Destination - IP address of destination we're trying to reach,
+// RCE - A pointer to an RCE which may have cached information.
+//
+// Returns: Status of the transmit - success, an error, or pending.
+//
+NDIS_STATUS
+ARPTransmit(void *Context, PNDIS_PACKET Packet, IPAddr Destination,
+ RouteCacheEntry *RCE)
+{
+ ARPInterface *ai = (ARPInterface *)Context; // Set up as AI pointer.
+ ARPContext *ac; // ARP context pointer.
+ ARPTableEntry *entry; // Pointer to ARP tbl. entry
+ CTELockHandle lhandle; // Lock handle
+ CTELockHandle tlhandle; // Lock handle for ARP table.
+ NDIS_STATUS Status;
+
+ CTEGetLock(&ai->ai_ARPTblLock, &tlhandle);
+ if (RCE != (RouteCacheEntry *)NULL) { // Have a valid RCE.
+ ac = (ARPContext *)RCE->rce_context; // Get pointer to context
+ entry = ac->ac_ate;
+ if (entry != (ARPTableEntry *)NULL) { // Have a valid ATE.
+ CTEGetLockAtDPC(&entry->ate_lock, &lhandle); // Lock this structure
+ if (IP_ADDR_EQUAL(entry->ate_dest, Destination)) {
+ CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
+ return ARPSendData(ai, Packet, entry, tlhandle); // Send the data
+ }
+
+ // We have an RCE that identifies the wrong ATE. We'll free it from
+ // this list and try and find an ATE that is valid.
+ ARPRemoveRCE(entry, RCE);
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ // Fall through to 'no valid entry' code.
+ }
+ }
+
+ // Here we have no valid ATE, either because the RCE is NULL or the ATE
+ // specified by the RCE was invalid. We'll try and find one in the table. If
+ // we find one, we'll fill in this RCE and send the packet. Otherwise we'll
+ // try to create one. At this point we hold the lock on the ARP table.
+
+ if ((entry = ARPLookup(ai, Destination, &lhandle)) != (ARPTableEntry *)NULL) {
+ // Found a matching entry. ARPLookup returns with the ATE lock held.
+ if (RCE != (RouteCacheEntry *)NULL) {
+ ac->ac_next = entry->ate_rce; // Fill in context for next time.
+ entry->ate_rce = RCE;
+ ac->ac_ate = entry;
+ }
+ CTEFreeLockFromDPC(&ai->ai_ARPTblLock, lhandle);
+ return ARPSendData(ai, Packet, entry, tlhandle);
+ }
+
+ // No valid entry in the ARP table. First we'll see if we're sending to a
+ // broadcast address or multicast address. If not, we'll try to create
+ // an entry in the table and get an ARP resolution going. ARPLookup returns
+ // with the table lock held when it fails, we'll free it here.
+ CTEFreeLock(&ai->ai_ARPTblLock, tlhandle);
+
+ if (ARPSendBCast(ai, Destination, Packet, &Status))
+ return Status;
+
+ entry = CreateARPTableEntry(ai, Destination, &lhandle);
+ if (entry != NULL) {
+ if (entry->ate_state <= ARP_RESOLVING) { // Newly created entry.
+
+ // Someone else could have raced in and created the entry between
+ // the time we free the lock and the time we called
+ // CreateARPTableEntry(). We check this by looking at the packet
+ // on the entry. If there is no old packet we'll ARP. If there is,
+ // we'll call ARPSendData to figure out what to do.
+
+ if (entry->ate_packet == NULL) {
+ entry->ate_packet = Packet;
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ SendARPRequest(ai, Destination, entry->ate_state, NULL, TRUE);
+ // We don't know the state of the entry - we've freed the lock
+ // and yielded, and it could conceivably have timed out by now,
+ // or SendARPRequest could have failed, etc. We could take the
+ // lock, check the status from SendARPRequest, see if it's
+ // still the same packet, and then make a decision on the
+ // return value, but it's easiest just to return pending. If
+ // SendARPRequest failed, the entry will time out anyway.
+ return NDIS_STATUS_PENDING;
+ } else
+ return ARPSendData(ai, Packet, entry, lhandle);
+
+ } else {
+ if (entry->ate_state == ARP_GOOD) // Yow! A valid entry.
+ return ARPSendData(ai, Packet, entry, lhandle);
+ else { // An invalid entry!
+ CTEFreeLock(&entry->ate_lock, lhandle);
+ return NDIS_STATUS_RESOURCES;
+ }
+ }
+ } else // Couldn't create an entry.
+ return NDIS_STATUS_RESOURCES;
+
+}
+
+//* RemoveARPTableEntry - Delete an entry from the ARP table.
+//
+// This is a simple utility function to delete an entry from the ATP table. We
+// assume locks are held on both the table and the entry.
+//
+// Entry: Previous - The entry immediately before the one to be deleted.
+// Entry - The entry to be deleted.
+//
+// Returns: Nothing.
+//
+void
+RemoveARPTableEntry(ARPTableEntry *Previous, ARPTableEntry *Entry)
+{
+ RouteCacheEntry *RCE; // Pointer to route cache entry
+ ARPContext *AC;
+
+ RCE = Entry->ate_rce;
+ // Loop through and invalidate all RCEs on this ATE.
+ while (RCE != (RouteCacheEntry *)NULL) {
+ AC = (ARPContext *)RCE->rce_context;
+ AC->ac_ate = (ARPTableEntry *)NULL;
+ RCE = AC->ac_next;
+ }
+
+ // Splice this guy out of the list.
+ Previous->ate_next = Entry->ate_next;
+}
+
+//* ARPXferData - Transfer data on behalf on an upper later protocol.
+//
+// This routine is called by the upper layer when it needs to transfer data
+// from an NDIS driver. We just map his call down.
+//
+// Entry: Context - Context value we gave to IP (really a pointer to an AI).
+// MACContext - Context value MAC gave us on a receive.
+// MyOffset - Packet offset we gave to the protocol earlier.
+// ByteOffset - Byte offset into packet protocol wants transferred.
+// BytesWanted - Number of bytes to transfer.
+// Packet - Pointer to packet to be used for transferring.
+// Transferred - Pointer to where to return bytes transferred.
+//
+// Returns: NDIS_STATUS of command.
+//
+NDIS_STATUS
+ARPXferData(void *Context, NDIS_HANDLE MACContext, uint MyOffset, uint ByteOffset,
+ uint BytesWanted, PNDIS_PACKET Packet, uint *Transferred)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ NDIS_STATUS Status;
+
+ NdisTransferData(&Status, Interface->ai_handle, MACContext, ByteOffset+MyOffset,
+ BytesWanted, Packet, Transferred);
+
+ return Status;
+}
+
+
+//* ARPClose - Close an adapter.
+//
+// Called by IP when it wants to close an adapter, presumably due to an error condition.
+// We'll close the adapter, but we won't free any memory.
+//
+// Entry: Context - Context value we gave him earlier.
+//
+// Returns: Nothing.
+//
+void
+ARPClose(void *Context)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ NDIS_STATUS Status;
+ CTELockHandle LockHandle;
+ NDIS_HANDLE Handle;
+
+ Interface->ai_operstate = IF_STATUS_DOWN;
+ Interface->ai_state = INTERFACE_DOWN;
+ CTEInitBlockStruc(&Interface->ai_block);
+
+ CTEGetLock(&Interface->ai_lock, &LockHandle);
+ if (Interface->ai_handle != (NDIS_HANDLE)NULL) {
+ Handle = Interface->ai_handle;
+ Interface->ai_handle = NULL;
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+
+ NdisCloseAdapter(&Status, Handle);
+
+ if (Status == NDIS_STATUS_PENDING)
+ Status = CTEBlock(&Interface->ai_block);
+
+ } else {
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+ }
+}
+
+//* ARPInvalidate - Notification that an RCE is invalid.
+//
+// Called by IP when an RCE is closed or otherwise invalidated. We look up the ATE for
+// the specified RCE, and then remove the RCE from the ATE list.
+//
+// Entry: Context - Context value we gave him earlier.
+// RCE - RCE to be invalidated
+//
+// Returns: Nothing.
+//
+void
+ARPInvalidate(void *Context, RouteCacheEntry *RCE)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ ARPTableEntry *ATE;
+ CTELockHandle Handle, ATEHandle;
+ ARPContext *AC = (ARPContext *)RCE->rce_context;
+
+ CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
+ if ((ATE = AC->ac_ate) == (ARPTableEntry *)NULL) {
+ CTEFreeLock(&Interface->ai_ARPTblLock, Handle); // No matching ATE.
+ return;
+ }
+
+ CTEGetLock(&ATE->ate_lock, &ATEHandle);
+ ARPRemoveRCE(ATE, RCE);
+ CTEMemSet(RCE->rce_context, 0, RCE_CONTEXT_SIZE);
+ CTEFreeLock(&Interface->ai_ARPTblLock, ATEHandle);
+ CTEFreeLock(&ATE->ate_lock, Handle);
+
+}
+
+//* ARPSetMCastList - Set the multicast address list for the adapter.
+//
+// Called to try and set the multicast reception list for the adapter.
+// We allocate a buffer big enough to hold the new address list, and format
+// the address list into the buffer. Then we submit the NDIS request to set
+// the list. If we can't set the list because the multicast address list is
+// full we'll put the card into all multicast mode.
+//
+// Input: Interface - Interface on which to set list.
+//
+// Returns: NDIS_STATUS of attempt.
+//
+NDIS_STATUS
+ARPSetMCastList(ARPInterface *Interface)
+{
+ CTELockHandle Handle;
+ uchar *MCastBuffer, *CurrentPtr;
+ uint MCastSize;
+ NDIS_STATUS Status;
+ uint i;
+ ARPMCastAddr *AddrPtr;
+ IPAddr UNALIGNED *Temp;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ MCastSize = Interface->ai_mcastcnt * ARP_802_ADDR_LENGTH;
+ if (MCastSize != 0)
+ MCastBuffer = CTEAllocMem(MCastSize);
+ else
+ MCastBuffer = NULL;
+
+ if (MCastBuffer != NULL || MCastSize == 0) {
+ // Got the buffer. Loop through, building the list.
+ AddrPtr = Interface->ai_mcast;
+
+ CurrentPtr = MCastBuffer;
+
+ for (i = 0; i < Interface->ai_mcastcnt; i++) {
+ CTEAssert(AddrPtr != NULL);
+
+ if (Interface->ai_media == NdisMedium802_3) {
+
+ CTEMemCopy(CurrentPtr, ENetMcst, ARP_802_ADDR_LENGTH);
+ Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2);
+ *Temp |= AddrPtr->ama_addr;
+ } else
+ if (Interface->ai_media == NdisMediumFddi) {
+ CTEMemCopy(CurrentPtr, ((FDDIHeader *)FDDIMcst)->fh_daddr,
+ ARP_802_ADDR_LENGTH);
+ Temp = (IPAddr UNALIGNED *)(CurrentPtr + 2);
+ *Temp |= AddrPtr->ama_addr;
+ } else
+ DEBUGCHK;
+
+ CurrentPtr += ARP_802_ADDR_LENGTH;
+ AddrPtr = AddrPtr->ama_next;
+ }
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ // We're built the list. Now give it to the driver to handle.
+ if (Interface->ai_media == NdisMedium802_3) {
+ Status = DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_802_3_MULTICAST_LIST, MCastBuffer, MCastSize, NULL);
+ } else
+ if (Interface->ai_media == NdisMediumFddi) {
+ Status = DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_FDDI_LONG_MULTICAST_LIST, MCastBuffer, MCastSize, NULL);
+ } else
+ DEBUGCHK;
+
+ if (MCastBuffer != NULL) {
+ CTEFreeMem(MCastBuffer);
+ }
+
+ if (Status == NDIS_STATUS_MULTICAST_FULL) {
+ // Multicast list is full. Try to set the filter to all multicasts.
+ Interface->ai_pfilter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+
+ Status = DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter,
+ sizeof(uint), NULL);
+ }
+
+ } else {
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ Status = NDIS_STATUS_RESOURCES;
+ }
+
+ return Status;
+
+}
+
+//* ARPFindMCast - Find a multicast address structure on our list.
+//
+// Called as a utility to find a multicast address structure. If we find
+// it, we return a pointer to it and it's predecessor. Otherwise we return
+// NULL. We assume the caller holds the lock on the interface already.
+//
+// Input: Interface - Interface to search.
+// Addr - Addr to find.
+// Prev - Where to return previous pointer.
+//
+// Returns: Pointer if we find one, NULL otherwise.
+//
+ARPMCastAddr *
+ARPFindMCast(ARPInterface *Interface, IPAddr Addr, ARPMCastAddr **Prev)
+{
+ ARPMCastAddr *AddrPtr, *PrevPtr;
+
+ PrevPtr = STRUCT_OF(ARPMCastAddr, &Interface->ai_mcast, ama_next);
+ AddrPtr = PrevPtr->ama_next;
+ while (AddrPtr != NULL) {
+ if (IP_ADDR_EQUAL(AddrPtr->ama_addr, Addr))
+ break;
+ else {
+ PrevPtr = AddrPtr;
+ AddrPtr = PrevPtr->ama_next;
+ }
+ }
+
+ *Prev = PrevPtr;
+ return AddrPtr;
+}
+
+//* ARPDelMCast - Delete a multicast address.
+//
+// Called when we want to delete a multicast address. We look for a matching
+// (masked) address. If we find one, we'll dec. the reference count and if
+// it goes to 0 we'll pull him from the list and reset the multicast list.
+//
+// Input: Interface - Interface on which to act.
+// Addr - Address to be deleted.
+//
+// Returns: TRUE if it worked, FALSE otherwise.
+//
+uint
+ARPDelMCast(ARPInterface *Interface, IPAddr Addr)
+{
+ ARPMCastAddr *AddrPtr, *PrevPtr;
+ CTELockHandle Handle;
+ uint Status = TRUE;
+
+ // When we support TR (RFC 1469) fully we'll need to change this.
+ if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media ==
+ NdisMediumFddi) {
+ // This is an interface that supports mcast addresses.
+ Addr &= ARP_MCAST_MASK;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
+ if (AddrPtr != NULL) {
+ // We found one. Dec. his refcnt, and if it's 0 delete him.
+ (AddrPtr->ama_refcnt)--;
+ if (AddrPtr->ama_refcnt == 0) {
+ // He's done.
+ PrevPtr->ama_next = AddrPtr->ama_next;
+ (Interface->ai_mcastcnt)--;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ CTEFreeMem(AddrPtr);
+ ARPSetMCastList(Interface);
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ }
+ } else
+ Status = FALSE;
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ }
+
+ return Status;
+}
+//* ARPAddMCast - Add a multicast address.
+//
+// Called when we want to start receiving a multicast address. We'll mask
+// the address and look it up in our address list. If we find it, we'll just
+// bump the reference count. Otherwise we'll try to create one and put him
+// on the list. In that case we'll need to set the multicast address list for
+// the adapter.
+//
+// Input: Interface - Interface to set on.
+// Addr - Address to set.
+//
+// Returns: TRUE if we succeed, FALSE if we fail.
+//
+uint
+ARPAddMCast(ARPInterface *Interface, IPAddr Addr)
+{
+ ARPMCastAddr *AddrPtr, *PrevPtr;
+ CTELockHandle Handle;
+ uint Status = TRUE;
+
+
+ if (Interface->ai_state != INTERFACE_UP)
+ return FALSE;
+
+ // BUGBUG Currently we don't do anything with token ring, since we send
+ // all mcasts as TR broadcasts. When we comply with RFC 1469 we'll need to
+ // fix this.
+ if (Interface->ai_media == NdisMedium802_3 || Interface->ai_media ==
+ NdisMediumFddi) {
+ // This is an interface that supports mcast addresses.
+ Addr &= ARP_MCAST_MASK;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ AddrPtr = ARPFindMCast(Interface, Addr, &PrevPtr);
+ if (AddrPtr != NULL) {
+ // We found one, just bump refcnt.
+ (AddrPtr->ama_refcnt)++;
+ } else {
+ // Didn't find one. Allocate space for one, link him in, and
+ // try to set the list.
+ AddrPtr = CTEAllocMem(sizeof(ARPMCastAddr));
+ if (AddrPtr != NULL) {
+ // Got one. Link him in.
+ AddrPtr->ama_addr = Addr;
+ AddrPtr->ama_refcnt = 1;
+ AddrPtr->ama_next = Interface->ai_mcast;
+ Interface->ai_mcast = AddrPtr;
+ (Interface->ai_mcastcnt)++;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+
+ // Now try to set the list.
+ if (ARPSetMCastList(Interface) != NDIS_STATUS_SUCCESS) {
+ // Couldn't set the list. Call the delete routine to delete
+ // the address we just tried to set.
+ Status = ARPDelMCast(Interface, Addr);
+ if (!Status)
+ DEBUGCHK;
+ Status = FALSE;
+ }
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ } else
+ Status = FALSE; // Couldn't get memory.
+ }
+
+ // We've done out best. Free the lock and return.
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ }
+
+ return Status;
+}
+
+//* ARPAddAddr - Add an address to the ARP table.
+//
+// This routine is called by IP to add an address as a local address, or
+// or specify the broadcast address for this interface.
+//
+// Entry: Context - Context we gave IP earlier (really an ARPInterface pointer)
+// Type - Type of address (local, p-arp, multicast, or
+// broadcast).
+// Address - Broadcast IP address to be added.
+// Mask - Mask for address.
+//
+// Returns: 0 if we failed, non-zero otherwise
+//
+uint
+ARPAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ CTELockHandle Handle;
+
+ if (Type != LLIP_ADDR_LOCAL && Type != LLIP_ADDR_PARP) {
+ // Not a local address, must be broadcast or multicast.
+
+ if (Type == LLIP_ADDR_BCAST) {
+ Interface->ai_bcast = Address;
+ return TRUE;
+ } else
+ if (Type == LLIP_ADDR_MCAST) {
+ return ARPAddMCast(Interface, Address);
+ } else
+ return FALSE;
+ } else { // This is a local address.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ if (Type != LLIP_ADDR_PARP) {
+ uint RetStatus = FALSE;
+ uint ArpForSelf = FALSE;
+
+ if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, 0)) {
+ Interface->ai_ipaddr.aia_addr = Address;
+ Interface->ai_ipaddr.aia_mask = Mask;
+ Interface->ai_ipaddr.aia_age = ARPADDR_NEW_LOCAL;
+ if (Interface->ai_state == INTERFACE_UP) {
+ Interface->ai_ipaddr.aia_context = Context2;
+ ArpForSelf = TRUE;
+ } else {
+ Interface->ai_ipaddr.aia_context = NULL;
+ }
+ RetStatus = TRUE;
+ } else {
+ ARPIPAddr *NewAddr;
+
+ NewAddr = CTEAllocMem(sizeof(ARPIPAddr));
+ if (NewAddr != (ARPIPAddr *)NULL) {
+ NewAddr->aia_addr = Address;
+ NewAddr->aia_mask = Mask;
+ NewAddr->aia_age = ARPADDR_NEW_LOCAL;
+ NewAddr->aia_next = Interface->ai_ipaddr.aia_next;
+ if (Interface->ai_state == INTERFACE_UP) {
+ NewAddr->aia_context = Context2;
+ ArpForSelf = TRUE;
+ } else {
+ NewAddr->aia_context = NULL;
+ }
+
+ Interface->ai_ipaddr.aia_next = NewAddr;
+ RetStatus = TRUE;
+ }
+ }
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ // ARP for the address we've added, to see it it already exists.
+ if (RetStatus == TRUE && ArpForSelf == TRUE) {
+ SendARPRequest(Interface, Address, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ return IP_PENDING;
+ }
+
+ return RetStatus;
+ } else if (Type == LLIP_ADDR_PARP) {
+ ARPPArpAddr *NewPArp;
+
+ // He's adding a proxy arp address.
+ NewPArp = CTEAllocMem(sizeof(ARPPArpAddr));
+ if (NewPArp != NULL) {
+ NewPArp->apa_addr = Address;
+ NewPArp->apa_mask = Mask;
+ NewPArp->apa_next = Interface->ai_parpaddr;
+ Interface->ai_parpaddr = NewPArp;
+ Interface->ai_parpcount++;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return FALSE;
+ }
+ }
+
+}
+
+//* ARPDeleteAddr - Delete a local or proxy address.
+//
+// Called to delete a local or proxy address.
+//
+// Entry: Context - An ARPInterface pointer.
+// Type - Type of address (local or p-arp).
+// Address - IP address to be deleted.
+// Mask - Mask for address. Used only for deleting proxy-ARP
+// entries.
+//
+// Returns: 0 if we failed, non-zero otherwise
+//
+uint
+ARPDeleteAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ CTELockHandle Handle;
+ ARPIPAddr *DelAddr, *PrevAddr;
+ ARPPArpAddr *DelPAddr, *PrevPAddr;
+
+ if (Type == LLIP_ADDR_LOCAL) {
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ if (IP_ADDR_EQUAL(Interface->ai_ipaddr.aia_addr, Address)) {
+ Interface->ai_ipaddr.aia_addr = NULL_IP_ADDR;
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ } else {
+ PrevAddr = STRUCT_OF(ARPIPAddr, &Interface->ai_ipaddr, aia_next);
+ DelAddr = PrevAddr->aia_next;
+ while (DelAddr != NULL)
+ if (IP_ADDR_EQUAL(DelAddr->aia_addr, Address))
+ break;
+ else {
+ PrevAddr = DelAddr;
+ DelAddr = DelAddr->aia_next;
+ }
+
+ if (DelAddr != NULL) {
+ PrevAddr->aia_next = DelAddr->aia_next;
+ CTEFreeMem(DelAddr);
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return (DelAddr != NULL);
+ }
+ } else if (Type == LLIP_ADDR_PARP) {
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ PrevPAddr = STRUCT_OF(ARPPArpAddr, &Interface->ai_parpaddr, apa_next);
+ DelPAddr = PrevPAddr->apa_next;
+ while (DelPAddr != NULL)
+ if (IP_ADDR_EQUAL(DelPAddr->apa_addr, Address) &&
+ DelPAddr->apa_mask == Mask)
+ break;
+ else {
+ PrevPAddr = DelPAddr;
+ DelPAddr = DelPAddr->apa_next;
+ }
+
+ if (DelPAddr != NULL) {
+ PrevPAddr->apa_next = DelPAddr->apa_next;
+ Interface->ai_parpcount--;
+ CTEFreeMem(DelPAddr);
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return (DelPAddr != NULL);
+ } else
+ if (Type == LLIP_ADDR_MCAST)
+ return ARPDelMCast(Interface, Address);
+ else
+ return FALSE;
+}
+
+//* ARPTimeout - ARP timeout routine.
+//
+// This is the timeout routine that is called periodically. We scan the ARP table, looking
+// for invalid entries that can be removed.
+//
+// Entry: Timer - Pointer to the timer that just fired.
+// Context - Pointer to the interface to be timed out.
+//
+// Returns: Nothing.
+//
+void
+ARPTimeout(CTEEvent *Timer, void *Context)
+{
+ ARPInterface *Interface = (ARPInterface *)Context; // Our interface.
+ ARPTable *Table;
+ ARPTableEntry *Current, *Previous;
+ int i; // Index variable.
+ ulong Now = CTESystemUpTime(), ValidTime;
+ CTELockHandle tblhandle, entryhandle;
+ uchar Deleted;
+ PNDIS_PACKET PList = (PNDIS_PACKET)NULL;
+ ARPIPAddr *Addr;
+
+ // Walk down the list of addresses, decrementing the age.
+ CTEGetLock(&Interface->ai_lock, &tblhandle);
+
+ Addr = &Interface->ai_ipaddr;
+
+ do {
+ if (Addr->aia_age != ARPADDR_OLD_LOCAL) {
+ (Addr->aia_age)--;
+ if (Addr->aia_age == ARPADDR_OLD_LOCAL) {
+ if (Addr->aia_context != NULL) {
+ SetAddrControl *SAC;
+ SetAddrRtn Rtn;
+
+ SAC = (SetAddrControl *)Addr->aia_context;
+ Rtn = (SetAddrRtn)SAC->sac_rtn;
+ CTEFreeLock(&Interface->ai_lock, tblhandle);
+ (*Rtn)(SAC, IP_SUCCESS);
+ CTEGetLock(&Interface->ai_lock, &tblhandle);
+ Addr->aia_context = NULL;
+ }
+ } else {
+ CTEFreeLock(&Interface->ai_lock, tblhandle);
+ SendARPRequest(Interface, Addr->aia_addr, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ CTEGetLock(&Interface->ai_lock, &tblhandle);
+ }
+ }
+
+ Addr = Addr->aia_next;
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, tblhandle);
+
+ // Loop through the ARP table for this interface, and delete stale entries.
+ CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
+ Table = Interface->ai_ARPTbl;
+ for (i = 0; i < ARP_TABLE_SIZE;i++) {
+ Previous = (ARPTableEntry *)((uchar *)&((*Table)[i]) - offsetof(struct ARPTableEntry, ate_next));
+ Current = (*Table)[i];
+ while (Current != (ARPTableEntry *)NULL) {
+ CTEGetLock(&Current->ate_lock, &entryhandle);
+ Deleted = 0;
+
+ if (Current->ate_state == ARP_GOOD) {
+ //
+ // The ARP entry is valid for ARP_VALID_TIMEOUT by default.
+ // If a cache life greater than ARP_VALID_TIMEOUT has been
+ // configured, we'll make the entry valid for that time.
+ //
+ ValidTime = ArpCacheLife * ARP_TIMER_TIME;
+
+ if (ValidTime < ARP_MIN_VALID_TIMEOUT) {
+ ValidTime = ARP_MIN_VALID_TIMEOUT;
+ }
+ }
+ else {
+ ValidTime = ARP_RESOLVE_TIMEOUT;
+ }
+
+ if (Current->ate_valid != ALWAYS_VALID &&
+ ( ((Now - Current->ate_valid) > ValidTime) ||
+ (Current->ate_state == ARP_GOOD &&
+ !(--(Current->ate_useticks))))) {
+
+ if (Current->ate_state != ARP_RESOLVING_LOCAL) {
+ // Really need to delete this guy.
+ PNDIS_PACKET Packet = Current->ate_packet;
+
+ if (Packet != (PNDIS_PACKET)NULL) {
+ ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link
+ = PList;
+ PList = Packet;
+ }
+ RemoveARPTableEntry(Previous, Current);
+ Interface->ai_count--;
+ Deleted = 1;
+ } else {
+ IPAddr Dest = Current->ate_dest;
+ // This entry is only resoving locally, presumably this is
+ // token ring. We'll need to transmit a 'global' resolution
+ // now.
+ CTEAssert(Interface->ai_media == NdisMedium802_5);
+
+ Now = CTESystemUpTime();
+ Current->ate_valid = Now;
+ Current->ate_state = ARP_RESOLVING_GLOBAL;
+ CTEFreeLock(&Current->ate_lock, entryhandle);
+ CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
+ // Send a global request.
+ SendARPRequest(Interface, Dest, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ CTEGetLock(&Interface->ai_ARPTblLock, &tblhandle);
+
+ // Since we've freed the locks, we need to start over from
+ // the start of this chain.
+ Previous = STRUCT_OF(ARPTableEntry, &((*Table)[i]),
+ ate_next);
+ Current = (*Table)[i];
+ continue;
+
+ }
+ }
+
+ // If we deleted the entry, leave the previous pointer alone, advance the
+ // current pointer, and free the memory. Otherwise move both pointers forward.
+ // We can free the entry lock now because the next pointers are protected by
+ // the table lock, and we've removed it from the list so nobody else should
+ // find it anyway.
+ CTEFreeLock(&Current->ate_lock, entryhandle);
+ if (Deleted) {
+ ARPTableEntry *Temp = Current;
+ Current = Current->ate_next;
+ CTEFreeMem(Temp);
+ } else {
+ Previous = Current;
+ Current = Current->ate_next;
+ }
+ }
+ }
+
+ CTEFreeLock(&Interface->ai_ARPTblLock, tblhandle);
+
+ while (PList != (PNDIS_PACKET)NULL) {
+ PNDIS_PACKET Packet = PList;
+
+ PList = ((PacketContext *)Packet->ProtocolReserved)->pc_common.pc_link;
+ IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
+ }
+
+ CTEStartTimer(&Interface->ai_timer, ARP_TIMER_TIME, ARPTimeout, Interface);
+}
+
+//* IsLocalAddr - Return info. about local status of address.
+//
+// Called when we need info. about whether or not a particular address is
+// local. We return info about whether or not it is, and if it is how old
+// it is.
+//
+// Entry: Interface - Pointer to interface structure to be searched.
+// Address - Address in question.
+//
+// Returns: ARPADDR_*, for how old it is.
+//
+//
+uint
+IsLocalAddr(ARPInterface *Interface, IPAddr Address)
+{
+ CTELockHandle Handle;
+ ARPIPAddr *CurrentAddr;
+ uint Age;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ CurrentAddr = &Interface->ai_ipaddr;
+ Age = ARPADDR_NOT_LOCAL;
+
+ do {
+ if (CurrentAddr->aia_addr == Address) {
+ Age = CurrentAddr->aia_age;
+ break;
+ }
+ CurrentAddr = CurrentAddr->aia_next;
+ } while (CurrentAddr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return Age;
+}
+
+//* ARPLocalAddr - Determine whether or not a given address if local.
+//
+// This routine is called when we receive an incoming packet and need to determine whether
+// or not it's local. We look up the provided address on the specified interface.
+//
+// Entry: Interface - Pointer to interface structure to be searched.
+// Address - Address in question.
+//
+// Returns: TRUE if it is a local address, FALSE if it's not.
+//
+uchar
+ARPLocalAddr(ARPInterface *Interface, IPAddr Address)
+{
+ CTELockHandle Handle;
+ ARPPArpAddr *CurrentPArp;
+ IPMask Mask, NetMask;
+ IPAddr MatchAddress;
+
+ // First, see if he's a local (not-proxy) address.
+ if (IsLocalAddr(Interface, Address) != ARPADDR_NOT_LOCAL)
+ return TRUE;
+
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ // Didn't find him in out local address list. See if he exists on our
+ // proxy ARP list.
+ for (CurrentPArp = Interface->ai_parpaddr; CurrentPArp != NULL;
+ CurrentPArp = CurrentPArp->apa_next) {
+ // See if this guy matches.
+ Mask = CurrentPArp->apa_mask;
+ MatchAddress = Address & Mask;
+ if (IP_ADDR_EQUAL(CurrentPArp->apa_addr, MatchAddress)) {
+ // He matches. We need to make a few more checks to make sure
+ // we don't reply to a broadcast address.
+ if (Mask == HOST_MASK) {
+ // We're matching the whole address, so it's OK.
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ }
+ // See if the non-mask part it all-zeros. Since the mask presumably
+ // covers a subnet, this trick will prevent us from replying to
+ // a zero host part.
+ if (IP_ADDR_EQUAL(MatchAddress, Address))
+ continue;
+
+ // See if the host part is all ones.
+ if (IP_ADDR_EQUAL(Address, MatchAddress | (IP_LOCAL_BCST & ~Mask)))
+ continue;
+
+ // If the mask we were given is not the net mask for this address,
+ // we'll need to repeat the above checks.
+ NetMask = IPNetMask(Address);
+ if (NetMask != Mask) {
+
+ MatchAddress = Address & NetMask;
+ if (IP_ADDR_EQUAL(MatchAddress, Address))
+ continue;
+
+ if (IP_ADDR_EQUAL(Address, MatchAddress |
+ (IP_LOCAL_BCST & ~NetMask)))
+ continue;
+ }
+
+ // If we get to this point we've passed all the tests, so it's
+ // local.
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TRUE;
+ }
+ }
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return FALSE;
+
+}
+
+
+#ifdef VXD
+
+
+#ifndef CHICAGO
+extern void DisplayPopup(uchar *Msg);
+uchar CMsg1[] = "The system has detected a conflict for IP address ";
+uchar CMsg2[] = " with the system having hardware address ";
+uchar CMsg3[] = ". The local interface has been disabled";
+
+uchar CMsg[sizeof(CMsg1) - 1 + sizeof(CMsg2) - 1 + sizeof(CMsg3) - 1 +
+ ((sizeof(IPAddr) * 4) - 1) + ((ARP_802_ADDR_LENGTH * 3) - 1)
+ + 1 + 1];
+#else
+extern void NotifyConflictProc(CTEEvent *Event, void *Context);
+extern void DisplayConflictPopup(uchar *IPAddr, uchar *HWAddr, uint Shutoff);
+#endif // CHICAGO
+
+#endif // NT
+
+//* NotifyConflictProc - Notify the user of an address conflict.
+//
+// Called when we need to notify the user of an address conflict. The
+// exact mechanism is system dependent, but generally involves a popup.
+//
+// Input: Event - Event that fired.
+// Context - Pointer to ARPNotifyStructure.
+//
+// Returns: Nothing.
+//
+void
+#ifndef CHICAGO
+NotifyConflictProc(CTEEvent *Event, void *Context)
+#else
+DisplayConflictProc(void *Context)
+#endif
+{
+#ifdef VXD
+ uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
+ uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
+ uint i;
+ uint IPAddrCharCount;
+ ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context;
+
+#ifndef CHICAGO
+ uint TotalSize;
+
+ CTEMemCopy(CMsg, CMsg1, sizeof(CMsg1) - 1);
+ TotalSize = sizeof(CMsg1) - 1;
+#endif
+
+ // Convert the IP address into a string.
+ IPAddrCharCount = 0;
+
+ for (i = 0; i < sizeof(IPAddr); i++) {
+ uint CurrentByte;
+
+ CurrentByte = NotifyStruct->ans_addr & 0xff;
+ if (CurrentByte > 99) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
+ CurrentByte %= 100;
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ } else if (CurrentByte > 9) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ }
+
+ IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
+ if (i != (sizeof(IPAddr) - 1))
+ IPAddrBuffer[IPAddrCharCount++] = '.';
+
+ NotifyStruct->ans_addr >>= 8;
+ }
+
+#ifndef CHICAGO
+ CTEMemCopy(&CMsg[TotalSize], IPAddrBuffer, IPAddrCharCount);
+ TotalSize += IPAddrCharCount;
+
+ CTEMemCopy(&CMsg[TotalSize], CMsg2, sizeof(CMsg2) - 1);
+ TotalSize += sizeof(CMsg2) - 1;
+#else
+ IPAddrBuffer[IPAddrCharCount] = '\0';
+#endif
+
+ for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
+ uchar CurrentHalf;
+
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
+ HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
+ HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ if (i != (NotifyStruct->ans_hwaddrlen - 1))
+ HWAddrBuffer[(i*3)+2] = ':';
+ }
+
+#ifndef CHICAGO
+ CTEMemCopy(&CMsg[TotalSize], HWAddrBuffer,
+ (NotifyStruct->ans_hwaddrlen * 3) - 1);
+ TotalSize += (NotifyStruct->ans_hwaddrlen * 3) - 1;
+
+ if (NotifyStruct->ans_shutoff) {
+ CTEMemCopy(&CMsg[TotalSize], CMsg3, sizeof(CMsg3) - 1);
+ TotalSize += sizeof(CMsg3) - 1;
+ }
+
+ CMsg[TotalSize] = '.';
+ CMsg[TotalSize+1] = '\0';
+
+ DisplayPopup(CMsg);
+#else
+ HWAddrBuffer[((NotifyStruct->ans_hwaddrlen * 3) - 1)] = '\0';
+ DisplayConflictPopup(IPAddrBuffer, HWAddrBuffer, NotifyStruct->ans_shutoff);
+ CTEFreeMem(NotifyStruct);
+#endif
+
+#else // VXD
+
+ ARPNotifyStruct *NotifyStruct = (ARPNotifyStruct *)Context;
+ PWCHAR stringList[2];
+ uchar IPAddrBuffer[(sizeof(IPAddr) * 4)];
+ uchar HWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
+ WCHAR unicodeIPAddrBuffer[((sizeof(IPAddr) * 4) + 1)];
+ WCHAR unicodeHWAddrBuffer[(ARP_802_ADDR_LENGTH * 3)];
+ uint i;
+ uint IPAddrCharCount;
+ UNICODE_STRING unicodeString;
+ ANSI_STRING ansiString;
+
+
+ PAGED_CODE();
+
+ //
+ // Convert the IP address into a string.
+ //
+ IPAddrCharCount = 0;
+
+ for (i = 0; i < sizeof(IPAddr); i++) {
+ uint CurrentByte;
+
+ CurrentByte = NotifyStruct->ans_addr & 0xff;
+ if (CurrentByte > 99) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 100) + '0';
+ CurrentByte %= 100;
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ } else if (CurrentByte > 9) {
+ IPAddrBuffer[IPAddrCharCount++] = (CurrentByte / 10) + '0';
+ CurrentByte %= 10;
+ }
+
+ IPAddrBuffer[IPAddrCharCount++] = CurrentByte + '0';
+ if (i != (sizeof(IPAddr) - 1))
+ IPAddrBuffer[IPAddrCharCount++] = '.';
+
+ NotifyStruct->ans_addr >>= 8;
+ }
+
+ //
+ // Convert the hardware address into a string.
+ //
+ for (i = 0; i < NotifyStruct->ans_hwaddrlen; i++) {
+ uchar CurrentHalf;
+
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] >> 4;
+ HWAddrBuffer[i*3] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ CurrentHalf = NotifyStruct->ans_hwaddr[i] & 0x0f;
+ HWAddrBuffer[(i*3)+1] = (uchar)(CurrentHalf < 10 ? CurrentHalf + '0' :
+ (CurrentHalf - 10) + 'A');
+ if (i != (NotifyStruct->ans_hwaddrlen - 1))
+ HWAddrBuffer[(i*3)+2] = ':';
+ }
+
+ //
+ // Unicode the strings.
+ //
+ *unicodeIPAddrBuffer = *unicodeHWAddrBuffer = UNICODE_NULL;
+
+ unicodeString.Buffer = unicodeIPAddrBuffer;
+ unicodeString.Length = 0;
+ unicodeString.MaximumLength = sizeof(WCHAR) * ((sizeof(IPAddr) * 4) + 1);
+ ansiString.Buffer = IPAddrBuffer;
+ ansiString.Length = IPAddrCharCount;
+ ansiString.MaximumLength = IPAddrCharCount;
+
+ RtlAnsiStringToUnicodeString(
+ &unicodeString,
+ &ansiString,
+ FALSE
+ );
+
+ stringList[0] = unicodeIPAddrBuffer;
+
+ unicodeString.Buffer = unicodeHWAddrBuffer;
+ unicodeString.Length = 0;
+ unicodeString.MaximumLength = sizeof(WCHAR) * (ARP_802_ADDR_LENGTH * 3);
+ ansiString.Buffer = HWAddrBuffer;
+ ansiString.Length = (NotifyStruct->ans_hwaddrlen * 3) - 1;
+ ansiString.MaximumLength = NotifyStruct->ans_hwaddrlen * 3;
+
+ RtlAnsiStringToUnicodeString(
+ &unicodeString,
+ &ansiString,
+ FALSE
+ );
+
+ stringList[1] = unicodeHWAddrBuffer;
+
+ //
+ // Kick off a popup and log an event.
+ //
+ if (NotifyStruct->ans_shutoff) {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADDRESS_CONFLICT1,
+ 0,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ IoRaiseInformationalHardError(
+ STATUS_IP_ADDRESS_CONFLICT1,
+ NULL,
+ NULL
+ );
+ }
+ else {
+ CTELogEvent(
+ IPDriverObject,
+ EVENT_TCPIP_ADDRESS_CONFLICT2,
+ 0,
+ 2,
+ stringList,
+ 0,
+ NULL
+ );
+
+ IoRaiseInformationalHardError(
+ STATUS_IP_ADDRESS_CONFLICT2,
+ NULL,
+ NULL
+ );
+ }
+
+ CTEFreeMem(NotifyStruct);
+
+#endif // VXD
+
+ return;
+}
+
+
+//* HandleARPPacket - Process an incoming ARP packet.
+//
+// This is the main routine to process an incoming ARP packet. We look at all ARP frames,
+// and update our cache entry for the source address if one exists. Else, if we are the
+// target we create an entry if one doesn't exist. Finally, we'll handle the opcode,
+// responding if this is a request or sending pending packets if this is a response.
+//
+// Entry: Interface - Pointer to interface structure for this adapter.
+// Header - Pointer to header buffer.
+// HeaderSize - Size of header buffer.
+// ARPHdr - ARP packet header.
+// ARPHdrSize - Size of ARP header.
+// ProtOffset - Offset into original data field of arp header.
+// Will be non-zero if we're using SNAP.
+//
+// Returns: An NDIS_STATUS value to be returned to the NDIS driver.
+//
+NDIS_STATUS
+HandleARPPacket(ARPInterface *Interface, void *Header, uint HeaderSize,
+ ARPHeader UNALIGNED *ARPHdr, uint ARPHdrSize, uint ProtOffset)
+{
+ ARPTableEntry *Entry; // Entry in ARP table
+ CTELockHandle LHandle, TableHandle;
+ RC UNALIGNED *SourceRoute = (RC UNALIGNED *)NULL; // Pointer to Source Route info, if any.
+ uint SourceRouteSize = 0;
+ ulong Now = CTESystemUpTime();
+ uchar LocalAddr;
+ uint LocalAddrAge;
+ uchar *SHAddr, *DHAddr;
+ IPAddr UNALIGNED *SPAddr, *DPAddr;
+ ENetHeader *ENetHdr;
+ TRHeader *TRHdr;
+ FDDIHeader *FHdr;
+ ARCNetHeader *AHdr;
+ ushort MaxMTU;
+ uint UseSNAP;
+ SetAddrControl *SAC;
+ SetAddrRtn Rtn = NULL;
+
+ // We examine all ARP frames. If we find the source address in the ARP table, we'll
+ // update the hardware address and set the state to valid. If we're the
+ // target and he's not in the table, we'll add him. Otherwise if we're the
+ // target and this is a response we'll send any pending packets to him.
+ if (Interface->ai_media != NdisMediumArcnet878_2) {
+ if (ARPHdrSize < sizeof(ARPHeader))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small.
+
+ if (ARPHdr->ah_hw != net_short(ARP_HW_ENET) &&
+ ARPHdr->ah_hw != net_short(ARP_HW_802))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type
+
+ if (ARPHdr->ah_hlen != ARP_802_ADDR_LENGTH)
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length.
+
+ if (Interface->ai_media == NdisMedium802_3 && Interface->ai_snapsize == 0)
+ UseSNAP = FALSE;
+ else
+ UseSNAP = (ProtOffset != 0);
+
+ // Figure out SR size on TR.
+ if (Interface->ai_media == NdisMedium802_5) {
+ // Check for source route information. SR is present if the header
+ // size is greater than the standard TR header size. If the SR is
+ // only an RC field, we ignore it because it came from the same
+ // ring which is the same as no SR.
+
+ if ((HeaderSize - sizeof(TRHeader)) > sizeof(RC)) {
+ SourceRouteSize = HeaderSize - sizeof(TRHeader);
+ SourceRoute = (RC UNALIGNED *)((uchar *)Header +
+ sizeof(TRHeader));
+ }
+ }
+
+ SHAddr = ARPHdr->ah_shaddr;
+ SPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_spaddr;
+ DHAddr = ARPHdr->ah_dhaddr;
+ DPAddr = (IPAddr UNALIGNED *) &ARPHdr->ah_dpaddr;
+
+ } else {
+ if (ARPHdrSize < (sizeof(ARPHeader) - ARCNET_ARPHEADER_ADJUSTMENT))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Frame is too small.
+
+ if (ARPHdr->ah_hw != net_short(ARP_HW_ARCNET))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong HW type
+
+ if (ARPHdr->ah_hlen != 1)
+ return NDIS_STATUS_NOT_RECOGNIZED; // Wrong address length.
+
+ UseSNAP = FALSE;
+ SHAddr = ARPHdr->ah_shaddr;
+ SPAddr = (IPAddr UNALIGNED *)(SHAddr + 1);
+ DHAddr = (uchar *)SPAddr + sizeof(IPAddr);
+ DPAddr = (IPAddr UNALIGNED *)(DHAddr + 1);
+ }
+
+ if (ARPHdr->ah_pro != net_short(ARP_ETYPE_IP))
+ return NDIS_STATUS_NOT_RECOGNIZED; // Unsupported protocol type.
+
+ if (ARPHdr->ah_plen != sizeof(IPAddr))
+ return NDIS_STATUS_NOT_RECOGNIZED;
+
+ if (IP_ADDR_EQUAL(*SPAddr, NULL_IP_ADDR))
+ return NDIS_STATUS_NOT_RECOGNIZED;
+
+ // First, let's see if we have an address conflict.
+ LocalAddrAge = IsLocalAddr(Interface, *SPAddr);
+ if (LocalAddrAge != ARPADDR_NOT_LOCAL) {
+ // The source IP address is one of ours. See if the source h/w address
+ // is ours also.
+ if (ARPHdr->ah_hlen != Interface->ai_addrlen ||
+ CTEMemCmp(SHAddr, Interface->ai_addr, Interface->ai_addrlen) != 0) {
+
+ uint Shutoff;
+ ARPNotifyStruct *NotifyStruct;
+
+ // This isn't from us; we must have an address conflict somewhere.
+ // We always log an error about this. If what triggered this is a
+ // response and the address in conflict is young, we'll turn off
+ // the interface.
+ if (LocalAddrAge != ARPADDR_OLD_LOCAL &&
+ ARPHdr->ah_opcode == net_short(ARP_RESPONSE)) {
+ // Send an arp request with the owner's address to reset the
+ // caches.
+
+ CTEGetLock(&Interface->ai_lock, &LHandle);
+ Interface->ai_state = INTERFACE_DOWN;
+ Interface->ai_adminstate = IF_STATUS_DOWN;
+ if (Interface->ai_ipaddr.aia_context != NULL) {
+ SAC = (SetAddrControl *)Interface->ai_ipaddr.aia_context;
+ Rtn = (SetAddrRtn)SAC->sac_rtn;
+ Interface->ai_ipaddr.aia_context = NULL;
+ }
+ CTEFreeLock(&Interface->ai_lock, LHandle);
+
+ SendARPRequest(Interface, *SPAddr, ARP_RESOLVING_GLOBAL,
+ SHAddr, FALSE); // Send a request.
+
+ Shutoff = TRUE;
+
+ if (Rtn != NULL) {
+ //
+ // this is a dhcp adapter. report the conflict to
+ // CompleteIPSetNTEAddrRequest so IOCTL_IP_SET_ADDRESS will
+ // be completed
+ //
+
+ (*Rtn)(SAC, IP_GENERAL_FAILURE);
+
+ //
+ // don't display a warning dialog in this case - DHCP will
+ // alert the user
+ //
+
+ goto no_dialog;
+ }
+ } else {
+ if (ARPHdr->ah_opcode == net_short(ARP_REQUEST) &&
+ (IsLocalAddr(Interface, *DPAddr) != ARPADDR_NOT_LOCAL)) {
+ // Send a response.
+ SendARPReply(Interface, *SPAddr, *DPAddr, SHAddr,
+ SourceRoute, SourceRouteSize, UseSNAP);
+ }
+ Shutoff = FALSE;
+ }
+
+ // Now allocate a structure, and schedule an event to notify
+ // the user.
+ NotifyStruct = CTEAllocMem(offsetof(ARPNotifyStruct, ans_hwaddr) +
+ ARPHdr->ah_hlen);
+ if (NotifyStruct != NULL) {
+ NotifyStruct->ans_addr = *SPAddr;
+ NotifyStruct->ans_shutoff = Shutoff;
+ NotifyStruct->ans_hwaddrlen = (uint)ARPHdr->ah_hlen;
+ CTEMemCopy(NotifyStruct->ans_hwaddr, SHAddr,
+ ARPHdr->ah_hlen);
+ CTEInitEvent(&NotifyStruct->ans_event, NotifyConflictProc);
+ CTEScheduleEvent(&NotifyStruct->ans_event, NotifyStruct);
+ }
+
+
+ no_dialog:
+ ;
+
+ }
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+
+ CTEGetLock(&Interface->ai_ARPTblLock, &TableHandle);
+ MaxMTU = Interface->ai_mtu;
+
+ LocalAddr = ARPLocalAddr(Interface, *DPAddr);
+ Entry = ARPLookup(Interface, *SPAddr, &LHandle);
+ if (Entry == (ARPTableEntry *)NULL) {
+
+ // Didn't find him, create one if it's for us. The call to ARPLookup
+ // returned with the ARPTblLock held, so we need to free it.
+
+ CTEFreeLock(&Interface->ai_ARPTblLock, TableHandle);
+ if (LocalAddr)
+ Entry = CreateARPTableEntry(Interface, *SPAddr, &LHandle);
+ else
+ return NDIS_STATUS_NOT_RECOGNIZED; // Not in our table, and not for us.
+ } else {
+ CTEFreeLock(&Interface->ai_ARPTblLock, LHandle);
+ LHandle = TableHandle;
+ }
+
+ // At this point, entry should be valid, and we hold the lock on the entry
+ // in LHandle.
+
+ if (Entry != (ARPTableEntry *)NULL) {
+ PNDIS_PACKET Packet; // Packet to be sent.
+
+ // If the entry is already static, we'll want to leave it as static.
+ if (Entry->ate_valid == ALWAYS_VALID)
+ Now = ALWAYS_VALID;
+
+ // OK, we have an entry to use, and hold the lock on it. Fill in the
+ // required fields.
+ switch (Interface->ai_media) {
+
+ case NdisMedium802_3:
+
+ // This is an Ethernet.
+ ENetHdr = (ENetHeader *)Entry->ate_addr;
+
+ CTEMemCopy(ENetHdr->eh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(ENetHdr->eh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+ ENetHdr->eh_type = net_short(ARP_ETYPE_IP);
+
+ // If we're using SNAP on this entry, copy in the SNAP header.
+ if (UseSNAP) {
+ CTEMemCopy(&Entry->ate_addr[sizeof(ENetHeader)], ARPSNAP,
+ sizeof(SNAPHeader));
+ Entry->ate_addrlength = (uchar)(sizeof(ENetHeader) +
+ sizeof(SNAPHeader));
+ *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ } else
+ Entry->ate_addrlength = sizeof(ENetHeader);
+
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now; // Mark last time he was
+ // valid.
+ break;
+
+ case NdisMedium802_5:
+
+ // This is TR.
+ // For token ring we have to deal with source routing. There's
+ // a special case to handle multiple responses for an all-routes
+ // request - if the entry is currently good and we knew it was
+ // valid recently, we won't update the entry.
+
+
+ if (Entry->ate_state != ARP_GOOD ||
+ (Now - Entry->ate_valid) > ARP_RESOLVE_TIMEOUT) {
+
+ TRHdr = (TRHeader *)Entry->ate_addr;
+
+ // We need to update a TR entry.
+ TRHdr->tr_ac = ARP_AC;
+ TRHdr->tr_fc = ARP_FC;
+ CTEMemCopy(TRHdr->tr_daddr, SHAddr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(TRHdr->tr_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+ if (SourceRoute != (RC UNALIGNED *)NULL) {
+ uchar MaxIFieldBits;
+
+ // We have source routing information.
+ CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)],
+ SourceRoute, SourceRouteSize);
+ MaxIFieldBits = (SourceRoute->rc_dlf & RC_LF_MASK) >>
+ LF_BIT_SHIFT;
+ MaxIFieldBits = MIN(MaxIFieldBits, MAX_LF_BITS);
+ MaxMTU = IFieldSize[MaxIFieldBits];
+
+ // The new MTU we've computed is the max I-field size,
+ // which doesn't include source routing info but
+ // does include SNAP info. Subtract off the SNAP size.
+ MaxMTU -= sizeof(SNAPHeader);
+
+ TRHdr->tr_saddr[0] |= TR_RII;
+ (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_dlf ^=
+ RC_DIR;
+ // Make sure it's non-broadcast.
+ (*(RC UNALIGNED *)&Entry->ate_addr[sizeof(TRHeader)]).rc_blen &=
+ RC_LENMASK;
+
+ }
+ CTEMemCopy(&Entry->ate_addr[sizeof(TRHeader)+SourceRouteSize],
+ ARPSNAP, sizeof(SNAPHeader));
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now;
+ Entry->ate_addrlength = (uchar)(sizeof(TRHeader) +
+ SourceRouteSize + sizeof(SNAPHeader));
+ *(ushort *)&Entry->ate_addr[Entry->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ }
+ break;
+ case NdisMediumFddi:
+ FHdr = (FDDIHeader *)Entry->ate_addr;
+
+ FHdr->fh_pri = ARP_FDDI_PRI;
+ CTEMemCopy(FHdr->fh_daddr, SHAddr, ARP_802_ADDR_LENGTH);
+ CTEMemCopy(FHdr->fh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+ CTEMemCopy(&Entry->ate_addr[sizeof(FDDIHeader)], ARPSNAP,
+ sizeof(SNAPHeader));
+ Entry->ate_addrlength = (uchar)(sizeof(FDDIHeader) +
+ sizeof(SNAPHeader));
+ *(ushort UNALIGNED *)&Entry->ate_addr[Entry->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now; // Mark last time he was
+ // valid.
+ break;
+ case NdisMediumArcnet878_2:
+ AHdr = (ARCNetHeader *)Entry->ate_addr;
+ AHdr->ah_saddr = Interface->ai_addr[0];
+ AHdr->ah_daddr = *SHAddr;
+ AHdr->ah_prot = ARP_ARCPROT_IP;
+ Entry->ate_addrlength = sizeof(ARCNetHeader);
+ Entry->ate_state = ARP_GOOD;
+ Entry->ate_valid = Now; // Mark last time he was
+ // valid.
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+
+ // At this point we've updated the entry, and we still hold the lock
+ // on it. If we have a packet that was pending to be sent, send it now.
+ // Otherwise just free the lock.
+
+ Packet = Entry->ate_packet;
+
+ if (Packet != NULL) {
+ // We have a packet to send.
+ CTEAssert(Entry->ate_state == ARP_GOOD);
+
+ Entry->ate_packet = NULL;
+
+ if (ARPSendData(Interface, Packet, Entry, LHandle) != NDIS_STATUS_PENDING)
+ IPSendComplete(Interface->ai_context, Packet, NDIS_STATUS_SUCCESS);
+ } else
+ CTEFreeLock(&Entry->ate_lock, LHandle);
+ }
+
+ // See if the MTU is less than our local one. This should only happen
+ // in the case of token ring source routing.
+ if (MaxMTU < Interface->ai_mtu) {
+ LLIPAddrMTUChange LAM;
+
+ LAM.lam_mtu = MaxMTU;
+ LAM.lam_addr = *SPAddr;
+
+ // It is less. Notify IP.
+ CTEAssert(Interface->ai_media == NdisMedium802_5);
+ IPStatus(Interface->ai_context, LLIP_STATUS_ADDR_MTU_CHANGE,
+ &LAM, sizeof(LLIPAddrMTUChange));
+
+ }
+
+ // At this point we've updated the entry (if we had one), and we've free
+ // all locks. If it's for a local address and it's a request, reply to
+ // it.
+ if (LocalAddr) { // It's for us.
+ if (ARPHdr->ah_opcode == net_short(ARP_REQUEST)) {
+ // It's a request, and we need to respond.
+ SendARPReply(Interface, *SPAddr, *DPAddr,
+ SHAddr, SourceRoute, SourceRouteSize, UseSNAP);
+ }
+ }
+
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+//* InitAdapter - Initialize an adapter.
+//
+// Called when an adapter is open to finish initialization. We set
+// up our lookahead size and packet filter, and we're ready to go.
+//
+// Entry:
+// adapter - Pointer to an adapter structure for the adapter to be
+// initialized.
+//
+// Exit: Nothing
+//
+void
+InitAdapter(ARPInterface *Adapter)
+{
+ NDIS_STATUS Status;
+ CTELockHandle Handle;
+ ARPIPAddr *Addr, *OldAddr;
+
+ if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
+ OID_GEN_CURRENT_LOOKAHEAD, &ARPLookahead, sizeof(ARPLookahead), NULL))
+ != NDIS_STATUS_SUCCESS) {
+ Adapter->ai_state = INTERFACE_DOWN;
+ return;
+ }
+
+ if ((Status = DoNDISRequest(Adapter, NdisRequestSetInformation,
+ OID_GEN_CURRENT_PACKET_FILTER, &Adapter->ai_pfilter, sizeof(uint),
+ NULL)) == NDIS_STATUS_SUCCESS) {
+ Adapter->ai_adminstate = IF_STATUS_UP;
+ Adapter->ai_operstate = IF_STATUS_UP;
+ Adapter->ai_state = INTERFACE_UP;
+ // Now walk through any addresses we have, and ARP for them.
+ CTEGetLock(&Adapter->ai_lock, &Handle);
+ OldAddr = NULL;
+ Addr = &Adapter->ai_ipaddr;
+ do {
+ if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
+ IPAddr Address = Addr->aia_addr;
+
+ Addr->aia_age = ARPADDR_NEW_LOCAL;
+ CTEFreeLock(&Adapter->ai_lock, Handle);
+ OldAddr = Addr;
+ SendARPRequest(Adapter, Address, ARP_RESOLVING_GLOBAL,
+ NULL, TRUE);
+ CTEGetLock(&Adapter->ai_lock, &Handle);
+ }
+ Addr = &Adapter->ai_ipaddr;
+ while (Addr != OldAddr && Addr != NULL)
+ Addr = Addr->aia_next;
+ if (Addr != NULL)
+ Addr = Addr->aia_next;
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Adapter->ai_lock, Handle);
+
+ } else
+ Adapter->ai_state = INTERFACE_DOWN;
+
+}
+
+//** ARPOAComplete - ARP Open adapter complete handler.
+//
+// This routine is called by the NDIS driver when an open adapter
+// call completes. Presumably somebody is blocked waiting for this, so
+// we'll wake him up now.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Status - Final status of command.
+// ErrorStatus - Final error status.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPOAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status, NDIS_STATUS ErrorStatus)
+{
+ ARPInterface *ai = (ARPInterface *)Handle; // For compiler.
+
+ CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status.
+
+}
+
+//** ARPCAComplete - ARP close adapter complete handler.
+//
+// This routine is called by the NDIS driver when a close adapter
+// call completes.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPCAComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
+{
+ ARPInterface *ai = (ARPInterface *)Handle; // For compiler.
+
+ CTESignal(&ai->ai_block, (uint)Status); // Wake him up, and return status.
+
+}
+
+//** ARPSendComplete - ARP send complete handler.
+//
+// This routine is called by the NDIS driver when a send completes.
+// This is a pretty time critical operation, we need to get through here
+// quickly. We'll strip our buffer off and put it back, and call the upper
+// later send complete handler.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Packet - A pointer to the packet that was sent.
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPSendComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status)
+{
+ ARPInterface *Interface = (ARPInterface *)Handle;
+ PacketContext *PC = (PacketContext *)Packet->ProtocolReserved;
+ PNDIS_BUFFER Buffer;
+
+ Interface->ai_qlen--;
+#ifdef VXD
+ CTEAssert(*(int *)&Interface->ai_qlen >= 0);
+#endif
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ Interface->ai_outoctets += Packet->Private.TotalLength;
+ } else {
+ if (Status == NDIS_STATUS_RESOURCES)
+ Interface->ai_outdiscards++;
+ else
+ Interface->ai_outerrors++;
+ }
+
+ // Get first buffer on packet.
+ NdisUnchainBufferAtFront(Packet, &Buffer);
+#ifdef DEBUG
+ if (Buffer == (PNDIS_BUFFER)NULL) // No buffer!
+ DEBUGCHK;
+#endif
+
+ FreeARPBuffer(Interface, Buffer); // Free it up.
+ if (PC->pc_common.pc_owner != PACKET_OWNER_LINK) { // We don't own this one.
+ IPSendComplete(Interface->ai_context, Packet, Status);
+ return;
+ }
+
+ // This packet belongs to us, so free it.
+ NdisFreePacket(Packet);
+
+}
+
+//** ARPTDComplete - ARP transfer data complete handler.
+//
+// This routine is called by the NDIS driver when a transfer data
+// call completes. Since we never transfer data ourselves, this must be
+// from the upper layer. We'll just call his routine and let him deal
+// with it.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Packet - A pointer to the packet used for the TD.
+// Status - Final status of command.
+// BytesCopied - Count of bytes copied.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPTDComplete(NDIS_HANDLE Handle, PNDIS_PACKET Packet, NDIS_STATUS Status,
+ uint BytesCopied)
+{
+ ARPInterface *ai = (ARPInterface *)Handle;
+
+ IPTDComplete(ai->ai_context, Packet, Status, BytesCopied);
+
+}
+
+//** ARPResetComplete - ARP reset complete handler.
+//
+// This routine is called by the NDIS driver when a reset completes.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPResetComplete(NDIS_HANDLE Handle, NDIS_STATUS Status)
+{
+}
+
+//** ARPRequestComplete - ARP request complete handler.
+//
+// This routine is called by the NDIS driver when a general request
+// completes. ARP blocks on all requests, so we'll just wake up
+// whoever's blocked on this request.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Request - A pointer to the request that completed.
+// Status - Final status of command.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPRequestComplete(NDIS_HANDLE Handle, PNDIS_REQUEST Request,
+ NDIS_STATUS Status)
+{
+ ARPInterface *ai = (ARPInterface *)Handle;
+
+ CTESignal(&ai->ai_block, (uint)Status);
+}
+
+//** ARPRcv - ARP receive data handler.
+//
+// This routine is called when data arrives from the NDIS driver.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// Context - NDIS context to be used for TD.
+// Header - Pointer to header
+// HeaderSize - Size of header
+// Data - Pointer to buffer of received data
+// Size - Byte count of data in buffer.
+// TotalSize - Byte count of total packet size.
+//
+// Exit: Status indicating whether or not we took the packet.
+//
+NDIS_STATUS NDIS_API
+ARPRcv(NDIS_HANDLE Handle, NDIS_HANDLE Context, void *Header, uint HeaderSize,
+ void *Data, uint Size, uint TotalSize)
+{
+ ARPInterface *Interface = Handle; // Interface for this driver.
+ ENetHeader UNALIGNED *EHdr = (ENetHeader UNALIGNED *)Header;
+ SNAPHeader UNALIGNED *SNAPHdr;
+ ushort type; // Protocol type
+ uint ProtOffset; // Offset in Data to non-media info.
+ uint NUCast; // TRUE if the frame is not
+ // a unicast frame.
+
+ if (Interface->ai_state == INTERFACE_UP &&
+ HeaderSize >= (uint)Interface->ai_hdrsize) {
+
+ Interface->ai_inoctets += TotalSize;
+
+ if (Interface->ai_media != NdisMediumArcnet878_2) {
+ if (Interface->ai_media == NdisMedium802_3 &&
+ (type = net_short(EHdr->eh_type)) >= MIN_ETYPE)
+ ProtOffset = 0;
+ else {
+ SNAPHdr = (SNAPHeader UNALIGNED *)Data;
+
+ if (Size >= sizeof(SNAPHeader) &&
+ SNAPHdr->sh_dsap == SNAP_SAP &&
+ SNAPHdr->sh_ssap == SNAP_SAP &&
+ SNAPHdr->sh_ctl == SNAP_UI) {
+ type = net_short(SNAPHdr->sh_etype);
+ ProtOffset = sizeof(SNAPHeader);
+ } else {
+ // BUGBUG handle XID/TEST here.
+ Interface->ai_uknprotos++;
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+ }
+ } else {
+ ARCNetHeader UNALIGNED *AH = (ARCNetHeader UNALIGNED *)Header;
+
+ ProtOffset = 0;
+ if (AH->ah_prot == ARP_ARCPROT_IP)
+ type = ARP_ETYPE_IP;
+ else
+ if (AH->ah_prot == ARP_ARCPROT_ARP)
+ type = ARP_ETYPE_ARP;
+ else
+ type = 0;
+ }
+
+ NUCast = ((*((uchar UNALIGNED *)EHdr + Interface->ai_bcastoff) &
+ Interface->ai_bcastmask) == Interface->ai_bcastval) ?
+ AI_NONUCAST_INDEX : AI_UCAST_INDEX;
+
+ if (type == ARP_ETYPE_IP) {
+
+ (Interface->ai_inpcount[NUCast])++;
+ IPRcv(Interface->ai_context, (uchar *)Data+ProtOffset,
+ Size-ProtOffset, TotalSize-ProtOffset, Context, ProtOffset,
+ NUCast);
+ return NDIS_STATUS_SUCCESS;
+ }
+ else {
+ if (type == ARP_ETYPE_ARP) {
+ (Interface->ai_inpcount[NUCast])++;
+ return HandleARPPacket(Interface, Header, HeaderSize,
+ (ARPHeader *)((uchar *)Data+ProtOffset), Size-ProtOffset,
+ ProtOffset);
+ } else {
+ Interface->ai_uknprotos++;
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+ }
+ } else {
+ // Interface is marked as down.
+ return NDIS_STATUS_NOT_RECOGNIZED;
+ }
+}
+
+//** ARPRcvComplete - ARP receive complete handler.
+//
+// This routine is called by the NDIS driver after some number of
+// receives. In some sense, it indicates 'idle time'.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPRcvComplete(NDIS_HANDLE Handle)
+{
+ IPRcvComplete();
+
+}
+
+
+//** ARPStatus - ARP status handler.
+//
+// Called by the NDIS driver when some sort of status change occurs.
+// We take action depending on the type of status.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+// GStatus - General type of status that caused the call.
+// Status - Pointer to a buffer of status specific information.
+// StatusSize - Size of the status buffer.
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPStatus(NDIS_HANDLE Handle, NDIS_STATUS GStatus, void *Status, uint
+ StatusSize)
+{
+ ARPInterface *ai = (ARPInterface *)Handle;
+
+ IPStatus(ai->ai_context, GStatus, Status, StatusSize);
+
+}
+
+//** ARPStatusComplete - ARP status complete handler.
+//
+// A routine called by the NDIS driver so that we can do postprocessing
+// after a status event.
+//
+// Entry:
+// Handle - The binding handle we specified (really a pointer to an AI).
+//
+// Exit: Nothing.
+//
+void NDIS_API
+ARPStatusComplete(NDIS_HANDLE Handle)
+{
+
+}
+
+extern void NDIS_API ARPBindAdapter(PNDIS_STATUS RetStatus,
+ NDIS_HANDLE BindContext, PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2);
+
+extern void NDIS_API ARPUnbindAdapter(PNDIS_STATUS RetStatus,
+ NDIS_HANDLE ProtBindContext, NDIS_HANDLE UnbindContext);
+extern void NDIS_API ARPUnloadProtocol(void);
+
+#ifndef NT
+NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = {
+ NDIS_MAJOR_VERSION,
+ NDIS_MINOR_VERSION,
+ 0,
+ ARPOAComplete,
+ ARPCAComplete,
+ ARPSendComplete,
+ ARPTDComplete,
+ ARPResetComplete,
+ ARPRequestComplete,
+ ARPRcv,
+ ARPRcvComplete,
+ ARPStatus,
+ ARPStatusComplete,
+#ifdef CHICAGO
+ ARPBindAdapter,
+ ARPUnbindAdapter,
+ ARPUnloadProtocol,
+#endif
+ { sizeof(TCP_NAME),
+ sizeof(TCP_NAME),
+ 0
+ }
+};
+#else // NT
+NDIS_PROTOCOL_CHARACTERISTICS ARPCharacteristics = {
+ NDIS_MAJOR_VERSION,
+ NDIS_MINOR_VERSION,
+ 0,
+ ARPOAComplete,
+ ARPCAComplete,
+ ARPSendComplete,
+ ARPTDComplete,
+ ARPResetComplete,
+ ARPRequestComplete,
+ ARPRcv,
+ ARPRcvComplete,
+ ARPStatus,
+ ARPStatusComplete,
+ { sizeof(TCP_NAME),
+ sizeof(TCP_NAME),
+ 0
+#ifdef _PNP_POWER
+ },
+ NULL,
+ ARPBindAdapter,
+ ARPUnbindAdapter,
+ NULL
+#else
+ }
+#endif
+};
+#endif
+
+//* ARPReadNext - Read the next entry in the ARP table.
+//
+// Called by the GetInfo code to read the next ATE in the table. We assume
+// the context passed in is valid, and the caller has the ARP TableLock.
+//
+// Input: Context - Pointer to a IPNMEContext.
+// Interface - Pointer to interface for table to read on.
+// Buffer - Pointer to an IPNetToMediaEntry structure.
+//
+// Returns: TRUE if more data is available to be read, FALSE is not.
+//
+uint
+ARPReadNext(void *Context, ARPInterface *Interface, void *Buffer)
+{
+ IPNMEContext *NMContext = (IPNMEContext *)Context;
+ IPNetToMediaEntry *IPNMEntry = (IPNetToMediaEntry *)Buffer;
+ CTELockHandle Handle;
+ ARPTableEntry *CurrentATE;
+ uint i;
+ ARPTable *Table = Interface->ai_ARPTbl;
+ uint AddrOffset;
+
+ CurrentATE = NMContext->inc_entry;
+
+ // Fill in the buffer.
+ CTEGetLock(&CurrentATE->ate_lock, &Handle);
+ IPNMEntry->inme_index = Interface->ai_index;
+ IPNMEntry->inme_physaddrlen = Interface->ai_addrlen;
+
+
+ switch (Interface->ai_media) {
+ case NdisMedium802_3:
+ AddrOffset = 0;
+ break;
+ case NdisMedium802_5:
+ AddrOffset = offsetof(struct TRHeader, tr_daddr);
+ break;
+ case NdisMediumFddi:
+ AddrOffset = offsetof(struct FDDIHeader, fh_daddr);
+ break;
+ case NdisMediumArcnet878_2:
+ AddrOffset = offsetof(struct ARCNetHeader, ah_daddr);
+ break;
+ default:
+ AddrOffset = 0;
+ break;
+ }
+
+ CTEMemCopy(IPNMEntry->inme_physaddr, &CurrentATE->ate_addr[AddrOffset],
+ Interface->ai_addrlen);
+ IPNMEntry->inme_addr = CurrentATE->ate_dest;
+
+ if (CurrentATE->ate_state == ARP_GOOD)
+ IPNMEntry->inme_type = (CurrentATE->ate_valid == ALWAYS_VALID ?
+ INME_TYPE_STATIC : INME_TYPE_DYNAMIC);
+ else
+ IPNMEntry->inme_type = INME_TYPE_INVALID;
+ CTEFreeLock(&CurrentATE->ate_lock, Handle);
+
+ // We've filled it in. Now update the context.
+ if (CurrentATE->ate_next != NULL) {
+ NMContext->inc_entry = CurrentATE->ate_next;
+ return TRUE;
+ } else {
+ // The next ATE is NULL. Loop through the ARP Table looking for a new
+ // one.
+ i = NMContext->inc_index + 1;
+ while (i < ARP_TABLE_SIZE) {
+ if ((*Table)[i] != NULL) {
+ NMContext->inc_entry = (*Table)[i];
+ NMContext->inc_index = i;
+ return TRUE;
+ break;
+ } else
+ i++;
+ }
+
+ NMContext->inc_index = 0;
+ NMContext->inc_entry = NULL;
+ return FALSE;
+ }
+
+}
+
+//* ARPValidateContext - Validate the context for reading an ARP table.
+//
+// Called to start reading an ARP table sequentially. We take in
+// a context, and if the values are 0 we return information about the
+// first route in the table. Otherwise we make sure that the context value
+// is valid, and if it is we return TRUE.
+// We assume the caller holds the ARPInterface lock.
+//
+// Input: Context - Pointer to a RouteEntryContext.
+// Interface - Pointer to an interface
+// Valid - Where to return information about context being
+// valid.
+//
+// Returns: TRUE if more data to be read in table, FALSE if not. *Valid set
+// to TRUE if input context is valid
+//
+uint
+ARPValidateContext(void *Context, ARPInterface *Interface, uint *Valid)
+{
+ IPNMEContext *NMContext = (IPNMEContext *)Context;
+ uint i;
+ ARPTableEntry *TargetATE;
+ ARPTableEntry *CurrentATE;
+ ARPTable *Table = Interface->ai_ARPTbl;
+
+ i = NMContext->inc_index;
+ TargetATE = NMContext->inc_entry;
+
+ // If the context values are 0 and NULL, we're starting from the beginning.
+ if (i == 0 && TargetATE == NULL) {
+ *Valid = TRUE;
+ do {
+ if ((CurrentATE = (*Table)[i]) != NULL) {
+ break;
+ }
+ i++;
+ } while (i < ARP_TABLE_SIZE);
+
+ if (CurrentATE != NULL) {
+ NMContext->inc_index = i;
+ NMContext->inc_entry = CurrentATE;
+ return TRUE;
+ } else
+ return FALSE;
+
+ } else {
+
+ // We've been given a context. We just need to make sure that it's
+ // valid.
+
+ if (i < ARP_TABLE_SIZE) {
+ CurrentATE = (*Table)[i];
+ while (CurrentATE != NULL) {
+ if (CurrentATE == TargetATE) {
+ *Valid = TRUE;
+ return TRUE;
+ break;
+ } else {
+ CurrentATE = CurrentATE->ate_next;
+ }
+ }
+
+ }
+
+ // If we get here, we didn't find the matching ATE.
+ *Valid = FALSE;
+ return FALSE;
+
+ }
+
+}
+
+#define IFE_FIXED_SIZE offsetof(struct IFEntry, if_descr)
+
+//* ARPQueryInfo - ARP query information handler.
+//
+// Called to query information about the ARP table or statistics about the
+// actual interface.
+//
+// Input: IFContext - Interface context (pointer to an ARPInterface).
+// ID - TDIObjectID for object.
+// Buffer - Buffer to put data into.
+// Size - Pointer to size of buffer. On return, filled with
+// bytes copied.
+// Context - Pointer to context block.
+//
+// Returns: Status of attempt to query information.
+//
+int
+ARPQueryInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size,
+ void *Context)
+{
+ ARPInterface *AI = (ARPInterface *)IFContext;
+ uint Offset = 0;
+ uint BufferSize = *Size;
+ CTELockHandle Handle;
+ uint ContextValid, DataLeft;
+ uint BytesCopied = 0;
+ uchar InfoBuff[sizeof(IFEntry)];
+ uint Entity;
+ uint Instance;
+
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // First, make sure it's possibly an ID we can handle.
+ if ((Entity != AT_ENTITY || Instance != AI->ai_atinst) &&
+ (Entity != IF_ENTITY || Instance != AI->ai_ifinst)) {
+ return TDI_INVALID_REQUEST;
+ }
+
+ *Size = 0; // In case of an error.
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER)
+ return TDI_INVALID_PARAMETER;
+
+ if (ID->toi_class == INFO_CLASS_GENERIC) {
+ if (ID->toi_id == ENTITY_TYPE_ID) {
+ // He's trying to see what type we are.
+ if (BufferSize >= sizeof(uint)) {
+ *(uint *)&InfoBuff[0] = (Entity == AT_ENTITY) ? AT_ARP :
+ IF_MIB;
+ (void)CopyToNdis(Buffer, InfoBuff, sizeof(uint), &Offset);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Might be able to handle this.
+ if (Entity == AT_ENTITY) {
+ // It's an address translation object. It could be a MIB object or
+ // an implementation specific object (the generic objects were handled
+ // above).
+
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ ARPPArpAddr *PArpAddr;
+
+ // It's an implementation specific ID. The only ones we handle
+ // are the PARP_COUNT_ID and the PARP_ENTRY ID.
+
+ if (ID->toi_id == AT_ARP_PARP_COUNT_ID) {
+ // He wants to know the count. Just return that to him.
+ if (BufferSize >= sizeof(uint)) {
+
+ CTEGetLock(&AI->ai_lock, &Handle);
+
+ (void)CopyToNdis(Buffer, (uchar *)&AI->ai_parpcount,
+ sizeof(uint), &Offset);
+
+ CTEFreeLock(&AI->ai_lock, Handle);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ if (ID->toi_id != AT_ARP_PARP_ENTRY_ID)
+ return TDI_INVALID_PARAMETER;
+
+ // It's for Proxy ARP entries. The context should be either NULL
+ // or a pointer to the next one to be read.
+ CTEGetLock(&AI->ai_lock, &Handle);
+
+ PArpAddr = *(ARPPArpAddr **)Context;
+
+ if (PArpAddr != NULL) {
+ ARPPArpAddr *CurrentPARP;
+
+ // Loop through the P-ARP addresses on the interface, and
+ // see if we can find this one.
+ CurrentPARP = AI->ai_parpaddr;
+ while (CurrentPARP != NULL) {
+ if (CurrentPARP == PArpAddr)
+ break;
+ else
+ CurrentPARP = CurrentPARP->apa_next;
+ }
+
+ // If we found a match, PARPAddr points to where to begin
+ // reading. Otherwise, fail the request.
+ if (CurrentPARP == NULL) {
+ // Didn't find a match, so fail the request.
+ CTEFreeLock(&AI->ai_lock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+ } else
+ PArpAddr = AI->ai_parpaddr;
+
+ // PARPAddr points to the next entry to put in the buffer, if
+ // there is one.
+ while (PArpAddr != NULL) {
+ if ((int)(BufferSize - BytesCopied) >=
+ (int)sizeof(ProxyArpEntry)) {
+ ProxyArpEntry *TempPArp;
+
+ TempPArp = (ProxyArpEntry *)InfoBuff;
+ TempPArp->pae_status = PAE_STATUS_VALID;
+ TempPArp->pae_addr = PArpAddr->apa_addr;
+ TempPArp->pae_mask = PArpAddr->apa_mask;
+ BytesCopied += sizeof(ProxyArpEntry);
+ Buffer = CopyToNdis(Buffer, (uchar *)TempPArp,
+ sizeof(ProxyArpEntry), &Offset);
+ PArpAddr = PArpAddr->apa_next;
+ } else
+ break;
+ }
+
+ // We're done copying. Free the lock and return the correct
+ // status.
+ CTEFreeLock(&AI->ai_lock, Handle);
+ *Size = BytesCopied;
+ **(ARPPArpAddr ***)&Context = PArpAddr;
+ return (PArpAddr == NULL) ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW;
+ }
+
+ if (ID->toi_id == AT_MIB_ADDRXLAT_INFO_ID) {
+ AddrXlatInfo *AXI;
+
+ // It's for the count. Just return the number of entries in the
+ // table.
+ if (BufferSize >= sizeof(AddrXlatInfo)) {
+ *Size = sizeof(AddrXlatInfo);
+ AXI = (AddrXlatInfo *)InfoBuff;
+ AXI->axi_count = AI->ai_count;
+ AXI->axi_index = AI->ai_index;
+ (void)CopyToNdis(Buffer, (uchar *)AXI, sizeof(AddrXlatInfo),
+ &Offset);
+ return TDI_SUCCESS;
+ } else
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID) {
+ // He's trying to read the table.
+ // Make sure we have a valid context.
+ CTEGetLock(&AI->ai_ARPTblLock, &Handle);
+ DataLeft = ARPValidateContext(Context, AI, &ContextValid);
+
+ // If the context is valid, we'll continue trying to read.
+ if (!ContextValid) {
+ CTEFreeLock(&AI->ai_ARPTblLock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ while (DataLeft) {
+ // The invariant here is that there is data in the table to
+ // read. We may or may not have room for it. So DataLeft
+ // is TRUE, and BufferSize - BytesCopied is the room left
+ // in the buffer.
+ if ((int)(BufferSize - BytesCopied) >=
+ (int)sizeof(IPNetToMediaEntry)) {
+ DataLeft = ARPReadNext(Context, AI, InfoBuff);
+ BytesCopied += sizeof(IPNetToMediaEntry);
+ Buffer = CopyToNdis(Buffer, InfoBuff,
+ sizeof(IPNetToMediaEntry), &Offset);
+ } else
+ break;
+
+ }
+
+ *Size = BytesCopied;
+
+ CTEFreeLock(&AI->ai_ARPTblLock, Handle);
+ return (!DataLeft ? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
+ }
+
+ return TDI_INVALID_PARAMETER;
+ }
+
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+ // He must be asking for interface level information. See if we support
+ // what he's asking for.
+ if (ID->toi_id == IF_MIB_STATS_ID) {
+ IFEntry *IFE = (IFEntry *)InfoBuff;
+
+
+ // He's asking for statistics. Make sure his buffer is at least big
+ // enough to hold the fixed part.
+
+ if (BufferSize < IFE_FIXED_SIZE) {
+ return TDI_BUFFER_TOO_SMALL;
+ }
+
+ // He's got enough to hold the fixed part. Build the IFEntry structure,
+ // and copy it to his buffer.
+ IFE->if_index = AI->ai_index;
+ switch (AI->ai_media) {
+ case NdisMedium802_3:
+ IFE->if_type = IF_TYPE_ETHERNET;
+ break;
+ case NdisMedium802_5:
+ IFE->if_type = IF_TYPE_TOKENRING;
+ break;
+ case NdisMediumFddi:
+ IFE->if_type = IF_TYPE_FDDI;
+ break;
+ case NdisMediumArcnet878_2:
+ default:
+ IFE->if_type = IF_TYPE_OTHER;
+ break;
+ }
+ IFE->if_mtu = AI->ai_mtu;
+ IFE->if_speed = AI->ai_speed;
+ IFE->if_physaddrlen = AI->ai_addrlen;
+ CTEMemCopy(IFE->if_physaddr,AI->ai_addr, AI->ai_addrlen);
+ IFE->if_adminstatus = (uint)AI->ai_adminstate;
+ IFE->if_operstatus = (uint)AI->ai_operstate;
+ IFE->if_lastchange = AI->ai_lastchange;
+ IFE->if_inoctets = AI->ai_inoctets;
+ IFE->if_inucastpkts = AI->ai_inpcount[AI_UCAST_INDEX];
+ IFE->if_innucastpkts = AI->ai_inpcount[AI_NONUCAST_INDEX];
+ IFE->if_indiscards = AI->ai_indiscards;
+ IFE->if_inerrors = AI->ai_inerrors;
+ IFE->if_inunknownprotos = AI->ai_uknprotos;
+ IFE->if_outoctets = AI->ai_outoctets;
+ IFE->if_outucastpkts = AI->ai_outpcount[AI_UCAST_INDEX];
+ IFE->if_outnucastpkts = AI->ai_outpcount[AI_NONUCAST_INDEX];
+ IFE->if_outdiscards = AI->ai_outdiscards;
+ IFE->if_outerrors = AI->ai_outerrors;
+ IFE->if_outqlen = AI->ai_qlen;
+ IFE->if_descrlen = AI->ai_desclen;
+ Buffer = CopyToNdis(Buffer, (uchar *)IFE, IFE_FIXED_SIZE, &Offset);
+
+ // See if he has room for the descriptor string.
+ if (BufferSize >= (IFE_FIXED_SIZE + AI->ai_desclen)) {
+ // He has room. Copy it.
+ if (AI->ai_desclen != 0) {
+ (void)CopyToNdis(Buffer, AI->ai_desc, AI->ai_desclen, &Offset);
+ }
+ *Size = IFE_FIXED_SIZE + AI->ai_desclen;
+ return TDI_SUCCESS;
+ } else {
+ // Not enough room to copy the desc. string.
+ *Size = IFE_FIXED_SIZE;
+ return TDI_BUFFER_OVERFLOW;
+ }
+
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+}
+
+//* ARPSetInfo - ARP set information handler.
+//
+// The ARP set information handler. We support setting of an I/F admin
+// status, and setting/deleting of ARP table entries.
+//
+// Input: Context - Pointer to I/F to set on.
+// ID - The object ID
+// Buffer - Pointer to buffer containing value to set.
+// Size - Size in bytes of Buffer.
+//
+// Returns: Status of attempt to set information.
+//
+int
+ARPSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ CTELockHandle Handle, EntryHandle;
+ int Status;
+ IFEntry *IFE = (IFEntry *)Buffer;
+ IPNetToMediaEntry *IPNME;
+ ARPTableEntry *PrevATE, *CurrentATE;
+ ARPTable *Table;
+ ENetHeader *Header;
+ uint Entity, Instance;
+
+ Entity = ID->toi_entity.tei_entity;
+ Instance = ID->toi_entity.tei_instance;
+
+ // First, make sure it's possibly an ID we can handle.
+ if ((Entity != AT_ENTITY || Instance != Interface->ai_atinst) &&
+ (Entity != IF_ENTITY || Instance != Interface->ai_ifinst)) {
+ return TDI_INVALID_REQUEST;
+ }
+
+ if (ID->toi_type != INFO_TYPE_PROVIDER) {
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Might be able to handle this.
+ if (Entity == IF_ENTITY) {
+
+ // It's for the I/F level, see if it's for the statistics.
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+ if (ID->toi_id == IF_MIB_STATS_ID) {
+ // It's for the stats. Make sure it's a valid size.
+ if (Size >= IFE_FIXED_SIZE) {
+ // It's a valid size. See what he wants to do.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+ switch (IFE->if_adminstatus) {
+ case IF_STATUS_UP:
+ // He's marking it up. If the operational state is
+ // alse up, mark the whole interface as up.
+ Interface->ai_adminstate = IF_STATUS_UP;
+ if (Interface->ai_operstate == IF_STATUS_UP)
+ Interface->ai_state = INTERFACE_UP;
+ Status = TDI_SUCCESS;
+ break;
+ case IF_STATUS_DOWN:
+ // He's taking it down. Mark both the admin state and
+ // the interface state down.
+ Interface->ai_adminstate = IF_STATUS_DOWN;
+ Interface->ai_state = INTERFACE_DOWN;
+ Status = TDI_SUCCESS;
+ break;
+ case IF_STATUS_TESTING:
+ // He's trying to cause up to do testing, which we
+ // don't support. Just return success.
+ Status = TDI_SUCCESS;
+ break;
+ default:
+ Status = TDI_INVALID_PARAMETER;
+ break;
+ }
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return Status;
+ } else
+ return TDI_INVALID_PARAMETER;
+ } else {
+ return TDI_INVALID_PARAMETER;
+ }
+ }
+
+ // Not for the interface level. See if it's an implementation or protocol
+ // class.
+ if (ID->toi_class == INFO_CLASS_IMPLEMENTATION) {
+ ProxyArpEntry *PArpEntry;
+ ARPIPAddr *Addr;
+ IPAddr AddAddr;
+ IPMask Mask;
+
+ // It's for the implementation. It should be the proxy-ARP ID.
+ if (ID->toi_id != AT_ARP_PARP_ENTRY_ID || Size < sizeof(ProxyArpEntry))
+ return TDI_INVALID_PARAMETER;
+
+ PArpEntry = (ProxyArpEntry *)Buffer;
+ AddAddr = PArpEntry->pae_addr;
+ Mask = PArpEntry->pae_mask;
+
+ // See if he's trying to add or delete a proxy arp entry.
+ if (PArpEntry->pae_status == PAE_STATUS_VALID) {
+ // We're trying to add an entry. We won't allow an entry
+ // to be added that we believe to be invalid or conflicting
+ // with our local addresses.
+
+ if (!IP_ADDR_EQUAL(AddAddr & Mask, AddAddr) ||
+ IP_ADDR_EQUAL(AddAddr, NULL_IP_ADDR) ||
+ IP_ADDR_EQUAL(AddAddr, IP_LOCAL_BCST) ||
+ CLASSD_ADDR(AddAddr))
+ return TDI_INVALID_PARAMETER;
+
+ // Walk through the list of addresses on the interface, and see
+ // if they would match the AddAddr. If so, fail the request.
+ CTEGetLock(&Interface->ai_lock, &Handle);
+
+ if (IsBCastOnIF(Interface, AddAddr & Mask)) {
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ return TDI_INVALID_PARAMETER;
+ }
+
+ Addr = &Interface->ai_ipaddr;
+ do {
+ if (!IP_ADDR_EQUAL(Addr->aia_addr, NULL_IP_ADDR)) {
+ if (IP_ADDR_EQUAL(Addr->aia_addr & Mask, AddAddr))
+ break;
+ }
+ Addr = Addr->aia_next;
+ } while (Addr != NULL);
+
+ CTEFreeLock(&Interface->ai_lock, Handle);
+ if (Addr != NULL)
+ return TDI_INVALID_PARAMETER;
+
+ // At this point, we believe we're ok. Try to add the address.
+ if (ARPAddAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask, NULL))
+ return TDI_SUCCESS;
+ else
+ return TDI_NO_RESOURCES;
+ } else {
+ if (PArpEntry->pae_status == PAE_STATUS_INVALID) {
+ // He's trying to delete a proxy ARP address.
+ if (ARPDeleteAddr(Interface, LLIP_ADDR_PARP, AddAddr, Mask))
+ return TDI_SUCCESS;
+ }
+ return TDI_INVALID_PARAMETER;
+ }
+
+ }
+
+ if (ID->toi_class != INFO_CLASS_PROTOCOL)
+ return TDI_INVALID_PARAMETER;
+
+
+ if (ID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID &&
+ Size >= sizeof(IPNetToMediaEntry)) {
+ // He does want to set an ARP table entry. See if he's trying to
+ // create or delete one.
+
+ IPNME = (IPNetToMediaEntry *)Buffer;
+ if (IPNME->inme_type == INME_TYPE_INVALID) {
+ uint Index = ARP_HASH(IPNME->inme_addr);
+
+ // We're trying to delete an entry. See if we can find it,
+ // and then delete it.
+ CTEGetLock(&Interface->ai_ARPTblLock, &Handle);
+ Table = Interface->ai_ARPTbl;
+ PrevATE = STRUCT_OF(ARPTableEntry, &((*Table)[Index]), ate_next);
+ CurrentATE = (*Table)[Index];
+ while (CurrentATE != (ARPTableEntry *)NULL) {
+ if (CurrentATE->ate_dest == IPNME->inme_addr) {
+ // Found him. Break out of the loop.
+ break;
+ } else {
+ PrevATE = CurrentATE;
+ CurrentATE = CurrentATE->ate_next;
+ }
+ }
+
+ if (CurrentATE != NULL) {
+ CTEGetLock(&CurrentATE->ate_lock, &EntryHandle);
+ RemoveARPTableEntry(PrevATE, CurrentATE);
+ Interface->ai_count--;
+ CTEFreeLock(&CurrentATE->ate_lock, EntryHandle);
+
+ if (CurrentATE->ate_packet != NULL)
+ IPSendComplete(Interface->ai_context, CurrentATE->ate_packet,
+ NDIS_STATUS_SUCCESS);
+
+ CTEFreeMem(CurrentATE);
+ Status = TDI_SUCCESS;
+ } else
+ Status = TDI_INVALID_PARAMETER;
+
+ CTEFreeLock(&Interface->ai_ARPTblLock, Handle);
+ return Status;
+ }
+
+ // We're not trying to delete. See if we're trying to create.
+ if (IPNME->inme_type != INME_TYPE_DYNAMIC &&
+ IPNME->inme_type != INME_TYPE_STATIC) {
+ // Not creating, return an error.
+ return TDI_INVALID_PARAMETER;
+ }
+
+ // Make sure he's trying to create a valid address.
+ if (IPNME->inme_physaddrlen != Interface->ai_addrlen)
+ return TDI_INVALID_PARAMETER;
+
+ // We're trying to create an entry. Call CreateARPTableEntry to create
+ // one, and fill it in.
+ CurrentATE = CreateARPTableEntry(Interface, IPNME->inme_addr, &Handle);
+ if (CurrentATE == NULL) {
+ return TDI_NO_RESOURCES;
+ }
+
+ // We've created or found an entry. Fill it in.
+ Header = (ENetHeader *)CurrentATE->ate_addr;
+
+ switch (Interface->ai_media) {
+ case NdisMedium802_5:
+ {
+ TRHeader *Temp = (TRHeader *)Header;
+
+ // Fill in the TR specific parts, and set the length to the
+ // size of a TR header.
+
+ Temp->tr_ac = ARP_AC;
+ Temp->tr_fc = ARP_FC;
+ CTEMemCopy(&Temp->tr_saddr[ARP_802_ADDR_LENGTH], ARPSNAP,
+ sizeof(SNAPHeader));
+
+ Header = (ENetHeader *)&Temp->tr_daddr;
+ CurrentATE->ate_addrlength = sizeof(TRHeader) +
+ sizeof(SNAPHeader);
+ }
+ break;
+ case NdisMedium802_3:
+ CurrentATE->ate_addrlength = sizeof(ENetHeader);
+ break;
+ case NdisMediumFddi:
+ {
+ FDDIHeader *Temp = (FDDIHeader *)Header;
+
+ Temp->fh_pri = ARP_FDDI_PRI;
+ CTEMemCopy(&Temp->fh_saddr[ARP_802_ADDR_LENGTH], ARPSNAP,
+ sizeof(SNAPHeader));
+ Header = (ENetHeader *)&Temp->fh_daddr;
+ CurrentATE->ate_addrlength = sizeof(FDDIHeader) +
+ sizeof(SNAPHeader);
+ }
+ break;
+ case NdisMediumArcnet878_2:
+ {
+ ARCNetHeader *Temp = (ARCNetHeader *)Header;
+
+ Temp->ah_saddr = Interface->ai_addr[0];
+ Temp->ah_daddr = IPNME->inme_physaddr[0];
+ Temp->ah_prot = ARP_ARCPROT_IP;
+ CurrentATE->ate_addrlength = sizeof(ARCNetHeader);
+ }
+ break;
+ default:
+ DEBUGCHK;
+ break;
+ }
+
+
+ // Copy in the source and destination addresses.
+
+ if (Interface->ai_media != NdisMediumArcnet878_2) {
+ CTEMemCopy(Header->eh_daddr, IPNME->inme_physaddr,
+ ARP_802_ADDR_LENGTH);
+ CTEMemCopy(Header->eh_saddr, Interface->ai_addr,
+ ARP_802_ADDR_LENGTH);
+
+ // Now fill in the Ethertype.
+ *(ushort *)&CurrentATE->ate_addr[CurrentATE->ate_addrlength-2] =
+ net_short(ARP_ETYPE_IP);
+ }
+
+ // If he's creating a static entry, mark it as always valid. Otherwise
+ // mark him as valid now.
+ if (IPNME->inme_type == INME_TYPE_STATIC)
+ CurrentATE->ate_valid = ALWAYS_VALID;
+ else
+ CurrentATE->ate_valid = CTESystemUpTime();
+
+ CurrentATE->ate_state = ARP_GOOD;
+
+ CTEFreeLock(&CurrentATE->ate_lock, Handle);
+ return TDI_SUCCESS;
+
+ }
+
+ return TDI_INVALID_PARAMETER;
+
+
+}
+
+
+static uint ARPPackets = ARP_DEFAULT_PACKETS;
+static uint ARPBuffers = ARP_DEFAULT_BUFFERS;
+
+#pragma BEGIN_INIT
+//** ARPInit - Initialize the ARP module.
+//
+// This functions intializes all of the ARP module, including allocating
+// the ARP table and any other necessary data structures.
+//
+// Entry: nothing.
+//
+// Exit: Returns 0 if we fail to init., !0 if we succeed.
+//
+int
+ARPInit()
+{
+ NDIS_STATUS Status; // Status for NDIS calls.
+
+// BUGBUG - Get configuration information dynamically.
+
+#ifdef NT
+ RtlInitUnicodeString(&(ARPCharacteristics.Name), ARPName);
+#else // NT
+ ARPCharacteristics.Name.Buffer = ARPName;
+#endif // NT
+
+ NdisRegisterProtocol(&Status, &ARPHandle, (NDIS_PROTOCOL_CHARACTERISTICS *)
+ &ARPCharacteristics, sizeof(ARPCharacteristics));
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+ return(1);
+ }
+ else {
+ return(0);
+ }
+}
+#pragma END_INIT
+
+#ifndef CHICAGO
+#pragma BEGIN_INIT
+#else
+#pragma code_seg("_LTEXT", "LCODE")
+#endif
+
+//* FreeARPInterface - Free an ARP interface
+//
+// Called in the event of some sort of initialization failure. We free all
+// the memory associated with an ARP interface.
+//
+// Entry: Interface - Pointer to interface structure to be freed.
+//
+// Returns: Nothing.
+//
+void
+FreeARPInterface(ARPInterface *Interface)
+{
+ NDIS_STATUS Status;
+ ARPBufferTracker *Tracker;
+ ARPTable *Table; // ARP table.
+ uint i; // Index variable.
+ ARPTableEntry *ATE;
+ CTELockHandle LockHandle;
+ NDIS_HANDLE Handle;
+
+ CTEStopTimer(&Interface->ai_timer);
+
+// If we're bound to the adapter, close it now.
+ CTEInitBlockStruc(&Interface->ai_block);
+
+ CTEGetLock(&Interface->ai_lock, &LockHandle);
+ if (Interface->ai_handle != (NDIS_HANDLE)NULL) {
+ Handle = Interface->ai_handle;
+ Interface->ai_handle = NULL;
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+
+ NdisCloseAdapter(&Status, Handle);
+
+ if (Status == NDIS_STATUS_PENDING)
+ Status = CTEBlock(&Interface->ai_block);
+ } else {
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+ }
+
+ // First free any outstanding ARP table entries.
+ Table = Interface->ai_ARPTbl;
+ if (Table != NULL) {
+ for (i = 0; i < ARP_TABLE_SIZE;i++) {
+ while ((*Table)[i] != NULL) {
+ ATE = (*Table)[i];
+ RemoveARPTableEntry(STRUCT_OF(ARPTableEntry, &((*Table)[i]),
+ ate_next),ATE);
+ CTEFreeMem(ATE);
+ }
+ }
+ CTEFreeMem(Table);
+ }
+
+ Interface->ai_ARPTbl = NULL;
+
+ if (Interface->ai_ppool != (NDIS_HANDLE)NULL)
+ NdisFreePacketPool(Interface->ai_ppool);
+
+ if (Interface->ai_bpool != (NDIS_HANDLE)NULL)
+ NdisFreeBufferPool(Interface->ai_bpool);
+
+ Tracker = Interface->ai_buflist;
+ while (Tracker != NULL) {
+ Interface->ai_buflist = Tracker->abt_next;
+ NdisFreeBufferPool(Tracker->abt_handle);
+ CTEFreeMem(Tracker->abt_buffer);
+ CTEFreeMem(Tracker);
+ Tracker = Interface->ai_buflist;
+ }
+
+ if (Interface->ai_bbbase != (uchar *)NULL)
+ CTEFreeMem(Interface->ai_bbbase);
+
+ // Free the interface itself.
+ CTEFreeMem(Interface);
+}
+
+//** ARPOpen - Open an adapter for reception.
+//
+// This routine is called when the upper layer is done initializing and wishes to
+// begin receiveing packets. The adapter is actually 'open', we just call InitAdapter
+// to set the packet filter and lookahead size.
+//
+// Input: Context - Interface pointer we gave to IP earlier.
+//
+// Returns: Nothing
+//
+void
+ARPOpen(void *Context)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ InitAdapter(Interface); // Set the packet filter - we'll begin receiving.
+}
+
+//* ARPGetEList - Get the entity list.
+//
+// Called at init time to get an entity list. We fill our stuff in, and
+// then call the interfaces below us to allow them to do the same.
+//
+// Input: EntityList - Pointer to entity list to be filled in.
+// Count - Pointer to number of entries in the list.
+//
+// Returns Status of attempt to get the info.
+//
+int
+ARPGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
+{
+ ARPInterface *Interface = (ARPInterface *)Context;
+ uint ECount;
+ uint MyATBase;
+ uint MyIFBase;
+ uint i;
+
+ ECount = *Count;
+
+ // Walk down the list, looking for existing AT or IF entities, and
+ // adjust our base instance accordingly.
+
+ MyATBase = 0;
+ MyIFBase = 0;
+ for (i = 0; i < ECount; i++, EntityList++) {
+ if (EntityList->tei_entity == AT_ENTITY)
+ MyATBase = MAX(MyATBase, EntityList->tei_instance + 1);
+ else
+ if (EntityList->tei_entity == IF_ENTITY)
+ MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1);
+ }
+
+ // EntityList points to the start of where we want to begin filling in.
+ // Make sure we have enough room. We need one for the ICMP instance,
+ // and one for the CL_NL instance.
+
+ if ((ECount + 2) > MAX_TDI_ENTITIES)
+ return FALSE;
+
+ // At this point we've figure out our base instance. Save for later use.
+ Interface->ai_atinst = MyATBase;
+ Interface->ai_ifinst = MyIFBase;
+
+ // Now fill it in.
+ EntityList->tei_entity = AT_ENTITY;
+ EntityList->tei_instance = MyATBase;
+ EntityList++;
+ EntityList->tei_entity = IF_ENTITY;
+ EntityList->tei_instance = MyIFBase;
+ *Count += 2;
+
+ return TRUE;
+}
+
+
+extern uint UseEtherSNAP(PNDIS_STRING Name);
+extern void GetAlwaysSourceRoute(uint *pArpAlwaysSourceRoute, uint *pIPAlwaysSourceRoute);
+extern uint GetArpCacheLife(void);
+
+
+//** ARPRegister - Register a protocol with the ARP module.
+//
+// We register a protocol for ARP processing. We also open the
+// NDIS adapter here.
+//
+// Note that much of the information passed in here is unused, as
+// ARP currently only works with IP.
+//
+// Entry:
+// Adapter - Name of the adapter to bind to.
+// IPContext - Value to be passed to IP on upcalls.
+//
+#ifndef _PNP_POWER
+int
+ARPRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn,
+ IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn,
+ IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound)
+#else
+int
+ARPRegister(PNDIS_STRING Adapter, uint *Flags, struct ARPInterface **Interface)
+#endif
+{
+ ARPInterface *ai; // Pointer to interface struct. for this
+ // interface.
+ NDIS_STATUS Status, OpenStatus; // Status values.
+ uint i = 0; // Medium index.
+ NDIS_MEDIUM MediaArray[MAX_MEDIA];
+ uchar sbsize;
+ uchar *buffer; // Pointer to our buffers.
+ uint mss;
+ uint speed;
+ uint Needed;
+ uint MacOpts;
+ uchar bcastmask, bcastval, bcastoff, addrlen, hdrsize, snapsize;
+ uint OID;
+ uint PF;
+ PNDIS_BUFFER Buffer;
+
+ if ((ai = CTEAllocMem(sizeof(ARPInterface))) == (ARPInterface *)NULL)
+ return FALSE; // Couldn't allocate memory for this one.
+
+#ifdef _PNP_POWER
+ *Interface = ai;
+#endif
+
+ CTEMemSet(ai, 0, sizeof(ARPInterface));
+ CTEInitTimer(&ai->ai_timer);
+
+#ifdef NT
+ ExInitializeSListHead(&ai->ai_sblist);
+#endif
+
+
+ MediaArray[MEDIA_DIX] = NdisMedium802_3;
+ MediaArray[MEDIA_TR] = NdisMedium802_5;
+ MediaArray[MEDIA_FDDI] = NdisMediumFddi;
+ MediaArray[MEDIA_ARCNET] = NdisMediumArcnet878_2;
+
+ // Initialize this adapter interface structure.
+ ai->ai_state = INTERFACE_INIT;
+ ai->ai_adminstate = IF_STATUS_DOWN;
+ ai->ai_operstate = IF_STATUS_DOWN;
+ ai->ai_bcast = IP_LOCAL_BCST;
+ ai->ai_maxhdrs = ARP_DEFAULT_MAXHDRS;
+
+#ifndef _PNP_POWER
+ ai->ai_index = NumIFBound + 1;
+ ai->ai_context = IPContext;
+ Info->lip_context = ai;
+ Info->lip_transmit = ARPTransmit;
+ Info->lip_transfer = ARPXferData;
+ Info->lip_close = ARPClose;
+ Info->lip_addaddr = ARPAddAddr;
+ Info->lip_deladdr = ARPDeleteAddr;
+ Info->lip_invalidate = ARPInvalidate;
+ Info->lip_open = ARPOpen;
+ Info->lip_qinfo = ARPQueryInfo;
+ Info->lip_setinfo = ARPSetInfo;
+ Info->lip_getelist = ARPGetEList;
+
+ Info->lip_index = ai->ai_index;
+#endif
+
+ // Initialize the locks.
+ CTEInitLock(&ai->ai_lock);
+ CTEInitLock(&ai->ai_ARPTblLock);
+
+ GetAlwaysSourceRoute(&sArpAlwaysSourceRoute, &sIPAlwaysSourceRoute);
+
+ ArpCacheLife = GetArpCacheLife();
+
+ if (!ArpCacheLife) {
+ ArpCacheLife = 1;
+ }
+
+ ArpCacheLife = (ArpCacheLife * 1000L) / ARP_TIMER_TIME;
+
+ // Allocate the buffer and packet pools.
+ NdisAllocatePacketPool(&Status, &ai->ai_ppool, ARPPackets, sizeof(struct PCCommon));
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ NdisAllocateBufferPool(&Status, &ai->ai_bpool, ARPBuffers);
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // Allocate the ARP table
+ if ((ai->ai_ARPTbl = (ARPTable *)CTEAllocMem(ARP_TABLE_SIZE * sizeof(ARPTableEntry *))) ==
+ (ARPTable *)NULL) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ //
+ // NULL out the pointers
+ //
+ CTEMemSet(ai->ai_ARPTbl, 0, ARP_TABLE_SIZE * sizeof(ARPTableEntry *));
+
+ CTEInitBlockStruc(&ai->ai_block);
+
+ // Open the NDIS adapter.
+ NdisOpenAdapter(&Status, &OpenStatus, &ai->ai_handle, &i, MediaArray,
+ MAX_MEDIA, ARPHandle, ai, Adapter, 0, NULL);
+
+ // Block for open to complete.
+ if (Status == NDIS_STATUS_PENDING)
+ Status = (NDIS_STATUS)CTEBlock(&ai->ai_block);
+
+ ai->ai_media = MediaArray[i]; // Fill in media type.
+
+ // Open adapter completed. If it succeeded, we'll finish our intialization.
+ // If it failed, bail out now.
+ if (Status != NDIS_STATUS_SUCCESS) {
+ ai->ai_handle = NULL;
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // Read the local address.
+ switch (ai->ai_media) {
+ case NdisMedium802_3:
+ addrlen = ARP_802_ADDR_LENGTH;
+ bcastmask = ENET_BCAST_MASK;
+ bcastval = ENET_BCAST_VAL;
+ bcastoff = ENET_BCAST_OFF;
+ OID = OID_802_3_CURRENT_ADDRESS;
+ sbsize = ARP_MAX_MEDIA_ENET;
+ hdrsize = sizeof(ENetHeader);
+ if (!UseEtherSNAP(Adapter)) {
+ snapsize = 0;
+ } else {
+ snapsize = sizeof(SNAPHeader);
+ sbsize += sizeof(SNAPHeader);
+ }
+
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED |
+ NDIS_PACKET_TYPE_MULTICAST;
+ break;
+ case NdisMedium802_5:
+ addrlen = ARP_802_ADDR_LENGTH;
+ bcastmask = TR_BCAST_MASK;
+ bcastval = TR_BCAST_VAL;
+ bcastoff = TR_BCAST_OFF;
+ OID = OID_802_5_CURRENT_ADDRESS;
+ sbsize = ARP_MAX_MEDIA_TR;
+ hdrsize = sizeof(TRHeader);
+ snapsize = sizeof(SNAPHeader);
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
+ break;
+ case NdisMediumFddi:
+ addrlen = ARP_802_ADDR_LENGTH;
+ bcastmask = FDDI_BCAST_MASK;
+ bcastval = FDDI_BCAST_VAL;
+ bcastoff = FDDI_BCAST_OFF;
+ OID = OID_FDDI_LONG_CURRENT_ADDR;
+ sbsize = ARP_MAX_MEDIA_FDDI;
+ hdrsize = sizeof(FDDIHeader);
+ snapsize = sizeof(SNAPHeader);
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED |
+ NDIS_PACKET_TYPE_MULTICAST;
+ break;
+ case NdisMediumArcnet878_2:
+ addrlen = 1;
+ bcastmask = ARC_BCAST_MASK;
+ bcastval = ARC_BCAST_VAL;
+ bcastoff = ARC_BCAST_OFF;
+ OID = OID_ARCNET_CURRENT_ADDRESS;
+ sbsize = ARP_MAX_MEDIA_ARC;
+ hdrsize = sizeof(ARCNetHeader);
+ snapsize = 0;
+ PF = NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED;
+ break;
+ default:
+ DEBUGCHK;
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ ai->ai_bcastmask = bcastmask;
+ ai->ai_bcastval = bcastval;
+ ai->ai_bcastoff = bcastoff;
+ ai->ai_addrlen = addrlen;
+ ai->ai_hdrsize = hdrsize;
+ ai->ai_snapsize = snapsize;
+ ai->ai_pfilter = PF;
+
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID,
+ ai->ai_addr, addrlen, NULL);
+
+ if (Status != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+#ifndef _PNP_POWER
+ Info->lip_addrlen = addrlen;
+ Info->lip_addr = ai->ai_addr;
+#endif
+
+ // Read the maximum frame size.
+ if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_MAXIMUM_FRAME_SIZE, &mss, sizeof(mss), NULL)) != NDIS_STATUS_SUCCESS) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // If this is token ring, figure out the RC len stuff now.
+ mss -= (uint)ai->ai_snapsize;
+
+ if (ai->ai_media == NdisMedium802_5) {
+ mss -= (sizeof(RC) + (ARP_MAX_RD * sizeof(ushort)));
+ } else {
+ if (ai->ai_media == NdisMediumFddi) {
+ mss = MIN(mss, ARP_FDDI_MSS);
+ }
+ }
+
+ ai->ai_mtu = (ushort)mss;
+
+#ifndef _PNP_POWER
+ Info->lip_mss = mss;
+#endif
+
+ // Read the speed for local purposes.
+ if ((Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_LINK_SPEED, &speed, sizeof(speed), NULL)) == NDIS_STATUS_SUCCESS) {
+ ai->ai_speed = speed * 100L;
+#ifndef _PNP_POWER
+ Info->lip_speed = ai->ai_speed;
+#endif
+ }
+
+ // Read and save the options.
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation, OID_GEN_MAC_OPTIONS,
+ &MacOpts, sizeof(MacOpts), NULL);
+
+ if (Status != NDIS_STATUS_SUCCESS)
+#ifndef _PNP_POWER
+ Info->lip_flags = 0;
+#else
+ *Flags = 0;
+#endif
+ else
+#ifndef _PNP_POWER
+ Info->lip_flags =
+#else
+ *Flags =
+#endif
+ (MacOpts & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) ? LIP_COPY_FLAG : 0;
+
+ // Read and store the vendor description string.
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_VENDOR_DESCRIPTION, &ai->ai_desc, 0, &Needed);
+
+ if ((Status == NDIS_STATUS_INVALID_LENGTH) ||
+ (Status == NDIS_STATUS_BUFFER_TOO_SHORT)) {
+ // We know the size we need. Allocate a buffer.
+ buffer = CTEAllocMem(Needed);
+ if (buffer != NULL) {
+ Status = DoNDISRequest(ai, NdisRequestQueryInformation,
+ OID_GEN_VENDOR_DESCRIPTION, buffer, Needed, NULL);
+ if (Status == NDIS_STATUS_SUCCESS) {
+ ai->ai_desc = buffer;
+ ai->ai_desclen = Needed;
+ }
+ }
+ }
+
+ // Allocate our small and big buffer pools.
+
+ if ((sbsize & 0x3)) {
+ //
+ // Must 32 bit align the buffers so pointers to them will be aligned.
+ //
+ sbsize = ((sbsize >> 2) + 1) << 2;
+ }
+
+ ai->ai_sbsize = sbsize;
+
+ // Pre-prime the ARP header buffer list.
+ Buffer = GrowARPHeaders(ai);
+ if (Buffer != NULL) {
+ FreeARPBuffer(ai, Buffer);
+ }
+
+
+ if ((buffer = CTEAllocMem((sbsize+sizeof(ARPHeader)) * ARPPackets)) == (uchar *)NULL) {
+ FreeARPInterface(ai);
+ return FALSE;
+ }
+
+ // Link big buffers into the list.
+ ai->ai_bbbase = buffer;
+ ai->ai_bblist = (uchar *)NULL;
+ for (i = 0; i < ARPPackets; i++) {
+ *(char **)&*buffer = ai->ai_bblist;
+ ai->ai_bblist = buffer;
+ buffer += sbsize+sizeof(ARPHeader);
+ }
+
+ // Everything's set up, so get the ARP timer running.
+ CTEStartTimer(&ai->ai_timer, ARP_TIMER_TIME, ARPTimeout, ai);
+
+ return TRUE;
+
+}
+
+#ifndef CHICAGO
+#pragma END_INIT
+#endif
+
+#ifdef _PNP_POWER
+
+//* ARPDynRegister - Dynamically register IP.
+//
+// Called by IP when he's about done binding to register with us. Since we
+// call him directly, we don't save his info here. We do keep his context
+// and index number.
+//
+// Input: See ARPRegister
+//
+// Returns: Nothing.
+//
+int
+ARPDynRegister(PNDIS_STRING Adapter, void *IPContext, IPRcvRtn RcvRtn,
+ IPTxCmpltRtn TxCmpltRtn, IPStatusRtn StatusRtn, IPTDCmpltRtn TDCmpltRtn,
+ IPRcvCmpltRtn RcvCmpltRtn, struct LLIPBindInfo *Info, uint NumIFBound)
+{
+ ARPInterface *Interface = (ARPInterface *)Info->lip_context;
+
+ Interface->ai_context = IPContext;
+ Interface->ai_index = NumIFBound;
+
+ return TRUE;
+}
+
+//* ARPBindAdapter - Bind and initialize an adapter.
+//
+// Called in a PNP environment to initialize and bind an adapter. We open
+// the adapter and get it running, and then we call up to IP to tell him
+// about it. IP will initialize, and if all goes well call us back to start
+// receiving.
+//
+// Input: RetStatus - Where to return the status of this call.
+// BindContext - Handle to use for calling BindAdapterComplete.
+// AdapterName - Pointer to name of adapter.
+// SS1 - System specific 1 parameter.
+// SS2 - System specific 2 parameter.
+//
+// Returns: Nothing.
+//
+void NDIS_API
+ARPBindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE BindContext,
+ PNDIS_STRING AdapterName, PVOID SS1, PVOID SS2)
+{
+ uint Flags; // MAC binding flags.
+ ARPInterface *Interface; // Newly created interface.
+ PNDIS_STRING ConfigName; // Name used by IP for config. info.
+ IP_STATUS Status; // State of IPAddInterface call.
+ LLIPBindInfo BindInfo; // Binding informatio for IP.
+ NDIS_HANDLE Handle ;
+
+ CTERefillMem();
+
+ if (!OpenIFConfig(SS1, &Handle)) {
+ *RetStatus = NDIS_STATUS_FAILURE;
+ return;
+ }
+
+ // If IsLLInterfaceValueNull is FALSE then this means that some other ARP module is
+ // used for this device so we skip it.
+ //
+ if (IsLLInterfaceValueNull(Handle) == FALSE) {
+ *RetStatus = NDIS_STATUS_FAILURE;
+ CloseIFConfig(Handle);
+ return ;
+ }
+
+ CloseIFConfig(Handle);
+
+
+ // First, open the adapter and get the info.
+ if (!ARPRegister(AdapterName, &Flags, &Interface)) {
+ *RetStatus = NDIS_STATUS_FAILURE;
+ return;
+ }
+
+ CTERefillMem();
+
+ // OK, we're opened the adapter. Call IP to tell him about it.
+ BindInfo.lip_context = Interface;
+ BindInfo.lip_transmit = ARPTransmit;
+ BindInfo.lip_transfer = ARPXferData;
+ BindInfo.lip_close = ARPClose;
+ BindInfo.lip_addaddr = ARPAddAddr;
+ BindInfo.lip_deladdr = ARPDeleteAddr;
+ BindInfo.lip_invalidate = ARPInvalidate;
+ BindInfo.lip_open = ARPOpen;
+ BindInfo.lip_qinfo = ARPQueryInfo;
+ BindInfo.lip_setinfo = ARPSetInfo;
+ BindInfo.lip_getelist = ARPGetEList;
+ BindInfo.lip_mss = Interface->ai_mtu;
+ BindInfo.lip_speed = Interface->ai_speed;
+ BindInfo.lip_flags = Flags;
+ BindInfo.lip_addrlen = Interface->ai_addrlen;
+ BindInfo.lip_addr = Interface->ai_addr;
+
+ Status = IPAddInterface((PNDIS_STRING)SS1, SS2, Interface, ARPDynRegister,
+ &BindInfo);
+
+ if (Status != IP_SUCCESS) {
+ // Need to close the binding. FreeARPInterface will do that, as well
+ // as freeing resources.
+
+ FreeARPInterface(Interface);
+ *RetStatus = NDIS_STATUS_FAILURE;
+ } else
+ *RetStatus = NDIS_STATUS_SUCCESS;
+
+}
+
+//* ARPUnbindAdapter - Unbind from an adapter.
+//
+// Called when we need to unbind from an adapter. We'll call up to IP to tell
+// him. When he's done, we'll free our memory and return.
+//
+// Input: RetStatus - Where to return status from call.
+// ProtBindContext - The context we gave NDIS earlier - really a
+// pointer to an ARPInterface structure.
+// UnbindContext - Context for completeing this request.
+//
+// Returns: Nothing.
+//
+void NDIS_API
+ARPUnbindAdapter(PNDIS_STATUS RetStatus, NDIS_HANDLE ProtBindContext,
+ NDIS_HANDLE UnbindContext)
+{
+ ARPInterface *Interface = (ARPInterface *)ProtBindContext;
+ NDIS_STATUS Status; // Status of close call.
+ CTELockHandle LockHandle;
+ NDIS_HANDLE Handle;
+
+ // Shut him up, so we don't get any more frames.
+ Interface->ai_pfilter = 0;
+ DoNDISRequest(Interface, NdisRequestSetInformation,
+ OID_GEN_CURRENT_PACKET_FILTER, &Interface->ai_pfilter, sizeof(uint),
+ NULL);
+
+ // Mark him as down.
+ Interface->ai_state = INTERFACE_DOWN;
+ Interface->ai_adminstate = IF_STATUS_DOWN;
+
+ // Now tell IP he's gone. We need to make sure that we don't tell him twice.
+ // To do this we set the context to NULL after we tell him the first time,
+ // and we check to make sure it's non-NULL before notifying him.
+
+ if (Interface->ai_context != NULL) {
+ IPDelInterface(Interface->ai_context);
+ Interface->ai_context = NULL;
+ }
+
+ // Finally, close him. We do this here so we can return a valid status.
+
+ CTEGetLock(&Interface->ai_lock, &LockHandle);
+
+ if (Interface->ai_handle != NULL) {
+ Handle = Interface->ai_handle;
+ Interface->ai_handle = NULL;
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+
+ CTEInitBlockStruc(&Interface->ai_block);
+ NdisCloseAdapter(&Status, Handle);
+
+ // Block for close to complete.
+ if (Status == NDIS_STATUS_PENDING)
+ Status = (NDIS_STATUS)CTEBlock(&Interface->ai_block);
+ } else {
+ CTEFreeLock(&Interface->ai_lock, LockHandle);
+ Status = NDIS_STATUS_SUCCESS;
+ }
+
+ *RetStatus = Status;
+
+ if (Status == NDIS_STATUS_SUCCESS) {
+
+ FreeARPInterface(Interface);
+ }
+}
+
+extern ulong VIPTerminate;
+
+//* ARPUnloadProtocol - Unload.
+//
+// Called when we need to unload. All we do is call up to IP, and return.
+//
+// Input: Nothing.
+//
+// Returns: Nothing.
+//
+void NDIS_API
+ARPUnloadProtocol(void)
+{
+ NDIS_STATUS Status;
+
+#ifdef CHICAGO
+
+ IPULUnloadNotify();
+
+ if (VIPTerminate) {
+ NdisDeregisterProtocol(&Status, ARPHandle);
+ CTEUnload(NULL);
+ }
+
+#endif
+
+}
+
+#endif
+
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 <packon.h>
+#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 <packoff.h>
+#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 <icmpif.h>
+
+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; i<NumAddrs; i++, RouterAddr++) {
+ if ((RouterAddr->irae_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 <tdiinfo.h>
+
+#ifdef NT
+#include <tdi.h>
+#include <tdikrnl.h>
+#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
+ &registrationData,
+ 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; i<MAX_TDI_ENTITIES; i++) {
+ IFBitMask[i/BITS_PER_WORD] = 0;
+ }
+ IFBitMask[0] = 1;
+#endif
+
+ IPSInfo.ipsi_forwarding = (ci->ici_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; i<ATC_SIZE; i++) {
+ if (ATCache[i].atc_flags & (ATCache[i].atc_addr == Address)) {
+ ATCache[i].atc_flags = 0;
+ }
+ }
+}
+
+//** GetAddrType - Return the type of a specified address.
+//
+// This function takes an input address, and determines what type it is. An
+// address can be local, bcast, remote, or remote bcast.
+//
+// Input: Address - Address to be check.
+//
+// Returns: Destination type.
+//
+uchar
+GetAddrType(IPAddr Address)
+{
+ NetTableEntry *NTE; // Pointer to current NTE.
+ IPMask Mask; // Mask for address.
+ uchar Result; // Result of broadcast check.
+ IPMask SNMask;
+ uint saveATCIndex;
+ uint i;
+
+ saveATCIndex = ATCIndex & ATC_MASK;
+ i = saveATCIndex;
+
+ do {
+ if (ATCache[i].atc_flags && (ATCache[i].atc_addr == Address)) {
+ Result = ATCache[i].atc_type;
+ if (ATCache[i].atc_flags && (ATCache[i].atc_addr == Address)) {
+ return(Result);
+ }
+ }
+ i = (--i) & ATC_MASK;
+ } while (i != saveATCIndex );
+
+
+
+ if (!CLASSE_ADDR(Address)) {
+ // See if it's one of our local addresses, or a broadcast
+ // on a local address.
+ NTE = NetTableList;
+ do {
+
+ if (IP_ADDR_EQUAL(NTE->nte_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 <oscfg.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <ip.h>
+#include "ipdef.h"
+#include "ipinit.h"
+#include <ntddip.h>
+#include <tdiinfo.h>
+#include <ipinfo.h>
+
+//
+// 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(&regKey, 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 <oscfg.h>
+#include <ndis.h>
+#include <cxport.h>
+#include <ip.h>
+#include "ipdef.h"
+#include "ipinit.h"
+#include "icmp.h"
+#include <ntddip.h>
+#include <llipif.h>
+#include <ipfilter.h>
+
+
+//
+// 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 <oscfg.h>
+#include <ndis.h>
+#include <cxport.h>
+
+
+#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