IP library. These files were moved from tcpip.sys and can be linked with
authorArt Yerkes <art.yerkes@gmail.com>
Wed, 29 Sep 2004 05:10:48 +0000 (05:10 +0000)
committerArt Yerkes <art.yerkes@gmail.com>
Wed, 29 Sep 2004 05:10:48 +0000 (05:10 +0000)
apps/tests/iptest using the undis library.  We need to make a change to
the way this is built so that we end up with ip.a (userland) and
ipkernel.a.  I'm still deciding how i want to do that.  For our purposes
now, it'll be fine to have it userland only as long as we still have the
option to build it for kernel use by hand to try out.

svn path=/trunk/; revision=11119

24 files changed:
reactos/drivers/lib/ip/include/precomp.h [new file with mode: 0644]
reactos/drivers/lib/ip/makefile [new file with mode: 0644]
reactos/drivers/lib/ip/network/address.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/arp.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/checksum.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/i386/checksum.S [new file with mode: 0644]
reactos/drivers/lib/ip/network/icmp.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/interface.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/ip.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/loopback.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/memtrack.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/neighbor.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/prefix.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/receive.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/route.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/router.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/routines.c [new file with mode: 0644]
reactos/drivers/lib/ip/network/transmit.c [new file with mode: 0644]
reactos/drivers/lib/ip/transport/datagram/datagram.c [new file with mode: 0644]
reactos/drivers/lib/ip/transport/rawip/rawip.c [new file with mode: 0644]
reactos/drivers/lib/ip/transport/tcp/event.c [new file with mode: 0644]
reactos/drivers/lib/ip/transport/tcp/if.c [new file with mode: 0644]
reactos/drivers/lib/ip/transport/tcp/tcp.c [new file with mode: 0644]
reactos/drivers/lib/ip/transport/udp/udp.c [new file with mode: 0644]

diff --git a/reactos/drivers/lib/ip/include/precomp.h b/reactos/drivers/lib/ip/include/precomp.h
new file mode 100644 (file)
index 0000000..e6b59ad
--- /dev/null
@@ -0,0 +1,34 @@
+#include <limits.h>
+#include <roscfg.h>
+#include <ddk/ntddk.h>
+#include <rosrtl/string.h>
+#include <rosrtl/recmutex.h>
+#include <tcpip.h>
+#include <loopback.h>
+#include <ip.h>
+#include <icmp.h>
+#include <udp.h>
+#include <tcp.h>
+#include <rawip.h>
+#include <address.h>
+#include <receive.h>
+#include <transmit.h>
+#include <routines.h>
+#include <neighbor.h>
+#include <checksum.h>
+#include <route.h>
+#include <router.h>
+#include <prefix.h>
+#include <pool.h>
+#include <arp.h>
+#include <lan.h>
+#include <irp.h>
+#include <tilists.h>
+#include <dispatch.h>
+#include <fileobjs.h>
+#include <datagram.h>
+#include <info.h>
+#include <ndishack.h>
+#include <memtrack.h>
+#include <interface.h>
+#include <oskittcp.h>
diff --git a/reactos/drivers/lib/ip/makefile b/reactos/drivers/lib/ip/makefile
new file mode 100644 (file)
index 0000000..ca918ca
--- /dev/null
@@ -0,0 +1,50 @@
+# $Id: makefile,v 1.1 2004/09/29 05:10:46 arty Exp $
+
+PATH_TO_TOP = ../../..
+
+TARGET_TYPE = library
+
+TARGET_NAME = ip$(KERNEL)
+
+TARGET_PCH = $(PATH_TO_TOP)/drivers/lib/ip/include/precomp.h
+
+# -DMEMTRACK
+TARGET_CFLAGS = \
+       -D__USE_W32API \
+       $(DRIVER) \
+       -Wall -Werror \
+       -Iinclude \
+       -I../../net/tcpip/include \
+       -I../undis/include \
+       -I../oskittcp/include \
+       -I$(PATH_TO_TOP)/include
+
+TARGET_CLEAN = network/*.o
+
+TARGET_OBJECTS = \
+       network/address.o \
+       network/arp.o \
+       network/checksum.o \
+       network/i386/checksum.o \
+       network/icmp.o \
+       network/interface.o \
+       network/ip.o \
+       network/loopback.o \
+       network/memtrack.o \
+       network/neighbor.o \
+       network/prefix.o \
+       network/receive.o \
+       network/route.o \
+       network/router.o \
+       network/routines.o \
+       network/transmit.o \
+       transport/datagram/datagram.o \
+       transport/rawip/rawip.o \
+       transport/tcp/event.o \
+       transport/tcp/if.o \
+       transport/tcp/tcp.o \
+       transport/udp/udp.o
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
diff --git a/reactos/drivers/lib/ip/network/address.c b/reactos/drivers/lib/ip/network/address.c
new file mode 100644 (file)
index 0000000..43a2edd
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        tcpip/address.c
+ * PURPOSE:     Routines for handling addresses
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+extern int sprintf( char *out, const char *fmt, ... );
+
+#ifdef DBG
+
+CHAR A2SStr[128];
+
+PCHAR A2S(
+    PIP_ADDRESS Address)
+/*
+ * FUNCTION: Convert an IP address to a string (for debugging)
+ * ARGUMENTS:
+ *     Address = Pointer to an IP address structure
+ * RETURNS:
+ *     Pointer to buffer with string representation of IP address
+ */
+{
+    ULONG ip;
+    PCHAR p;
+
+    p = A2SStr;
+
+    if (!Address) {
+        TI_DbgPrint(MIN_TRACE, ("NULL address given.\n"));
+        strcpy(p, "(NULL)");
+        return p;
+    }
+
+    switch (Address->Type) {
+    case IP_ADDRESS_V4:
+       ip = DN2H(Address->Address.IPv4Address);
+       sprintf(p, "%d.%d.%d.%d", 
+               (INT)((ip >> 24) & 0xFF), 
+               (INT)((ip >> 16) & 0xFF), 
+               (INT)((ip >> 8) & 0xFF), 
+               (INT)(ip & 0xFF));
+       break;
+       
+    case IP_ADDRESS_V6:
+       /* FIXME: IPv6 is not supported */
+       strcpy(p, "(IPv6 address not supported)");
+       break;
+    }
+    return p;
+}
+
+#endif /* DBG */
+
+ULONG IPv4NToHl( ULONG Address ) {
+    return 
+       ((Address & 0xff) << 24) |
+       ((Address & 0xff00) << 8) |
+       ((Address >> 8) & 0xff00) |
+       ((Address >> 24) & 0xff);
+}
+
+UINT AddrCountPrefixBits( PIP_ADDRESS Netmask ) {
+    UINT Prefix = 0;
+    if( Netmask->Type == IP_ADDRESS_V4 ) {
+       ULONG BitTest = 0x80000000;
+       
+       /* The mask has been read in network order.  Put it in host order
+        * in order to scan it. */
+
+       ULONG TestMask = IPv4NToHl(Netmask->Address.IPv4Address);
+       
+       while( (BitTest & TestMask) == BitTest ) {
+           Prefix++;
+           BitTest >>= 1;
+       }
+       return Prefix;
+    } else {
+       TI_DbgPrint(DEBUG_DATALINK, ("Don't know address type %d\n", 
+                                    Netmask->Type));
+       return 0;
+    }
+}
+
+VOID IPAddressFree(
+    PVOID Object)
+/*
+ * FUNCTION: Frees an IP_ADDRESS object
+ * ARGUMENTS:
+ *     Object = Pointer to an IP address structure
+ * RETURNS:
+ *     Nothing
+ */
+{
+    ExFreePool(Object);
+}
+
+
+BOOLEAN AddrIsUnspecified(
+    PIP_ADDRESS Address)
+/*
+ * FUNCTION: Return wether IP address is an unspecified address
+ * ARGUMENTS:
+ *     Address = Pointer to an IP address structure
+ * RETURNS:
+ *     TRUE if the IP address is an unspecified address, FALSE if not
+ */
+{
+    switch (Address->Type) {
+        case IP_ADDRESS_V4:
+            return (Address->Address.IPv4Address == 0);
+
+        case IP_ADDRESS_V6:
+        /* FIXME: IPv6 is not supported */
+        default:
+            return FALSE;
+    }
+}
+
+
+/*
+ * FUNCTION: Extract IP address from TDI address structure
+ * ARGUMENTS:
+ *     AddrList = Pointer to transport address list to extract from
+ *     Address  = Address of a pointer to where an IP address is stored
+ *     Port     = Pointer to where port number is stored
+ *     Cache    = Address of pointer to a cached address (updated on return)
+ * RETURNS:
+ *     Status of operation
+ */
+NTSTATUS AddrGetAddress(
+    PTRANSPORT_ADDRESS AddrList,
+    PIP_ADDRESS Address,
+    PUSHORT Port)
+{
+    PTA_ADDRESS CurAddr;
+    INT i;
+
+    /* We can only use IP addresses. Search the list until we find one */
+    CurAddr = AddrList->Address;
+
+    for (i = 0; i < AddrList->TAAddressCount; i++) {
+        switch (CurAddr->AddressType) {
+        case TDI_ADDRESS_TYPE_IP:
+            if (CurAddr->AddressLength >= TDI_ADDRESS_LENGTH_IP) {
+                /* This is an IPv4 address */
+                PTDI_ADDRESS_IP ValidAddr = (PTDI_ADDRESS_IP)CurAddr->Address;
+                *Port = ValidAddr->sin_port;
+               Address->Type = CurAddr->AddressType;
+               ValidAddr = (PTDI_ADDRESS_IP)CurAddr->Address;
+               AddrInitIPv4(Address, ValidAddr->in_addr);
+               return STATUS_SUCCESS;
+           }
+       }
+    }
+
+    return STATUS_INVALID_ADDRESS;
+}
+
+
+/*
+ * FUNCTION: Extract IP address from TDI address structure
+ * ARGUMENTS:
+ *     TdiAddress = Pointer to transport address list to extract from
+ *     Address    = Address of a pointer to where an IP address is stored
+ *     Port       = Pointer to where port number is stored
+ * RETURNS:
+ *     Status of operation
+ */
+NTSTATUS AddrBuildAddress(
+    PTA_ADDRESS TdiAddress,
+    PIP_ADDRESS *Address,
+    PUSHORT Port)
+{
+  PTDI_ADDRESS_IP ValidAddr;
+  PIP_ADDRESS IPAddress;
+
+  if (TdiAddress->AddressType != TDI_ADDRESS_TYPE_IP) {
+      TI_DbgPrint
+         (MID_TRACE,("AddressType %x, Not valid\n", 
+                     TdiAddress->AddressType));
+    return STATUS_INVALID_ADDRESS;
+  }
+  if (TdiAddress->AddressLength < TDI_ADDRESS_LENGTH_IP) {
+      TI_DbgPrint
+         (MID_TRACE,("AddressLength %x, Not valid (expected %x)\n", 
+                     TdiAddress->AddressLength, TDI_ADDRESS_LENGTH_IP));
+      return STATUS_INVALID_ADDRESS;
+  }
+
+
+  ValidAddr = (PTDI_ADDRESS_IP)TdiAddress->Address;
+
+  IPAddress = ExAllocatePool(NonPagedPool, sizeof(IP_ADDRESS));
+  if (!IPAddress)
+    return STATUS_INSUFFICIENT_RESOURCES;
+
+  AddrInitIPv4(IPAddress, ValidAddr->in_addr);
+  *Address = IPAddress;
+  *Port = ValidAddr->sin_port;
+
+  return STATUS_SUCCESS;
+}
+
+
+/*
+ * FUNCTION: Returns wether two addresses are equal
+ * ARGUMENTS:
+ *     Address1 = Pointer to first address
+ *     Address2 = Pointer to last address
+ * RETURNS:
+ *     TRUE if Address1 = Address2, FALSE if not
+ */
+BOOLEAN AddrIsEqual(
+    PIP_ADDRESS Address1,
+    PIP_ADDRESS Address2)
+{
+    if (Address1->Type != Address2->Type)
+        return FALSE;
+
+    switch (Address1->Type) {
+        case IP_ADDRESS_V4:
+            return (Address1->Address.IPv4Address == Address2->Address.IPv4Address);
+
+        case IP_ADDRESS_V6:
+            return (RtlCompareMemory(&Address1->Address, &Address2->Address,
+                sizeof(IPv6_RAW_ADDRESS)) == sizeof(IPv6_RAW_ADDRESS));
+            break;
+    }
+
+    return FALSE;
+}
+
+
+/*
+ * FUNCTION: Returns wether Address1 is less than Address2
+ * ARGUMENTS:
+ *     Address1 = Pointer to first address
+ *     Address2 = Pointer to last address
+ * RETURNS:
+ *     -1 if Address1 < Address2, 1 if Address1 > Address2,
+ *     or 0 if they are equal
+ */
+INT AddrCompare(
+    PIP_ADDRESS Address1,
+    PIP_ADDRESS Address2)
+{
+    switch (Address1->Type) {
+        case IP_ADDRESS_V4: {
+            ULONG Addr1, Addr2;
+            if (Address2->Type == IP_ADDRESS_V4) {
+                Addr1 = DN2H(Address1->Address.IPv4Address);
+                Addr2 = DN2H(Address2->Address.IPv4Address);
+                if (Addr1 < Addr2)
+                    return -1;
+                else
+                    if (Addr1 == Addr2)
+                        return 0;
+                    else
+                        return 1;
+            } else
+                /* FIXME: Support IPv6 */
+                return -1;
+
+        case IP_ADDRESS_V6:
+            /* FIXME: Support IPv6 */
+        break;
+        }
+    }
+
+    return FALSE;
+}
+
+
+/*
+ * FUNCTION: Returns wether two addresses are equal with IPv4 as input
+ * ARGUMENTS:
+ *     Address1 = Pointer to first address
+ *     Address2 = Pointer to last address
+ * RETURNS:
+ *     TRUE if Address1 = Address2, FALSE if not
+ */
+BOOLEAN AddrIsEqualIPv4(
+    PIP_ADDRESS Address1,
+    IPv4_RAW_ADDRESS Address2)
+{
+    if (Address1->Type == IP_ADDRESS_V4)
+        return (Address1->Address.IPv4Address == Address2);
+
+    return FALSE;
+}
+
+
+/*
+ * FUNCTION: Build an IPv4 style address
+ * ARGUMENTS:
+ *     Address = Raw IPv4 address (network byte order)
+ * RETURNS:
+ *     Pointer to IP address structure, NULL if there was not enough free
+ *     non-paged memory
+ */
+PIP_ADDRESS AddrBuildIPv4(
+    IPv4_RAW_ADDRESS Address)
+{
+    PIP_ADDRESS IPAddress;
+
+    IPAddress = ExAllocatePool(NonPagedPool, sizeof(IP_ADDRESS));
+    if (IPAddress != NULL) {
+        IPAddress->RefCount            = 1;
+        IPAddress->Type                = IP_ADDRESS_V4;
+        IPAddress->Address.IPv4Address = Address;
+        IPAddress->Free                = IPAddressFree;
+    }
+
+    return IPAddress;
+}
+
+
+/*
+ * FUNCTION: Clone an IP address
+ * ARGUMENTS:
+ *     IPAddress = Pointer to IP address
+ * RETURNS:
+ *     Pointer to new IP address structure, NULL if there was not enough free
+ *     non-paged memory
+ */
+PIP_ADDRESS AddrCloneAddress(
+  PIP_ADDRESS Address)
+{
+  if (Address->Type == IP_ADDRESS_V4)
+    {
+      return AddrBuildIPv4(Address->Address.IPv4Address);
+    }
+  else
+    {
+      TI_DbgPrint(MIN_TRACE, ("Cannot clone IPv6 address.\n"));
+      return NULL;
+    }
+}
+
+
+/*
+ * FUNCTION: Locates and returns an address entry using IPv4 adress as argument
+ * ARGUMENTS:
+ *     Address = Raw IPv4 address
+ * RETURNS:
+ *     Pointer to address entry if found, NULL if not found
+ * NOTES:
+ *     Only unicast addresses are considered.
+ *     If found, the address is referenced
+ */
+PADDRESS_ENTRY AddrLocateADEv4(
+    IPv4_RAW_ADDRESS Address)
+{
+    IP_ADDRESS Addr;
+
+    AddrInitIPv4(&Addr, Address);
+
+    return IPLocateADE(&Addr, ADE_UNICAST);
+}
+
+unsigned long PASCAL inet_addr(const char *AddrString)
+/*
+ * Convert an ansi string dotted-quad address to a ulong
+ * NOTES:
+ *     - this isn't quite like the real inet_addr() - * it doesn't 
+ *       handle "10.1" and similar - but it's good enough.
+ *     - Returns in *host* byte order, unlike real inet_addr()
+ */
+{
+       ULONG Octets[4] = {0,0,0,0};
+       ULONG i = 0;
+
+       if(!AddrString)
+               return -1;
+
+       while(*AddrString)
+               {
+                       CHAR c = *AddrString;
+                       AddrString++;
+
+                       if(c == '.')
+                               {
+                                       i++;
+                                       continue;
+                               }
+
+                       if(c < '0' || c > '9')
+                               return -1;
+
+                       Octets[i] *= 10;
+                       Octets[i] += (c - '0');
+
+                       if(Octets[i] > 255)
+                               return -1;
+               }
+
+       return (Octets[3] << 24) + (Octets[2] << 16) + (Octets[1] << 8) + Octets[0];
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/arp.c b/reactos/drivers/lib/ip/network/arp.c
new file mode 100644 (file)
index 0000000..fc293f3
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        datalink/arp.c
+ * PURPOSE:     Address Resolution Protocol routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+PNDIS_PACKET PrepareARPPacket(
+    USHORT HardwareType,
+    USHORT ProtocolType,
+    UCHAR LinkAddressLength,
+    UCHAR ProtoAddressLength,
+    PVOID SenderLinkAddress,
+    PVOID SenderProtoAddress,
+    PVOID TargetLinkAddress,
+    PVOID TargetProtoAddress,
+    USHORT Opcode)
+/*
+ * FUNCTION: Prepares an ARP packet
+ * ARGUMENTS:
+ *     HardwareType       = Hardware type (in network byte order)
+ *     ProtocolType       = Protocol type (in network byte order)
+ *     LinkAddressLength  = Length of link address fields
+ *     ProtoAddressLength = Length of protocol address fields
+ *     SenderLinkAddress  = Sender's link address
+ *     SenderProtoAddress = Sender's protocol address
+ *     TargetLinkAddress  = Target's link address (NULL if don't care)
+ *     TargetProtoAddress = Target's protocol address
+ *     Opcode             = ARP opcode (in network byte order)
+ * RETURNS:
+ *     Pointer to NDIS packet, NULL if there is not enough free resources
+ */
+{
+    PNDIS_PACKET NdisPacket;
+    NDIS_STATUS NdisStatus;
+    PARP_HEADER Header;
+    PVOID DataBuffer;
+    ULONG Size, Contig;
+
+    TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
+
+    /* Prepare ARP packet */
+    Size = MaxLLHeaderSize +
+       sizeof(ARP_HEADER) + 
+        2 * LinkAddressLength + /* Hardware address length */
+        2 * ProtoAddressLength; /* Protocol address length */
+    Size = MAX(Size, MinLLFrameSize);
+
+    NdisStatus = AllocatePacketWithBuffer( &NdisPacket, NULL, Size );
+    if( !NT_SUCCESS(NdisStatus) ) return NULL;
+
+    GetDataPtr( NdisPacket, 0, (PCHAR *)&DataBuffer, (PUINT)&Contig );
+
+    RtlZeroMemory(DataBuffer, Size);
+    Header = (PARP_HEADER)((ULONG_PTR)DataBuffer + MaxLLHeaderSize);
+    Header->HWType       = HardwareType;
+    Header->ProtoType    = ProtocolType;
+    Header->HWAddrLen    = LinkAddressLength;
+    Header->ProtoAddrLen = ProtoAddressLength;
+    Header->Opcode       = Opcode; /* Already swapped */
+    DataBuffer = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
+
+    /* Our hardware address */
+    RtlCopyMemory(DataBuffer, SenderLinkAddress, LinkAddressLength);
+    DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + LinkAddressLength);
+
+    /* Our protocol address */
+    RtlCopyMemory(DataBuffer, SenderProtoAddress, ProtoAddressLength);
+
+    if (TargetLinkAddress) {
+        DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + ProtoAddressLength);
+        /* Target hardware address */
+        RtlCopyMemory(DataBuffer, TargetLinkAddress, LinkAddressLength);
+        DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + LinkAddressLength);
+    } else
+        /* Don't care about target hardware address */
+        DataBuffer = (PVOID)((ULONG_PTR)DataBuffer + ProtoAddressLength + LinkAddressLength);
+
+    /* Target protocol address */
+    RtlCopyMemory(DataBuffer, TargetProtoAddress, ProtoAddressLength);
+
+    return NdisPacket;
+}
+
+
+VOID ARPTransmitComplete(
+    PVOID Context,
+    PNDIS_PACKET NdisPacket,
+    NDIS_STATUS NdisStatus)
+/*
+ * FUNCTION: ARP request transmit completion handler
+ * ARGUMENTS:
+ *     Context    = Pointer to context information (IP_INTERFACE)
+ *     Packet     = Pointer to NDIS packet that was sent
+ *     NdisStatus = NDIS status of operation
+ * NOTES:
+ *    This routine is called when an ARP request has been sent
+ */
+{
+    TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
+    FreeNdisPacket(NdisPacket);
+}
+
+
+BOOLEAN ARPTransmit(
+    PIP_ADDRESS Address,
+    PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Creates an ARP request and transmits it on a network
+ * ARGUMENTS:
+ *     Address = Pointer to IP address to resolve
+ *     NTE     = Pointer to net table entru to use for transmitting request
+ * RETURNS:
+ *     TRUE if the request was successfully sent, FALSE if not
+ */
+{
+    PIP_INTERFACE Interface;
+    PNDIS_PACKET NdisPacket;
+    UCHAR ProtoAddrLen;
+    USHORT ProtoType;
+
+    TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
+
+    Interface = NTE->Interface;
+
+    switch (Address->Type) {
+        case IP_ADDRESS_V4:
+            ProtoType    = (USHORT)ETYPE_IPv4; /* IPv4 */
+            ProtoAddrLen = 4;                  /* Length of IPv4 address */
+            break;
+        case IP_ADDRESS_V6:
+            ProtoType    = (USHORT)ETYPE_IPv6; /* IPv6 */
+            ProtoAddrLen = 16;                 /* Length of IPv6 address */
+            break;
+        default:
+           TI_DbgPrint(DEBUG_ARP,("Bad Address Type %x\n", Address->Type));
+           KeBugCheck(0);
+            /* Should not happen */
+            return FALSE;
+    }
+
+    NdisPacket = PrepareARPPacket(
+        WN2H(0x0001),                    /* FIXME: Ethernet only */
+        ProtoType,                       /* Protocol type */
+        (UCHAR)Interface->AddressLength, /* Hardware address length */
+        (UCHAR)ProtoAddrLen,             /* Protocol address length */
+        Interface->Address,              /* Sender's (local) hardware address */
+        &NTE->Address->Address,          /* Sender's (local) protocol address */
+        NULL,                            /* Don't care */
+        &Address->Address,               /* Target's (remote) protocol address */
+        ARP_OPCODE_REQUEST);             /* ARP request */
+
+    PC(NdisPacket)->DLComplete = ARPTransmitComplete;
+
+    TI_DbgPrint(DEBUG_ARP,("Sending ARP Packet\n"));
+    
+    (*Interface->Transmit)(Interface->Context, NdisPacket,
+        MaxLLHeaderSize, NULL, LAN_PROTO_ARP);
+
+    return TRUE;
+}
+
+
+VOID ARPReceive(
+    PVOID Context,
+    PIP_PACKET Packet)
+/*
+ * FUNCTION: Receives an ARP packet
+ * ARGUMENTS:
+ *     Context = Pointer to context information (IP_INTERFACE)
+ *     Packet  = Pointer to packet
+ */
+{
+    PARP_HEADER Header;
+    PIP_ADDRESS Address;
+    PVOID SenderHWAddress;
+    PVOID SenderProtoAddress;
+    PVOID TargetProtoAddress;
+    PADDRESS_ENTRY ADE;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    PNDIS_PACKET NdisPacket;
+    PIP_INTERFACE Interface = (PIP_INTERFACE)Context;
+
+    TI_DbgPrint(DEBUG_ARP, ("Called.\n"));
+
+    Header = (PARP_HEADER)Packet->Header;
+
+    /* FIXME: Ethernet only */
+    if (WN2H(Header->HWType) != 1) {
+        TI_DbgPrint(DEBUG_ARP, ("Unknown ARP hardware type (0x%X).\n", WN2H(Header->HWType)));
+        return;
+    }
+
+    /* Check protocol type */
+    if (Header->ProtoType != ETYPE_IPv4) {
+        TI_DbgPrint(DEBUG_ARP, ("Unknown ARP protocol type (0x%X).\n", WN2H(Header->ProtoType)));
+        return;
+    }
+
+    SenderHWAddress    = (PVOID)((ULONG_PTR)Header + sizeof(ARP_HEADER));
+    SenderProtoAddress = (PVOID)((ULONG_PTR)SenderHWAddress + Header->HWAddrLen);
+
+    /* Check if we have the target protocol address */
+
+    TargetProtoAddress = (PVOID)((ULONG_PTR)SenderProtoAddress +
+        Header->ProtoAddrLen + Header->HWAddrLen);
+
+    Address = AddrBuildIPv4(*((PULONG)TargetProtoAddress));
+    ADE = IPLocateADE(Address, ADE_UNICAST);
+    if (!ADE) {
+        TI_DbgPrint(DEBUG_ARP, ("Target address (0x%X) is not mine.\n", *((PULONG)TargetProtoAddress)));
+        return;
+    }
+
+    /* Check if we know the sender */
+
+    AddrInitIPv4(Address, *((PULONG)SenderProtoAddress));
+    NCE = NBLocateNeighbor(Address);
+    if (NCE) {
+        DereferenceObject(Address);
+        /* We know the sender. Update the hardware address 
+           and state in our neighbor address cache */
+        NBUpdateNeighbor(NCE, SenderHWAddress, NUD_REACHABLE);
+    } else {
+        /* The packet had our protocol address as target. The sender
+           may want to communicate with us soon, so add his address
+           to our address cache */
+        NCE = NBAddNeighbor(Interface, Address, SenderHWAddress,
+            Header->HWAddrLen, NUD_REACHABLE);
+    }
+    if (NCE)
+        DereferenceObject(NCE)
+
+    if (Header->Opcode != ARP_OPCODE_REQUEST)
+        return;
+    
+    /* This is a request for our address. Swap the addresses and
+       send an ARP reply back to the sender */
+    NdisPacket = PrepareARPPacket(
+        Header->HWType,                  /* Hardware type */
+        Header->ProtoType,               /* Protocol type */
+        (UCHAR)Interface->AddressLength, /* Hardware address length */
+        (UCHAR)Header->ProtoAddrLen,     /* Protocol address length */
+        Interface->Address,              /* Sender's (local) hardware address */
+        &ADE->Address->Address,          /* Sender's (local) protocol address */
+        SenderHWAddress,                 /* Target's (remote) hardware address */
+        SenderProtoAddress,              /* Target's (remote) protocol address */
+        ARP_OPCODE_REPLY);               /* ARP reply */
+    if (NdisPacket) {
+        PC(NdisPacket)->DLComplete = ARPTransmitComplete;
+        (*Interface->Transmit)(Interface->Context,
+                               NdisPacket,
+                               MaxLLHeaderSize,
+                               SenderHWAddress,
+                               LAN_PROTO_ARP);
+    }
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/checksum.c b/reactos/drivers/lib/ip/network/checksum.c
new file mode 100644 (file)
index 0000000..7f11c1a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        tcpip/checksum.c
+ * PURPOSE:     Checksum routines
+ * NOTES:       The checksum routine is from RFC 1071
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+ULONG ChecksumFold(
+  ULONG Sum)
+{
+  /* Fold 32-bit sum to 16 bits */
+  while (Sum >> 16)
+    {
+      Sum = (Sum & 0xFFFF) + (Sum >> 16);
+    }
+
+  return Sum;
+}
+
+ULONG ChecksumCompute(
+  PVOID Data,
+  UINT Count,
+  ULONG Seed)
+/*
+ * FUNCTION: Calculate checksum of a buffer
+ * ARGUMENTS:
+ *     Data  = Pointer to buffer with data
+ *     Count = Number of bytes in buffer
+ *     Seed  = Previously calculated checksum (if any)
+ * RETURNS:
+ *     Checksum of buffer
+ */
+{
+  register ULONG Sum = Seed;
+
+  while (Count > 1)
+    {
+      Sum += *(PUSHORT)Data;
+      Count -= 2;
+      Data = (PVOID)((ULONG_PTR) Data + 2);
+    }
+
+  /* Add left-over byte, if any */
+  if (Count > 0)
+    {
+      Sum += *(PUCHAR)Data;
+    }
+
+  return Sum;
+}
diff --git a/reactos/drivers/lib/ip/network/i386/checksum.S b/reactos/drivers/lib/ip/network/i386/checksum.S
new file mode 100644 (file)
index 0000000..468b85e
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * INET                An implementation of the TCP/IP protocol suite for the LINUX
+ *             operating system.  INET is implemented using the  BSD Socket
+ *             interface as the means of communication with the user level.
+ *
+ *             IP/TCP/UDP checksumming routines
+ *
+ * Authors:    Jorge Cwik, <jorge@laser.satlink.net>
+ *             Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *             Tom May, <ftom@netcom.com>
+ *              Pentium Pro/II routines:
+ *              Alexander Kjeldaas <astor@guardian.no>
+ *              Finn Arne Gangstad <finnag@guardian.no>
+ *             Lots of code moved from tcp.c and ip.c; see those files
+ *             for more names.
+ *
+ * Changes:     Ingo Molnar, converted csum_partial_copy() to 2.1 exception
+ *                          handling.
+ *             Andi Kleen,  add zeroing on error
+ *                   converted to pure assembler
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ */
+                               
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+/*     
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+ */
+               
+.text
+.align 4
+.globl _csum_partial                                                           
+               
+#ifndef CONFIG_X86_USE_PPRO_CHECKSUM
+
+         /*            
+          * Experiments with Ethernet and SLIP connections show that buff
+          * is aligned on either a 2-byte or 4-byte boundary.  We get at
+          * least a twofold speedup on 486 and Pentium if it is 4-byte aligned.
+          * Fortunately, it is easy to convert 2-byte alignment to 4-byte
+          * alignment for the unrolled loop.
+          */           
+_csum_partial: 
+       pushl %esi
+       pushl %ebx
+       movl 20(%esp),%eax      # Function arg: unsigned int sum
+       movl 16(%esp),%ecx      # Function arg: int len
+       movl 12(%esp),%esi      # Function arg: unsigned char *buff
+       testl $3, %esi          # Check alignment.
+       jz 2f                   # Jump if alignment is ok.
+       testl $1, %esi          # Check alignment.
+       jz 10f                  # Jump if alignment is boundary of 2bytes.
+
+       # buf is odd
+       dec %ecx
+       jl 8f
+       movzbl (%esi), %ebx
+       adcl %ebx, %eax
+       roll $8, %eax
+       inc %esi
+       testl $2, %esi
+       jz 2f
+10:
+       subl $2, %ecx           # Alignment uses up two bytes.
+       jae 1f                  # Jump if we had at least two bytes.
+       addl $2, %ecx           # ecx was < 2.  Deal with it.
+       jmp 4f
+1:     movw (%esi), %bx
+       addl $2, %esi
+       addw %bx, %ax
+       adcl $0, %eax
+2:
+       movl %ecx, %edx
+       shrl $5, %ecx
+       jz 2f
+       testl %esi, %esi
+1:     movl (%esi), %ebx
+       adcl %ebx, %eax
+       movl 4(%esi), %ebx
+       adcl %ebx, %eax
+       movl 8(%esi), %ebx
+       adcl %ebx, %eax
+       movl 12(%esi), %ebx
+       adcl %ebx, %eax
+       movl 16(%esi), %ebx
+       adcl %ebx, %eax
+       movl 20(%esi), %ebx
+       adcl %ebx, %eax
+       movl 24(%esi), %ebx
+       adcl %ebx, %eax
+       movl 28(%esi), %ebx
+       adcl %ebx, %eax
+       lea 32(%esi), %esi
+       dec %ecx
+       jne 1b
+       adcl $0, %eax
+2:     movl %edx, %ecx
+       andl $0x1c, %edx
+       je 4f
+       shrl $2, %edx           # This clears CF
+3:     adcl (%esi), %eax
+       lea 4(%esi), %esi
+       dec %edx
+       jne 3b
+       adcl $0, %eax
+4:     andl $3, %ecx
+       jz 7f
+       cmpl $2, %ecx
+       jb 5f
+       movw (%esi),%cx
+       leal 2(%esi),%esi
+       je 6f
+       shll $16,%ecx
+5:     movb (%esi),%cl
+6:     addl %ecx,%eax
+       adcl $0, %eax 
+7:     
+       testl $1, 12(%esp)
+       jz 8f
+       roll $8, %eax
+8:
+       popl %ebx
+       popl %esi
+       ret
+
+#else
+
+/* Version for PentiumII/PPro */
+
+csum_partial:
+       pushl %esi
+       pushl %ebx
+       movl 20(%esp),%eax      # Function arg: unsigned int sum
+       movl 16(%esp),%ecx      # Function arg: int len
+       movl 12(%esp),%esi      # Function arg: const unsigned char *buf
+
+       testl $3, %esi         
+       jnz 25f                 
+10:
+       movl %ecx, %edx
+       movl %ecx, %ebx
+       andl $0x7c, %ebx
+       shrl $7, %ecx
+       addl %ebx,%esi
+       shrl $2, %ebx  
+       negl %ebx
+       lea 45f(%ebx,%ebx,2), %ebx
+       testl %esi, %esi
+       jmp *%ebx
+
+       # Handle 2-byte-aligned regions
+20:    addw (%esi), %ax
+       lea 2(%esi), %esi
+       adcl $0, %eax
+       jmp 10b
+25:
+       testl $1, %esi         
+       jz 30f                 
+       # buf is odd
+       dec %ecx
+       jl 90f
+       movzbl (%esi), %ebx
+       addl %ebx, %eax
+       adcl $0, %eax
+       roll $8, %eax
+       inc %esi
+       testl $2, %esi
+       jz 10b
+
+30:    subl $2, %ecx          
+       ja 20b                 
+       je 32f
+       addl $2, %ecx
+       jz 80f
+       movzbl (%esi),%ebx      # csumming 1 byte, 2-aligned
+       addl %ebx, %eax
+       adcl $0, %eax
+       jmp 80f
+32:
+       addw (%esi), %ax        # csumming 2 bytes, 2-aligned
+       adcl $0, %eax
+       jmp 80f
+
+40: 
+       addl -128(%esi), %eax
+       adcl -124(%esi), %eax
+       adcl -120(%esi), %eax
+       adcl -116(%esi), %eax   
+       adcl -112(%esi), %eax   
+       adcl -108(%esi), %eax
+       adcl -104(%esi), %eax
+       adcl -100(%esi), %eax
+       adcl -96(%esi), %eax
+       adcl -92(%esi), %eax
+       adcl -88(%esi), %eax
+       adcl -84(%esi), %eax
+       adcl -80(%esi), %eax
+       adcl -76(%esi), %eax
+       adcl -72(%esi), %eax
+       adcl -68(%esi), %eax
+       adcl -64(%esi), %eax     
+       adcl -60(%esi), %eax     
+       adcl -56(%esi), %eax     
+       adcl -52(%esi), %eax   
+       adcl -48(%esi), %eax   
+       adcl -44(%esi), %eax
+       adcl -40(%esi), %eax
+       adcl -36(%esi), %eax
+       adcl -32(%esi), %eax
+       adcl -28(%esi), %eax
+       adcl -24(%esi), %eax
+       adcl -20(%esi), %eax
+       adcl -16(%esi), %eax
+       adcl -12(%esi), %eax
+       adcl -8(%esi), %eax
+       adcl -4(%esi), %eax
+45:
+       lea 128(%esi), %esi
+       adcl $0, %eax
+       dec %ecx
+       jge 40b
+       movl %edx, %ecx
+50:    andl $3, %ecx
+       jz 80f
+
+       # Handle the last 1-3 bytes without jumping
+       notl %ecx               # 1->2, 2->1, 3->0, higher bits are masked
+       movl $0xffffff,%ebx     # by the shll and shrl instructions
+       shll $3,%ecx
+       shrl %cl,%ebx
+       andl -128(%esi),%ebx    # esi is 4-aligned so should be ok
+       addl %ebx,%eax
+       adcl $0,%eax
+80: 
+       testl $1, 12(%esp)
+       jz 90f
+       roll $8, %eax
+90: 
+       popl %ebx
+       popl %esi
+       ret
+                               
+#endif
diff --git a/reactos/drivers/lib/ip/network/icmp.c b/reactos/drivers/lib/ip/network/icmp.c
new file mode 100644 (file)
index 0000000..e6ac346
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/icmp.c
+ * PURPOSE:     Internet Control Message Protocol routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+VOID SendICMPComplete(
+    PVOID Context,
+    PNDIS_PACKET Packet,
+    NDIS_STATUS NdisStatus)
+/*
+ * FUNCTION: ICMP datagram transmit completion handler
+ * ARGUMENTS:
+ *     Context    = Pointer to context infomation (IP_PACKET)
+ *     Packet     = Pointer to NDIS packet
+ *     NdisStatus = Status of transmit operation
+ * NOTES:
+ *     This routine is called by IP when a ICMP send completes
+ */
+{
+    PIP_PACKET IPPacket = NULL;
+    IPPacket =(PIP_PACKET)Context;
+
+    TI_DbgPrint(DEBUG_ICMP, ("Freeing NDIS packet (%X).\n", Packet));
+
+    /* Free packet */
+    FreeNdisPacket(Packet);
+
+    TI_DbgPrint(DEBUG_ICMP, ("Freeing IP packet at %X.\n", IPPacket));
+}
+
+
+PIP_PACKET PrepareICMPPacket(
+    PNET_TABLE_ENTRY NTE,
+    PIP_ADDRESS Destination,
+    UINT DataSize)
+/*
+ * FUNCTION: Prepares an ICMP packet
+ * ARGUMENTS:
+ *     NTE         = Pointer to net table entry to use
+ *     Destination = Pointer to destination address
+ *     DataSize    = Size of dataarea
+ * RETURNS:
+ *     Pointer to IP packet, NULL if there is not enough free resources
+ */
+{
+    PIP_PACKET IPPacket;
+    PNDIS_PACKET NdisPacket;
+    PNDIS_BUFFER NdisBuffer;
+    NDIS_STATUS NdisStatus;
+    PIPv4_HEADER IPHeader;
+    PVOID DataBuffer;
+    ULONG Size;
+
+    TI_DbgPrint(DEBUG_ICMP, ("Called. DataSize (%d).\n", DataSize));
+
+    /* Prepare ICMP packet */
+
+    /* FIXME: Assumes IPv4*/
+    IPPacket = IPCreatePacket(IP_ADDRESS_V4);
+    if (!IPPacket)
+        return NULL;
+
+    /* No special flags */
+    IPPacket->Flags = 0;
+
+    Size = MaxLLHeaderSize + sizeof(IPv4_HEADER) +
+        sizeof(ICMP_HEADER) + DataSize;
+    DataBuffer = exAllocatePool(NonPagedPool, Size);
+    if (!DataBuffer) {
+        return NULL;
+    }
+
+    TI_DbgPrint(DEBUG_ICMP, ("Size (%d). Data at (0x%X).\n", Size, DataBuffer));
+
+    /* Allocate NDIS packet */
+    NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool);
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        exFreePool(DataBuffer);
+        return NULL;
+    }
+
+    TI_DbgPrint(MAX_TRACE, ("NdisPacket at (0x%X).\n", NdisPacket));
+
+    /* Allocate NDIS buffer for maximum link level header and ICMP packet */
+    NdisAllocateBuffer(&NdisStatus, &NdisBuffer, GlobalBufferPool,
+        DataBuffer, Size);
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        FreeNdisPacket(NdisPacket);
+        exFreePool(DataBuffer);
+        return NULL;
+    }
+    
+    TI_DbgPrint(MAX_TRACE, ("NdisBuffer at (0x%X).\n", NdisBuffer));
+
+    /* Link NDIS buffer into packet */
+    NdisChainBufferAtFront(NdisPacket, NdisBuffer);
+    IPPacket->NdisPacket = NdisPacket;
+    IPPacket->Header     = (PVOID)((ULONG_PTR)DataBuffer + MaxLLHeaderSize);
+    IPPacket->Data       = (PVOID)((ULONG_PTR)DataBuffer + MaxLLHeaderSize + sizeof(IPv4_HEADER));
+
+    IPPacket->HeaderSize = sizeof(IPv4_HEADER);
+    IPPacket->TotalSize  = Size - MaxLLHeaderSize;
+    RtlCopyMemory(&IPPacket->DstAddr, Destination, sizeof(IP_ADDRESS));
+
+    /* Build IPv4 header. FIXME: IPv4 only */
+
+    IPHeader = (PIPv4_HEADER)IPPacket->Header;
+
+    /* Version = 4, Length = 5 DWORDs */
+    IPHeader->VerIHL = 0x45;
+    /* Normal Type-of-Service */
+    IPHeader->Tos = 0;
+    /* Length of data and header */
+    IPHeader->TotalLength = WH2N((USHORT)DataSize +
+        sizeof(IPv4_HEADER) + sizeof(ICMP_HEADER));
+    /* Identification */
+    IPHeader->Id = (USHORT)Random();
+    /* One fragment at offset 0 */
+    IPHeader->FlagsFragOfs = 0;
+    /* Time-to-Live is 128 */
+    IPHeader->Ttl = 128;
+    /* Internet Control Message Protocol */
+    IPHeader->Protocol = IPPROTO_ICMP;
+    /* Checksum is 0 (for later calculation of this) */
+    IPHeader->Checksum = 0;
+    /* Source address */
+    IPHeader->SrcAddr = NTE->Address->Address.IPv4Address;
+    /* Destination address */
+    IPHeader->DstAddr = Destination->Address.IPv4Address;
+
+    /* Completion handler */
+    PC(NdisPacket)->Complete = SendICMPComplete;
+    PC(NdisPacket)->Context  = IPPacket;
+
+    return IPPacket;
+}
+
+
+VOID ICMPReceive(
+    PNET_TABLE_ENTRY NTE,
+    PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Receives an ICMP packet
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry which the packet was received on
+ *     IPPacket = Pointer to an IP packet that was received
+ */
+{
+    PICMP_HEADER ICMPHeader;
+    PIP_PACKET NewPacket;
+    UINT DataSize;
+
+    TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
+
+    ICMPHeader = (PICMP_HEADER)IPPacket->Data;
+
+    TI_DbgPrint(DEBUG_ICMP, ("Size (%d).\n", IPPacket->TotalSize));
+
+    TI_DbgPrint(DEBUG_ICMP, ("HeaderSize (%d).\n", IPPacket->HeaderSize));
+
+    TI_DbgPrint(DEBUG_ICMP, ("Type (%d).\n", ICMPHeader->Type));
+
+    TI_DbgPrint(DEBUG_ICMP, ("Code (%d).\n", ICMPHeader->Code));
+
+    TI_DbgPrint(DEBUG_ICMP, ("Checksum (0x%X).\n", ICMPHeader->Checksum));
+
+    /* Checksum ICMP header and data */
+    if (!IPv4CorrectChecksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize)) {
+        TI_DbgPrint(DEBUG_ICMP, ("Bad ICMP checksum.\n"));
+        /* Discard packet */
+        return;
+    }
+
+    switch (ICMPHeader->Type) {
+    case ICMP_TYPE_ECHO_REQUEST:
+        /* Reply with an ICMP echo reply message */
+        DataSize  = IPPacket->TotalSize - IPPacket->HeaderSize - sizeof(ICMP_HEADER);
+        NewPacket = PrepareICMPPacket(NTE, &IPPacket->SrcAddr, DataSize);
+        if (!NewPacket)
+            return;
+
+        /* Copy ICMP header and data into new packet */
+        RtlCopyMemory(NewPacket->Data, IPPacket->Data, DataSize + sizeof(ICMP_HEADER));
+        ((PICMP_HEADER)NewPacket->Data)->Type     = ICMP_TYPE_ECHO_REPLY;
+        ((PICMP_HEADER)NewPacket->Data)->Code     = 0;
+        ((PICMP_HEADER)NewPacket->Data)->Checksum = 0;
+
+#ifdef DBG
+        DisplayIPPacket(IPPacket);
+        DisplayIPPacket(NewPacket);
+#endif
+
+        ICMPTransmit(NTE, NewPacket);
+
+        TI_DbgPrint(DEBUG_ICMP, ("Echo reply sent.\n"));
+        return;
+
+    case ICMP_TYPE_ECHO_REPLY:
+        break;
+
+    default:
+        TI_DbgPrint(DEBUG_ICMP, ("Discarded ICMP datagram of unknown type %d.\n",
+            ICMPHeader->Type));
+        /* Discard packet */
+        break;
+    }
+
+    /* Send datagram up the protocol stack */
+    RawIPReceive(NTE, IPPacket);
+}
+
+
+VOID ICMPTransmit(
+    PNET_TABLE_ENTRY NTE,
+    PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Transmits an ICMP packet
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry to use (NULL if don't care)
+ *     IPPacket = Pointer to IP packet to transmit
+ */
+{
+    PROUTE_CACHE_NODE RCN;
+
+    TI_DbgPrint(DEBUG_ICMP, ("Called.\n"));
+
+    /* Calculate checksum of ICMP header and data */
+    ((PICMP_HEADER)IPPacket->Data)->Checksum = (USHORT)
+        IPv4Checksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize, 0);
+
+    /* Get a route to the destination address */
+    if (RouteGetRouteToDestination(&IPPacket->DstAddr, NULL, &RCN) == IP_SUCCESS) {
+        /* Send the packet */
+        if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS) {
+            FreeNdisPacket(IPPacket->NdisPacket);
+        }
+        /* We're done with the RCN */
+        DereferenceObject(RCN);
+    } else {
+        TI_DbgPrint(MIN_TRACE, ("RCN at (0x%X).\n", RCN));
+
+        /* No route to destination (or no free resources) */
+        TI_DbgPrint(DEBUG_ICMP, ("No route to destination address 0x%X.\n",
+            IPPacket->DstAddr.Address.IPv4Address));
+        /* Discard packet */
+        FreeNdisPacket(IPPacket->NdisPacket);
+    }
+}
+
+
+VOID ICMPReply(
+    PNET_TABLE_ENTRY NTE,
+    PIP_PACKET IPPacket,
+         UCHAR Type,
+         UCHAR Code)
+/*
+ * FUNCTION: Transmits an ICMP packet in response to an incoming packet
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry to use
+ *     IPPacket = Pointer to IP packet that was received
+ *     Type     = ICMP message type
+ *     Code     = ICMP message code
+ * NOTES:
+ *     We have received a packet from someone and is unable to
+ *     process it due to error(s) in the packet or we have run out
+ *     of resources. We transmit an ICMP message to the host to
+ *     notify him of the problem
+ */
+{
+    UINT DataSize;
+    PIP_PACKET NewPacket;
+
+    TI_DbgPrint(DEBUG_ICMP, ("Called. Type (%d)  Code (%d).\n", Type, Code));
+
+    DataSize = IPPacket->TotalSize;
+    if ((DataSize) > (576 - sizeof(IPv4_HEADER) - sizeof(ICMP_HEADER)))
+        DataSize = 576;
+
+    NewPacket = PrepareICMPPacket(NTE, &IPPacket->SrcAddr, DataSize);
+    if (!NewPacket)
+        return;
+
+    RtlCopyMemory((PVOID)((ULONG_PTR)NewPacket->Data + sizeof(ICMP_HEADER)),
+        IPPacket->Header, DataSize);
+    ((PICMP_HEADER)NewPacket->Data)->Type     = Type;
+    ((PICMP_HEADER)NewPacket->Data)->Code     = Code;
+    ((PICMP_HEADER)NewPacket->Data)->Checksum = 0;
+
+    ICMPTransmit(NTE, NewPacket);
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/interface.c b/reactos/drivers/lib/ip/network/interface.c
new file mode 100644 (file)
index 0000000..950cc4a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        tcpip/interface.c
+ * PURPOSE:     Convenient abstraction for getting and setting information
+ *              in IP_INTERFACE.
+ * PROGRAMMERS: Art Yerkes
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+NTSTATUS GetInterfaceIPv4Address( PIP_INTERFACE Interface, 
+                                 ULONG TargetType,
+                                 PULONG Address ) {
+    ADE_LIST_ITER(CurrentADE);
+
+    ForEachADE(Interface->ADEListHead,CurrentADE) {
+       if (CurrentADE->Type == TargetType) {
+           *Address = CurrentADE->Address->Address.IPv4Address;
+           return STATUS_SUCCESS;
+       }
+    } EndFor(CurrentADE);
+    
+    return STATUS_UNSUCCESSFUL;
+}
+
+UINT CountInterfaces() {
+    DWORD Count = 0;
+    KIRQL OldIrql;
+    IF_LIST_ITER(CurrentIF);
+
+    KeAcquireSpinLock(&InterfaceListLock, &OldIrql);
+    
+    ForEachInterface(CurrentIF) {
+       Count++;
+    } EndFor(CurrentIF);
+
+    KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+
+    return Count;
+}
+
+UINT CountInterfaceAddresses( PIP_INTERFACE Interface ) {
+    UINT AddrCount = 0;
+    ADE_LIST_ITER(CurrentADE);
+
+    ForEachADE(Interface->ADEListHead,CurrentADE) {
+       if( CurrentADE->Type == ADE_UNICAST )
+           AddrCount++;
+    } EndFor(CurrentADE);
+
+    return AddrCount;
+}
+
+NTSTATUS GetInterfaceSpeed( PIP_INTERFACE Interface, PUINT Speed ) {
+    NDIS_STATUS NdisStatus;
+    PLAN_ADAPTER IF = (PLAN_ADAPTER)Interface->Context;
+    
+#ifdef __NTDRIVER__
+    /* Get maximum link speed */
+    NdisStatus = NDISCall(IF,
+                          NdisRequestQueryInformation,
+                          OID_GEN_LINK_SPEED,
+                          Speed,
+                          sizeof(UINT));
+#else
+    (void)IF;
+    NdisStatus = NDIS_STATUS_SUCCESS;
+    *Speed = 10000;
+#endif
+    
+    return 
+       NdisStatus != NDIS_STATUS_SUCCESS ? 
+       STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
+}
+
+NTSTATUS GetInterfaceName( PIP_INTERFACE Interface, 
+                          PCHAR NameBuffer,
+                          UINT Len ) {
+    NDIS_STATUS NdisStatus;
+    PLAN_ADAPTER IF = (PLAN_ADAPTER)Interface->Context;
+    
+#ifdef __NTDRIVER__
+    /* Get maximum link speed */
+    NdisStatus = NDISCall(IF,
+                          NdisRequestQueryInformation,
+                          OID_GEN_FRIENDLY_NAME,
+                          NameBuffer,
+                         Len);
+#else
+    (void)IF;
+    NdisStatus = NDIS_STATUS_SUCCESS;
+    strncpy( NameBuffer, "eth", Len );
+#endif
+    
+    return 
+       NdisStatus != NDIS_STATUS_SUCCESS ? 
+       STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
+}
diff --git a/reactos/drivers/lib/ip/network/ip.c b/reactos/drivers/lib/ip/network/ip.c
new file mode 100644 (file)
index 0000000..4926714
--- /dev/null
@@ -0,0 +1,1070 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/ip.c
+ * PURPOSE:     Internet Protocol module
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+KTIMER IPTimer;
+KDPC IPTimeoutDpc;
+LIST_ENTRY InterfaceListHead;
+KSPIN_LOCK InterfaceListLock;
+LIST_ENTRY NetTableListHead;
+KSPIN_LOCK NetTableListLock;
+UINT MaxLLHeaderSize; /* Largest maximum header size */
+UINT MinLLFrameSize;  /* Largest minimum frame size */
+BOOLEAN IPInitialized = FALSE;
+NPAGED_LOOKASIDE_LIST IPPacketList;
+
+IP_PROTOCOL_HANDLER ProtocolTable[IP_PROTOCOL_TABLE_SIZE];
+
+
+VOID FreePacket(
+    PVOID Object)
+/*
+ * FUNCTION: Frees an IP packet object
+ * ARGUMENTS:
+ *     Object = Pointer to an IP packet structure
+ */
+{
+    ExFreeToNPagedLookasideList(&IPPacketList, Object);
+}
+
+
+VOID DontFreePacket(
+    PVOID Object)
+/*
+ * FUNCTION: Do nothing for when the IPPacket struct is part of another
+ * ARGUMENTS:
+ *     Object = Pointer to an IP packet structure
+ */
+{
+}
+
+
+VOID FreeADE(
+    PVOID Object)
+/*
+ * FUNCTION: Frees an address entry object
+ * ARGUMENTS:
+ *     Object = Pointer to an address entry structure
+ */
+{
+    exFreePool(Object);
+}
+
+
+VOID FreeNTE(
+    PVOID Object)
+/*
+ * FUNCTION: Frees a net table entry object
+ * ARGUMENTS:
+ *     Object = Pointer to an net table entry structure
+ */
+{
+    exFreePool(Object);
+}
+
+
+VOID FreeIF(
+    PVOID Object)
+/*
+ * FUNCTION: Frees an interface object
+ * ARGUMENTS:
+ *     Object = Pointer to an interface structure
+ */
+{
+    exFreePool(Object);
+}
+
+
+PADDRESS_ENTRY CreateADE(
+    PIP_INTERFACE IF,    
+    PIP_ADDRESS Address,
+    UCHAR Type,
+    PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Creates an address entry and binds it to an interface
+ * ARGUMENTS:
+ *     IF      = Pointer to interface
+ *     Address = Pointer to referenced interface address
+ *     Type    = Type of address (ADE_*)
+ *     NTE     = Pointer to net table entry
+ * RETURNS:
+ *     Pointer to ADE, NULL if there was not enough free resources
+ * NOTES:
+ *     The interface lock must be held when called. The address entry
+ *     retains a reference to the provided address and NTE. The caller
+ *     is responsible for referencing the these before calling.
+ *     As long as you have referenced an ADE you can safely use the
+ *     address and NTE as the ADE references both
+ */
+{
+    PADDRESS_ENTRY ADE;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X)  Address (0x%X)  Type (0x%X)  NTE (0x%X).\n",
+        IF, Address, Type, NTE));
+
+    TI_DbgPrint(DEBUG_IP, ("Address (%s)  NTE (%s).\n",
+        A2S(Address), A2S(NTE->Address)));
+
+    /* Allocate space for an ADE and set it up */
+    ADE = exAllocatePool(NonPagedPool, sizeof(ADDRESS_ENTRY));
+    if (!ADE) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+    INIT_TAG(ADE, TAG('A','D','E',' '));
+    ADE->Free     = FreeADE;
+    ADE->RefCount = 1;
+    ADE->NTE      = NTE;
+    ADE->Type     = Type;
+    ADE->Address  = Address;
+
+    /* Add ADE to the list on the interface */
+    InsertTailList(&IF->ADEListHead, &ADE->ListEntry);
+
+    return ADE;
+}
+
+
+VOID DestroyADE(
+    PIP_INTERFACE IF,
+    PADDRESS_ENTRY ADE)
+/*
+ * FUNCTION: Destroys an address entry
+ * ARGUMENTS:
+ *     IF  = Pointer to interface
+ *     ADE = Pointer to address entry
+ * NOTES:
+ *     The interface lock must be held when called
+ */
+{
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X)  ADE (0x%X).\n", IF, ADE));
+
+    TI_DbgPrint(DEBUG_IP, ("ADE (%s).\n", ADE->Address));
+
+    /* Unlink the address entry from the list */
+    RemoveEntryList(&ADE->ListEntry);
+
+    /* Dereference the address */
+    DereferenceObject(ADE->Address);
+
+    /* Dereference the NTE */
+    DereferenceObject(ADE->NTE);
+
+#ifdef DBG
+    ADE->RefCount--;
+
+    if (ADE->RefCount != 0) {
+        TI_DbgPrint(MIN_TRACE, ("Address entry at (0x%X) has (%d) references (should be 0).\n", ADE, ADE->RefCount));
+    }
+#endif
+
+    /* And free the ADE */
+    FreeADE(ADE);
+}
+
+
+VOID DestroyADEs(
+    PIP_INTERFACE IF)
+/*
+ * FUNCTION: Destroys all address entries on an interface
+ * ARGUMENTS:
+ *     IF  = Pointer to interface
+ * NOTES:
+ *     The interface lock must be held when called
+ */
+{
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+    PADDRESS_ENTRY Current;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
+
+    /* Search the list and remove every ADE we find */
+    CurrentEntry = IF->ADEListHead.Flink;
+    while (CurrentEntry != &IF->ADEListHead) {
+        NextEntry = CurrentEntry->Flink;
+           Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_ENTRY, ListEntry);
+        /* Destroy the ADE */
+        DestroyADE(IF, Current);
+        CurrentEntry = NextEntry;
+    }
+}
+
+
+PIP_PACKET IPCreatePacket(
+  ULONG Type)
+/*
+ * FUNCTION: Creates an IP packet object
+ * ARGUMENTS:
+ *     Type = Type of IP packet
+ * RETURNS:
+ *     Pointer to the created IP packet. NULL if there was not enough free resources.
+ */
+{
+  PIP_PACKET IPPacket;
+
+  IPPacket = ExAllocateFromNPagedLookasideList(&IPPacketList);
+  if (!IPPacket)
+    return NULL;
+
+    /* FIXME: Is this needed? */
+  RtlZeroMemory(IPPacket, sizeof(IP_PACKET));
+
+  INIT_TAG(IPPacket, TAG('I','P','K','T'));
+
+  IPPacket->Free       = FreePacket;
+  IPPacket->RefCount   = 1;
+  IPPacket->Type       = Type;
+  IPPacket->HeaderSize = 20;
+
+  return IPPacket;
+}
+
+PIP_PACKET IPInitializePacket(
+    PIP_PACKET IPPacket,
+    ULONG Type)
+/*
+ * FUNCTION: Creates an IP packet object
+ * ARGUMENTS:
+ *     Type = Type of IP packet
+ * RETURNS:
+ *     Pointer to the created IP packet. NULL if there was not enough free resources.
+ */
+{
+    /* FIXME: Is this needed? */
+    RtlZeroMemory(IPPacket, sizeof(IP_PACKET));
+    
+    INIT_TAG(IPPacket, TAG('I','P','K','T'));
+    
+    IPPacket->Free     = DontFreePacket;
+    IPPacket->RefCount = 1;
+    IPPacket->Type     = Type;
+    
+    return IPPacket;
+}
+
+
+PNET_TABLE_ENTRY IPCreateNTE(
+    PIP_INTERFACE IF,
+    PIP_ADDRESS Address,
+    UINT PrefixLength)
+/*
+ * FUNCTION: Creates a net table entry and binds it to an interface
+ * ARGUMENTS:
+ *     IF           = Pointer to interface
+ *     Address      = Pointer to interface address
+ *     PrefixLength = Length of prefix
+ * RETURNS:
+ *     Pointer to NTE, NULL if there was not enough free resources
+ * NOTES:
+ *     The interface lock must be held when called.
+ *     The net table entry retains a reference to the interface and
+ *     the provided address. The caller is responsible for providing
+ *     these references
+ */
+{
+    PNET_TABLE_ENTRY NTE;
+    PADDRESS_ENTRY ADE;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X)  Address (0x%X)  PrefixLength (%d).\n", IF, Address, PrefixLength));
+
+    TI_DbgPrint(DEBUG_IP, ("Address (%s).\n", A2S(Address)));
+
+    /* Allocate room for an NTE */
+    NTE = exAllocatePool(NonPagedPool, sizeof(NET_TABLE_ENTRY));
+    if (!NTE) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+    INIT_TAG(NTE, TAG('N','T','E',' '));
+    INIT_TAG(Address, TAG('A','D','R','S'));
+
+    NTE->Free = FreeNTE;
+
+    NTE->Interface = IF;
+
+    /* One reference is for beeing alive and one reference is for the ADE */
+    NTE->RefCount = 2;
+
+    NTE->Address = Address;
+    /* One reference is for NTE, one reference is given to the
+       address entry, and one reference is given to the prefix
+       list entry */
+    ReferenceObject(Address);
+    ReferenceObject(Address);
+    ReferenceObject(Address);
+
+    /* Create an address entry and add it to the list */
+    ADE = CreateADE(IF, NTE->Address, ADE_UNICAST, NTE);
+    if (!ADE) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        exFreePool(NTE);
+        return NULL;
+    }
+
+    /* Create a prefix list entry for unicast address */
+    NTE->PLE = CreatePLE(IF, NTE->Address, PrefixLength);
+    if (!NTE->PLE) {
+        DestroyADE(IF, ADE);
+        exFreePool(NTE);
+        return NULL;
+    }
+
+    /* Reference the interface for the prefix list entry */
+    ReferenceObject(IF);
+
+    /* Add NTE to the list on the interface */
+    InsertTailList(&IF->NTEListHead, &NTE->IFListEntry);
+
+    /* Add NTE to the global net table list */
+    ExInterlockedInsertTailList(&NetTableListHead, &NTE->NTListEntry, &NetTableListLock);
+
+    return NTE;
+}
+
+
+VOID DestroyNTE(
+    PIP_INTERFACE IF,
+    PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Destroys a net table entry
+ * ARGUMENTS:
+ *     IF  = Pointer to interface
+ *     NTE = Pointer to net table entry
+ * NOTES:
+ *     The net table list lock must be held when called
+ *     The interface lock must be held when called
+ */
+{
+    KIRQL OldIrql;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X)  NTE (0x%X).\n", IF, NTE));
+
+    TI_DbgPrint(DEBUG_IP, ("NTE (%s).\n", NTE->Address));
+
+    /* Invalidate the prefix list entry for this NTE */
+    KeAcquireSpinLock(&PrefixListLock, &OldIrql);
+    DestroyPLE(NTE->PLE);
+    KeReleaseSpinLock(&PrefixListLock, OldIrql);
+
+    /* Remove NTE from the interface list */
+    RemoveEntryList(&NTE->IFListEntry);
+    /* Remove NTE from the net table list */
+
+/* TODO: DEBUG: removed by RobD to prevent failure when testing under bochs 6 sept 2002.
+
+    RemoveEntryList(&NTE->NTListEntry);
+
+ */
+
+    /* Dereference the objects that are referenced */
+    DereferenceObject(NTE->Address);
+    DereferenceObject(NTE->Interface);
+#ifdef DBG
+    NTE->RefCount--;
+
+    if (NTE->RefCount != 0) {
+        TI_DbgPrint(MIN_TRACE, ("Net table entry at (0x%X) has (%d) references (should be 0).\n", NTE, NTE->RefCount));
+    }
+#endif
+    /* And free the NTE */
+    exFreePool(NTE);
+}
+
+
+VOID DestroyNTEs(
+    PIP_INTERFACE IF)
+/*
+ * FUNCTION: Destroys all net table entries on an interface
+ * ARGUMENTS:
+ *     IF  = Pointer to interface
+ * NOTES:
+ *     The net table list lock must be held when called
+ *     The interface lock may be held when called
+ */
+{
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+    PNET_TABLE_ENTRY Current;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
+
+    /* Search the list and remove every NTE we find */
+    CurrentEntry = IF->NTEListHead.Flink;
+    while (CurrentEntry != &IF->NTEListHead) {
+        NextEntry = CurrentEntry->Flink;
+             Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
+        /* Destroy the NTE */
+        DestroyNTE(IF, Current);
+        CurrentEntry = NextEntry;
+    }
+}
+
+
+PNET_TABLE_ENTRY IPLocateNTEOnInterface(
+    PIP_INTERFACE IF,
+    PIP_ADDRESS Address,
+    PUINT AddressType)
+/*
+ * FUNCTION: Locates an NTE on an interface
+ * ARGUMENTS:
+ *     IF          = Pointer to interface
+ *     Address     = Pointer to IP address
+ *     AddressType = Address of type of IP address
+ * NOTES:
+ *     If found, the NTE is referenced for the caller. The caller is
+ *     responsible for dereferencing after use
+ * RETURNS:
+ *     Pointer to net table entry, NULL if none was found
+ */
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY CurrentEntry;
+    PADDRESS_ENTRY Current;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X)  Address (%s)  AddressType (0x%X).\n",
+        IF, A2S(Address), AddressType));
+
+    KeAcquireSpinLock(&IF->Lock, &OldIrql);
+
+    /* Search the list and return the NTE if found */
+    CurrentEntry = IF->ADEListHead.Flink;
+
+    if (CurrentEntry == &IF->ADEListHead) {
+        TI_DbgPrint(DEBUG_IP, ("NTE list is empty!!!\n"));
+    }
+
+    while (CurrentEntry != &IF->ADEListHead) {
+             Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_ENTRY, ListEntry);
+        if (AddrIsEqual(Address, Current->Address)) {
+            ReferenceObject(Current->NTE);
+            *AddressType = Current->Type;
+            KeReleaseSpinLock(&IF->Lock, OldIrql);
+            return Current->NTE;
+        }
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeReleaseSpinLock(&IF->Lock, OldIrql);
+
+    return NULL;
+}
+
+
+PNET_TABLE_ENTRY IPLocateNTE(
+    PIP_ADDRESS Address,
+    PUINT AddressType)
+/*
+ * FUNCTION: Locates an NTE for the network Address is on 
+ * ARGUMENTS:
+ *     Address     = Pointer to an address to find associated NTE of
+ *     AddressType = Address of address type
+ * NOTES:
+ *     If found the NTE is referenced for the caller. The caller is
+ *     responsible for dereferencing after use
+ * RETURNS:
+ *     Pointer to NTE if the address was found, NULL if not.
+ */
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY CurrentEntry;
+    PNET_TABLE_ENTRY Current;
+    PNET_TABLE_ENTRY NTE;
+
+//    TI_DbgPrint(DEBUG_IP, ("Called. Address (0x%X)  AddressType (0x%X).\n",
+//        Address, AddressType));
+
+//    TI_DbgPrint(DEBUG_IP, ("Address (%s).\n", A2S(Address)));
+
+    KeAcquireSpinLock(&NetTableListLock, &OldIrql);
+
+    /* Search the list and return the NTE if found */
+    CurrentEntry = NetTableListHead.Flink;
+    while (CurrentEntry != &NetTableListHead) {
+             Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, NTListEntry);
+        NTE = IPLocateNTEOnInterface(Current->Interface, Address, AddressType);
+        if (NTE) {
+            ReferenceObject(NTE);
+            KeReleaseSpinLock(&NetTableListLock, OldIrql);
+            return NTE;
+        }
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeReleaseSpinLock(&NetTableListLock, OldIrql);
+
+    return NULL;
+}
+
+
+PADDRESS_ENTRY IPLocateADE(
+    PIP_ADDRESS Address,
+    UINT AddressType)
+/*
+ * FUNCTION: Locates an ADE for the address
+ * ARGUMENTS:
+ *     Address     = Pointer to an address to find associated ADE of
+ *     AddressType = Type of address
+ * RETURNS:
+ *     Pointer to ADE if the address was found, NULL if not.
+ * NOTES:
+ *     If found the ADE is referenced for the caller. The caller is
+ *     responsible for dereferencing after use
+ */
+{
+    KIRQL OldIrql;
+    IF_LIST_ITER(CurrentIF);
+    ADE_LIST_ITER(CurrentADE);
+
+//    TI_DbgPrint(DEBUG_IP, ("Called. Address (0x%X)  AddressType (0x%X).\n",
+//        Address, AddressType));
+
+//    TI_DbgPrint(DEBUG_IP, ("Address (%s).\n", A2S(Address)));
+
+    KeAcquireSpinLock(&InterfaceListLock, &OldIrql);
+
+    /* Search the interface list */
+    ForEachInterface(CurrentIF) {
+        /* Search the address entry list and return the ADE if found */
+       ForEachADE(CurrentIF->ADEListHead,CurrentADE) {
+            if ((AddrIsEqual(Address, CurrentADE->Address)) && 
+                (CurrentADE->Type == AddressType)) {
+                ReferenceObject(CurrentADE);
+                KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+                return CurrentADE;
+            }
+        } EndFor(CurrentADE);
+    } EndFor(CurrentIF);
+
+    KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+
+    return NULL;
+}
+
+
+PADDRESS_ENTRY IPGetDefaultADE(
+    UINT AddressType)
+/*
+ * FUNCTION: Returns a default address entry
+ * ARGUMENTS:
+ *     AddressType = Type of address
+ * RETURNS:
+ *     Pointer to ADE if found, NULL if not.
+ * NOTES:
+ *     Loopback interface is only considered if it is the only interface.
+ *     If found, the address entry is referenced
+ */
+{
+    KIRQL OldIrql;
+    ADE_LIST_ITER(CurrentADE);
+    IF_LIST_ITER(CurrentIF);
+    BOOLEAN LoopbackIsRegistered = FALSE;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. AddressType (0x%X).\n", AddressType));
+
+    KeAcquireSpinLock(&InterfaceListLock, &OldIrql);
+
+    /* Search the interface list */
+    ForEachInterface(CurrentIF) {
+        if (CurrentIF != Loopback) {
+            /* Search the address entry list and return the first appropriate ADE found */
+           TI_DbgPrint(DEBUG_IP,("Checking interface %x\n", CurrentIF));
+           ForEachADE(CurrentIF->ADEListHead,CurrentADE) {
+                if (CurrentADE->Type == AddressType) {
+                    ReferenceObject(CurrentADE);
+                    KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+                    return CurrentADE;
+                }
+           } EndFor(CurrentADE);
+        } else
+            LoopbackIsRegistered = TRUE;
+    } EndFor(CurrentIF);
+
+    /* No address was found. Use loopback interface if available */
+    if (LoopbackIsRegistered) {
+       ForEachADE(CurrentIF->ADEListHead,CurrentADE) {
+            if (CurrentADE->Type == AddressType) {
+                ReferenceObject(CurrentADE);
+                KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+                return CurrentADE;
+            }
+        } EndFor(CurrentADE);
+    }
+
+    KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+
+    return NULL;
+}
+
+
+VOID STDCALL IPTimeout(
+    PKDPC Dpc,
+    PVOID DeferredContext,
+    PVOID SystemArgument1,
+    PVOID SystemArgument2)
+/*
+ * FUNCTION: Timeout DPC
+ * ARGUMENTS:
+ *     Dpc             = Pointer to our DPC object
+ *     DeferredContext = Pointer to context information (unused)
+ *     SystemArgument1 = Unused
+ *     SystemArgument2 = Unused
+ * NOTES:
+ *     This routine is dispatched once in a while to do maintainance jobs
+ */
+{
+    /* Check if datagram fragments have taken too long to assemble */
+    IPDatagramReassemblyTimeout();
+
+    /* Clean possible outdated cached neighbor addresses */
+    NBTimeout();
+
+    /* Call upper layer timeout routines */
+    TCPTimeout();
+}
+
+
+VOID IPDispatchProtocol(
+    PNET_TABLE_ENTRY NTE,
+    PIP_PACKET IPPacket)
+/*
+ * FUNCTION: IP protocol dispatcher
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry which the packet was received on
+ *     IPPacket = Pointer to an IP packet that was received
+ * NOTES:
+ *     This routine examines the IP header and passes the packet on to the
+ *     right upper level protocol receive handler
+ */
+{
+    UINT Protocol;
+
+    switch (IPPacket->Type) {
+    case IP_ADDRESS_V4:
+        Protocol = ((PIPv4_HEADER)(IPPacket->Header))->Protocol;
+        break;
+    case IP_ADDRESS_V6:
+        /* FIXME: IPv6 adresses not supported */
+        TI_DbgPrint(MIN_TRACE, ("IPv6 datagram discarded.\n"));
+        return;
+    default:
+        Protocol = 0;
+    }
+
+    /* Call the appropriate protocol handler */
+    (*ProtocolTable[Protocol])(NTE, IPPacket);
+}
+
+
+PIP_INTERFACE IPCreateInterface(
+    PLLIP_BIND_INFO BindInfo)
+/*
+ * FUNCTION: Creates an IP interface
+ * ARGUMENTS:
+ *     BindInfo = Pointer to link layer to IP binding information
+ * RETURNS:
+ *     Pointer to IP_INTERFACE structure, NULL if there was
+ *     not enough free resources
+ */
+{
+    PIP_INTERFACE IF;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. BindInfo (0x%X).\n", BindInfo));
+
+#ifdef DBG
+    if (BindInfo->Address) {
+        PUCHAR A = BindInfo->Address;
+        TI_DbgPrint(DEBUG_IP, ("Interface address (%02X %02X %02X %02X %02X %02X).\n",
+            A[0], A[1], A[2], A[3], A[4], A[5]));
+    }
+#endif
+
+    IF = exAllocatePool(NonPagedPool, sizeof(IP_INTERFACE));
+    if (!IF) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+    INIT_TAG(IF, TAG('F','A','C','E'));
+
+    IF->Free       = FreeIF;
+    IF->RefCount   = 1;
+    IF->Context    = BindInfo->Context;
+    IF->HeaderSize = BindInfo->HeaderSize;
+         if (IF->HeaderSize > MaxLLHeaderSize)
+               MaxLLHeaderSize = IF->HeaderSize;
+
+    IF->MinFrameSize = BindInfo->MinFrameSize;
+         if (IF->MinFrameSize > MinLLFrameSize)
+               MinLLFrameSize = IF->MinFrameSize;
+
+    IF->MTU           = BindInfo->MTU;
+    IF->Address       = BindInfo->Address;
+    IF->AddressLength = BindInfo->AddressLength;
+    IF->Transmit      = BindInfo->Transmit;
+
+    InitializeListHead(&IF->ADEListHead);
+    InitializeListHead(&IF->NTEListHead);
+
+    KeInitializeSpinLock(&IF->Lock);
+
+#ifdef __NTDRIVER__
+    InsertTDIInterfaceEntity( IF );
+#endif
+
+    return IF;
+}
+
+
+VOID IPDestroyInterface(
+    PIP_INTERFACE IF)
+/*
+ * FUNCTION: Destroys an IP interface
+ * ARGUMENTS:
+ *     IF = Pointer to interface to destroy
+ */
+{
+    KIRQL OldIrql1;
+    KIRQL OldIrql2;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
+
+#ifdef __NTDRIVER__
+    RemoveTDIInterfaceEntity( IF );
+#endif
+
+    KeAcquireSpinLock(&NetTableListLock, &OldIrql1);
+    KeAcquireSpinLock(&IF->Lock, &OldIrql2);
+    DestroyADEs(IF);
+    DestroyNTEs(IF);
+    KeReleaseSpinLock(&IF->Lock, OldIrql2);
+    KeReleaseSpinLock(&NetTableListLock, OldIrql1);
+
+#ifdef DBG
+    IF->RefCount--;
+
+    if (IF->RefCount != 0) {
+        TI_DbgPrint(MIN_TRACE, ("Interface at (0x%X) has (%d) references (should be 0).\n", IF, IF->RefCount));
+    }
+#endif
+
+    exFreePool(IF);
+}
+
+
+BOOLEAN IPRegisterInterface(
+    PIP_INTERFACE IF)
+/*
+ * FUNCTION: Registers an IP interface with IP layer
+ * ARGUMENTS:
+ *     IF = Pointer to interface to register
+ * RETURNS;
+ *     TRUE if interface was successfully registered, FALSE if not
+ */
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY CurrentEntry;
+    PNET_TABLE_ENTRY Current;
+    PROUTE_CACHE_NODE RCN;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+
+    TI_DbgPrint(MID_TRACE, ("Called. IF (0x%X).\n", IF));
+
+    KeAcquireSpinLock(&IF->Lock, &OldIrql);
+
+    /* Add routes to all NTEs on this interface */
+    CurrentEntry = IF->NTEListHead.Flink;
+    while (CurrentEntry != &IF->NTEListHead) {
+           Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
+
+        /* Add a permanent neighbor for this NTE */
+        ReferenceObject(Current->Address);
+        NCE = NBAddNeighbor(IF, Current->Address, IF->Address,
+            IF->AddressLength, NUD_PERMANENT);
+        if (!NCE) {
+            TI_DbgPrint(MIN_TRACE, ("Could not create NCE.\n"));
+            DereferenceObject(Current->Address);
+            KeReleaseSpinLock(&IF->Lock, OldIrql);
+            return FALSE;
+        }
+
+        /* Reference objects for forward information base */
+        ReferenceObject(Current->Address);
+        ReferenceObject(Current->PLE->Prefix);
+       ReferenceObject(NCE);
+
+        /* NCE is already referenced */
+        if (!RouterAddRoute(Current->Address, Current->PLE->Prefix, NCE, 1)) {
+            TI_DbgPrint(MIN_TRACE, ("Could not add route due to insufficient resources.\n"));
+            DereferenceObject(Current->Address);
+            DereferenceObject(Current->PLE->Prefix);
+            DereferenceObject(NCE);
+        }
+
+        RCN = RouteAddRouteToDestination(Current->Address, Current, IF, NCE);
+        if (!RCN) {
+            TI_DbgPrint(MIN_TRACE, ("Could not create RCN.\n"));
+            DereferenceObject(Current->Address);
+            KeReleaseSpinLock(&IF->Lock, OldIrql);
+        }
+        /* Don't need this any more since the route cache references the NCE */
+        DereferenceObject(NCE);
+
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    /* Add interface to the global interface list */
+    ASSERT(&IF->ListEntry);
+    ExInterlockedInsertTailList(&InterfaceListHead, 
+                               &IF->ListEntry, 
+                               &InterfaceListLock);
+
+    /* Allow TCP to hang some configuration on this interface */
+    IF->TCPContext = TCPPrepareInterface( IF );
+
+    KeReleaseSpinLock(&IF->Lock, OldIrql);
+
+    return TRUE;
+}
+
+
+VOID IPUnregisterInterface(
+    PIP_INTERFACE IF)
+/*
+ * FUNCTION: Unregisters an IP interface with IP layer
+ * ARGUMENTS:
+ *     IF = Pointer to interface to unregister
+ */
+{
+    KIRQL OldIrql1;
+    KIRQL OldIrql2;
+    KIRQL OldIrql3;
+    PLIST_ENTRY CurrentEntry;
+    PNET_TABLE_ENTRY Current;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
+
+    KeAcquireSpinLock(&NetTableListLock, &OldIrql1);
+    KeAcquireSpinLock(&IF->Lock, &OldIrql2);
+
+    /* Remove routes to all NTEs on this interface */
+    CurrentEntry = IF->NTEListHead.Flink;
+    while (CurrentEntry != &IF->NTEListHead) {
+        Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
+
+        /* Remove NTE from global net table list */
+        RemoveEntryList(&Current->NTListEntry);
+
+        /* Remove all references from route cache to NTE */
+        RouteInvalidateNTE(Current);
+
+        /* Remove permanent NCE, but first we have to find it */
+        NCE = NBLocateNeighbor(Current->Address);
+        if (NCE) {
+            DereferenceObject(NCE);
+            NBRemoveNeighbor(NCE);
+        }
+
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeAcquireSpinLock(&InterfaceListLock, &OldIrql3);
+    /* Ouch...three spinlocks acquired! Fortunately
+       we don't unregister interfaces very often */
+    RemoveEntryList(&IF->ListEntry);
+    KeReleaseSpinLock(&InterfaceListLock, OldIrql3);
+
+    KeReleaseSpinLock(&IF->Lock, OldIrql2);
+    KeReleaseSpinLock(&NetTableListLock, OldIrql1);
+}
+
+
+VOID IPRegisterProtocol(
+    UINT ProtocolNumber,
+    IP_PROTOCOL_HANDLER Handler)
+/*
+ * FUNCTION: Registers a handler for an IP protocol number
+ * ARGUMENTS:
+ *     ProtocolNumber = Internet Protocol number for which to register handler
+ *     Handler        = Pointer to handler to be called when a packet is received
+ * NOTES:
+ *     To unregister a protocol handler, call this function with Handler = NULL
+ */
+{
+#ifdef DBG
+    if (ProtocolNumber >= IP_PROTOCOL_TABLE_SIZE)
+        TI_DbgPrint(MIN_TRACE, ("Protocol number is out of range (%d).\n", ProtocolNumber));
+#endif
+
+    ProtocolTable[ProtocolNumber] = Handler;
+}
+
+
+VOID DefaultProtocolHandler(
+    PNET_TABLE_ENTRY NTE,
+    PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Default handler for Internet protocols
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry which the packet was received on
+ *     IPPacket = Pointer to an IP packet that was received
+ */
+{
+    TI_DbgPrint(MID_TRACE, ("Packet of unknown Internet protocol discarded.\n"));
+}
+
+
+NTSTATUS IPStartup(
+    PDRIVER_OBJECT DriverObject,
+    PUNICODE_STRING RegistryPath)
+/*
+ * FUNCTION: Initializes the IP subsystem
+ * ARGUMENTS:
+ *     DriverObject = Pointer to a driver object for this driver
+ *     RegistryPath = Our registry node for configuration parameters
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    UINT i;
+    LARGE_INTEGER DueTime;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+       MaxLLHeaderSize = 0;
+    MinLLFrameSize  = 0;
+
+    /* Initialize lookaside lists */
+    ExInitializeNPagedLookasideList(
+      &IPDRList,                      /* Lookaside list */
+           NULL,                           /* Allocate routine */
+           NULL,                           /* Free routine */
+           0,                              /* Flags */
+           sizeof(IPDATAGRAM_REASSEMBLY),  /* Size of each entry */
+           TAG('I','P','D','R'),           /* Tag */
+           0);                             /* Depth */
+
+    ExInitializeNPagedLookasideList(
+      &IPPacketList,                  /* Lookaside list */
+           NULL,                           /* Allocate routine */
+           NULL,                           /* Free routine */
+           0,                              /* Flags */
+           sizeof(IP_PACKET),              /* Size of each entry */
+           TAG('I','P','P','K'),           /* Tag */
+           0);                             /* Depth */
+
+    ExInitializeNPagedLookasideList(
+      &IPFragmentList,                /* Lookaside list */
+           NULL,                           /* Allocate routine */
+           NULL,                           /* Free routine */
+           0,                              /* Flags */
+           sizeof(IP_FRAGMENT),            /* Size of each entry */
+           TAG('I','P','F','G'),           /* Tag */
+           0);                             /* Depth */
+
+    ExInitializeNPagedLookasideList(
+      &IPHoleList,                    /* Lookaside list */
+           NULL,                           /* Allocate routine */
+           NULL,                           /* Free routine */
+           0,                              /* Flags */
+           sizeof(IPDATAGRAM_HOLE),        /* Size of each entry */
+           TAG('I','P','H','L'),           /* Tag */
+           0);                             /* Depth */
+
+    /* Start routing subsystem */
+    RouterStartup();
+
+    /* Start route cache subsystem */
+    RouteStartup();
+
+    /* Start neighbor cache subsystem */
+    NBStartup();
+
+    /* Fill the protocol dispatch table with pointers
+       to the default protocol handler */
+    for (i = 0; i < IP_PROTOCOL_TABLE_SIZE; i++)
+        IPRegisterProtocol(i, DefaultProtocolHandler);
+
+    /* Register network level protocol receive handlers */
+    IPRegisterProtocol(IPPROTO_ICMP, ICMPReceive);
+
+    /* Initialize NTE list and protecting lock */
+    InitializeListHead(&NetTableListHead);
+    KeInitializeSpinLock(&NetTableListLock);
+
+    /* Initialize reassembly list and protecting lock */
+    InitializeListHead(&ReassemblyListHead);
+    KeInitializeSpinLock(&ReassemblyListLock);
+
+    InitPLE();
+
+    /* Initialize our periodic timer and its associated DPC object. When the
+       timer expires, the IPTimeout deferred procedure call (DPC) is queued */
+    KeInitializeDpc(&IPTimeoutDpc, IPTimeout, NULL);
+    KeInitializeTimer(&IPTimer);
+
+    /* Start the periodic timer with an initial and periodic
+       relative expiration time of IP_TIMEOUT milliseconds */
+    DueTime.QuadPart = -(LONGLONG)IP_TIMEOUT * 10000;
+    KeSetTimerEx(&IPTimer, DueTime, IP_TIMEOUT, &IPTimeoutDpc);
+
+    IPInitialized = TRUE;
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS IPShutdown(
+    VOID)
+/*
+ * FUNCTION: Shuts down the IP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    if (!IPInitialized)
+        return STATUS_SUCCESS;
+
+    /* Cancel timer */
+    KeCancelTimer(&IPTimer);
+
+    /* Shutdown neighbor cache subsystem */
+    NBShutdown();
+
+    /* Shutdown route cache subsystem */
+    RouteShutdown();
+
+    /* Shutdown routing subsystem */
+    RouterShutdown();
+
+    IPFreeReassemblyList();
+
+    /* Clear prefix list */
+    DestroyPLEs();
+
+    /* Destroy lookaside lists */
+    ExDeleteNPagedLookasideList(&IPHoleList);
+    ExDeleteNPagedLookasideList(&IPDRList);
+    ExDeleteNPagedLookasideList(&IPPacketList);
+    ExDeleteNPagedLookasideList(&IPFragmentList);
+
+    IPInitialized = FALSE;
+
+    return STATUS_SUCCESS;
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/loopback.c b/reactos/drivers/lib/ip/network/loopback.c
new file mode 100644 (file)
index 0000000..4d25c5d
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        datalink/loopback.c
+ * PURPOSE:     Loopback adapter
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+WORK_QUEUE_ITEM LoopWorkItem;
+PIP_INTERFACE Loopback = NULL;
+/* Indicates wether the loopback interface is currently transmitting */
+BOOLEAN LoopBusy = FALSE;
+/* Loopback transmit queue */
+PNDIS_PACKET LoopQueueHead = (PNDIS_PACKET)NULL;
+PNDIS_PACKET LoopQueueTail = (PNDIS_PACKET)NULL;
+/* Spin lock for protecting loopback transmit queue */
+KSPIN_LOCK LoopQueueLock;
+
+
+VOID STDCALL RealTransmit(
+  PVOID Context)
+/*
+ * FUNCTION: Transmits one or more packet(s) in loopback queue to ourselves
+ * ARGUMENTS:
+ *   Context = Pointer to context information (loopback interface)
+ */
+{
+  PNDIS_PACKET NdisPacket;
+  PNDIS_BUFFER NdisBuffer;
+  IP_PACKET IPPacket;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  KeAcquireSpinLockAtDpcLevel(&LoopQueueLock);
+
+  while (TRUE)
+    {
+      /* Get the next packet from the queue (if any) */
+      NdisPacket = LoopQueueHead;
+      if (!NdisPacket)
+        break;
+
+      TI_DbgPrint(MAX_TRACE, ("NdisPacket (0x%X)\n", NdisPacket));
+
+      LoopQueueHead = *(PNDIS_PACKET*)NdisPacket->u.s3.MacReserved;
+      KeReleaseSpinLockFromDpcLevel(&LoopQueueLock);
+      IPPacket.NdisPacket = NdisPacket;
+
+      NdisGetFirstBufferFromPacket(NdisPacket,
+        &NdisBuffer,
+        &IPPacket.Header,
+        &IPPacket.ContigSize,
+        &IPPacket.TotalSize);
+      IPReceive(Context, &IPPacket);
+      AdjustPacket(NdisPacket, 0, PC(NdisPacket)->DLOffset);
+      PC(NdisPacket)->DLComplete(Context, NdisPacket, NDIS_STATUS_SUCCESS);
+      KeAcquireSpinLockAtDpcLevel(&LoopQueueLock);
+    }
+
+  LoopBusy = FALSE;
+  KeReleaseSpinLockFromDpcLevel(&LoopQueueLock);
+}
+
+VOID LoopTransmit(
+  PVOID Context,
+  PNDIS_PACKET NdisPacket,
+  UINT Offset,
+  PVOID LinkAddress,
+  USHORT Type)
+/*
+ * FUNCTION: Transmits a packet
+ * ARGUMENTS:
+ *   Context     = Pointer to context information (NULL)
+ *   NdisPacket  = Pointer to NDIS packet to send
+ *   Offset      = Offset in packet where packet data starts
+ *   LinkAddress = Pointer to link address
+ *   Type        = LAN protocol type (unused)
+ */
+{
+  PNDIS_PACKET *pNdisPacket;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  /* NDIS send routines don't have an offset argument so we
+     must offset the data in upper layers and adjust the
+     packet here. We save the offset in the packet context
+     area so it can be undone before we release the packet */
+  AdjustPacket(NdisPacket, Offset, 0);
+  PC(NdisPacket)->DLOffset = Offset;
+
+  pNdisPacket = (PNDIS_PACKET*)NdisPacket->u.s3.MacReserved;
+  *pNdisPacket = NULL;
+
+  KeAcquireSpinLock(&LoopQueueLock, &OldIrql);
+
+  /* Add packet to transmit queue */
+  if (LoopQueueHead != NULL)
+    {
+      /* Transmit queue is not empty */
+      pNdisPacket = (PNDIS_PACKET*)LoopQueueTail->u.s3.MacReserved;
+      *pNdisPacket = NdisPacket;
+    }
+  else
+    {
+      /* Transmit queue is empty */
+      LoopQueueHead = NdisPacket;
+    }
+
+  LoopQueueTail = NdisPacket;
+
+  /* If RealTransmit is not running (or scheduled to run) then schedule it to run now */
+  if (!LoopBusy)
+    {
+      LoopBusy = TRUE;  /* The loopback interface is now busy */
+      ExQueueWorkItem(&LoopWorkItem, CriticalWorkQueue);
+    }
+
+  KeReleaseSpinLock(&LoopQueueLock, OldIrql);
+}
+
+NDIS_STATUS LoopRegisterAdapter(
+  PNDIS_STRING AdapterName,
+  PLAN_ADAPTER *Adapter)
+/*
+ * FUNCTION: Registers loopback adapter with the network layer
+ * ARGUMENTS:
+ *   AdapterName = Unused
+ *   Adapter     = Unused
+ * RETURNS:
+ *   Status of operation
+ */
+{
+  PIP_ADDRESS Address;
+  NDIS_STATUS Status;
+
+  Status = NDIS_STATUS_SUCCESS;
+
+  TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+  Address = AddrBuildIPv4(LOOPBACK_ADDRESS_IPv4);
+  if (Address != NULL)
+    {
+      LLIP_BIND_INFO BindInfo;
+
+      /* Bind the adapter to network (IP) layer */
+      BindInfo.Context = NULL;
+      BindInfo.HeaderSize = 0;
+      BindInfo.MinFrameSize = 0;
+      BindInfo.MTU = 16384;
+      BindInfo.Address = NULL;
+      BindInfo.AddressLength = 0;
+      BindInfo.Transmit = LoopTransmit;
+
+      Loopback = IPCreateInterface(&BindInfo);
+
+      if ((Loopback != NULL) && (IPCreateNTE(Loopback, Address, 8)))
+        {
+          /* Reference the interface for the NTE. The reference for
+             the address is just passed on to the NTE */
+          ReferenceObject(Loopback);
+
+          IPRegisterInterface(Loopback);
+
+          ExInitializeWorkItem(&LoopWorkItem, RealTransmit, Loopback);
+
+          KeInitializeSpinLock(&LoopQueueLock);
+          LoopBusy = FALSE;
+        }
+      else
+        {
+          Status = NDIS_STATUS_RESOURCES;
+        }
+    }
+  else
+    {
+      Status = NDIS_STATUS_RESOURCES;
+    }
+
+  if (!NT_SUCCESS(Status))
+    {
+      LoopUnregisterAdapter(NULL);
+    }
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+  return Status;
+}
+
+
+NDIS_STATUS LoopUnregisterAdapter(
+  PLAN_ADAPTER Adapter)
+/*
+ * FUNCTION: Unregisters loopback adapter with the network layer
+ * ARGUMENTS:
+ *   Adapter = Unused
+ * RETURNS:
+ *   Status of operation
+ * NOTES:
+ *   Does not care wether we have registered loopback adapter
+ */
+{
+  TI_DbgPrint(MID_TRACE, ("Called.\n"));
+
+  if (Loopback != NULL)
+    {
+      IPUnregisterInterface(Loopback);
+      IPDestroyInterface(Loopback);
+      Loopback = NULL;
+    }
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+  return NDIS_STATUS_SUCCESS;
+}
diff --git a/reactos/drivers/lib/ip/network/memtrack.c b/reactos/drivers/lib/ip/network/memtrack.c
new file mode 100644 (file)
index 0000000..dda61e9
--- /dev/null
@@ -0,0 +1,143 @@
+#define MEMTRACK_NO_POOL
+#include "precomp.h"
+
+#ifdef MEMTRACK
+
+LIST_ENTRY AllocatedObjectsList;
+KSPIN_LOCK AllocatedObjectsLock;
+DWORD TagsToShow[MEMTRACK_MAX_TAGS_TO_TRACK] = { 0 };
+
+VOID TrackTag( DWORD Tag ) {
+    UINT i;
+
+    for( i = 0; TagsToShow[i]; i++ );
+    TagsToShow[i] = Tag;
+}
+
+VOID TrackingInit() {
+    KeInitializeSpinLock( &AllocatedObjectsLock );
+    InitializeListHead( &AllocatedObjectsList );
+}
+
+VOID ShowTrackedThing( PCHAR What, PALLOCATION_TRACKER Thing, 
+                      PCHAR File, UINT Line ) {
+    /* if( ShowTag( Thing->Tag ) ) */
+    if( File ) {
+       DbgPrint( "[%s] Thing %08x %c%c%c%c (%s:%d) (Called from %s:%d)\n", 
+                 What,
+                 Thing->Thing,
+                 ((PCHAR)&Thing->Tag)[3],
+                 ((PCHAR)&Thing->Tag)[2],
+                 ((PCHAR)&Thing->Tag)[1],
+                 ((PCHAR)&Thing->Tag)[0],
+                 Thing->FileName,
+                 Thing->LineNo,
+                 File, Line );
+    } else {
+       DbgPrint( "[%s] Thing %08x %c%c%c%c (%s:%d)\n", 
+                 What,
+                 Thing->Thing,
+                 ((PCHAR)&Thing->Tag)[3],
+                 ((PCHAR)&Thing->Tag)[2],
+                 ((PCHAR)&Thing->Tag)[1],
+                 ((PCHAR)&Thing->Tag)[0],
+                 Thing->FileName,
+                 Thing->LineNo );
+    }
+}
+
+VOID TrackWithTag( DWORD Tag, PVOID Thing, PCHAR FileName, DWORD LineNo ) {
+    PALLOCATION_TRACKER TrackedThing = 
+       ExAllocatePool( NonPagedPool, sizeof(*TrackedThing) );
+
+    KIRQL OldIrql;
+    PLIST_ENTRY Entry;
+    PALLOCATION_TRACKER ThingInList;
+
+    KeAcquireSpinLock( &AllocatedObjectsLock, &OldIrql );
+    Entry = AllocatedObjectsList.Flink;
+    while( Entry != &AllocatedObjectsList ) {
+       ThingInList = CONTAINING_RECORD(Entry, ALLOCATION_TRACKER, Entry);
+       if( ThingInList->Thing == Thing ) {
+           RemoveEntryList(Entry);
+           
+           KeReleaseSpinLock( &AllocatedObjectsLock, OldIrql );
+           ShowTrackedThing( "Alloc", ThingInList, FileName, LineNo );
+           ExFreePool( ThingInList );
+           TrackDumpFL( FileName, LineNo );
+           DbgPrint("TRACK: SPECIFIED ALREADY ALLOCATED ITEM %x\n", Thing);
+           KeBugCheck( 0 );
+       }
+       Entry = Entry->Flink;
+    }
+
+    KeReleaseSpinLock( &AllocatedObjectsLock, OldIrql );
+
+    if( TrackedThing ) {
+       TrackedThing->Tag      = Tag;
+       TrackedThing->Thing    = Thing;
+       TrackedThing->FileName = FileName;
+       TrackedThing->LineNo   = LineNo;
+       
+       ExInterlockedInsertTailList( &AllocatedObjectsList, 
+                                    &TrackedThing->Entry,
+                                    &AllocatedObjectsLock );
+       ShowTrackedThing( "Alloc", TrackedThing, FileName, LineNo );
+    }
+
+    /*TrackDumpFL( FileName, LineNo );*/
+}
+
+BOOL ShowTag( DWORD Tag ) {
+    UINT i;
+
+    for( i = 0; TagsToShow[i] && TagsToShow[i] != Tag; i++ );
+    
+    return TagsToShow[i] ? TRUE : FALSE;
+}
+
+VOID UntrackFL( PCHAR File, DWORD Line, PVOID Thing ) {
+    KIRQL OldIrql;
+    PLIST_ENTRY Entry;
+    PALLOCATION_TRACKER ThingInList;
+
+    KeAcquireSpinLock( &AllocatedObjectsLock, &OldIrql );
+    Entry = AllocatedObjectsList.Flink;
+    while( Entry != &AllocatedObjectsList ) {
+       ThingInList = CONTAINING_RECORD(Entry, ALLOCATION_TRACKER, Entry);
+       if( ThingInList->Thing == Thing ) {
+           RemoveEntryList(Entry);
+           
+           ShowTrackedThing( "Free ", ThingInList, File, Line );
+           
+           ExFreePool( ThingInList );
+           KeReleaseSpinLock( &AllocatedObjectsLock, OldIrql );
+           /* TrackDumpFL( File, Line ); */
+           return;
+       }
+       Entry = Entry->Flink;
+    }
+    KeReleaseSpinLock( &AllocatedObjectsLock, OldIrql );
+    TrackDumpFL( File, Line );
+    DbgPrint("UNTRACK: SPECIFIED ALREADY FREE ITEM %x\n", Thing);
+    KeBugCheck( 0 );
+}
+
+VOID TrackDumpFL( PCHAR File, DWORD Line ) {
+    KIRQL OldIrql;
+    PLIST_ENTRY Entry;
+    PALLOCATION_TRACKER Thing;
+
+    DbgPrint("Dump: %s:%d\n", File, Line);
+
+    KeAcquireSpinLock( &AllocatedObjectsLock, &OldIrql );
+    Entry = AllocatedObjectsList.Flink;
+    while( Entry != &AllocatedObjectsList ) {
+       Thing = CONTAINING_RECORD(Entry, ALLOCATION_TRACKER, Entry);
+       ShowTrackedThing( "Dump ", Thing, 0, 0 );
+       Entry = Entry->Flink;
+    }
+    KeReleaseSpinLock( &AllocatedObjectsLock, OldIrql );
+}
+
+#endif/*MEMTRACK*/
diff --git a/reactos/drivers/lib/ip/network/neighbor.c b/reactos/drivers/lib/ip/network/neighbor.c
new file mode 100644 (file)
index 0000000..51e41f6
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/neighbor.c
+ * PURPOSE:     Neighbor address cache
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1];
+
+
+VOID FreeNCE(
+  PVOID Object)
+{
+  ExFreePool(Object);
+}
+
+VOID NCETimeout(
+  PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Neighbor cache entry timeout handler
+ * NOTES:
+ *   The neighbor cache lock must be held
+ */
+{
+  PNDIS_PACKET NdisPacket;
+  PNDIS_PACKET NextNdisPacket;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
+
+  TI_DbgPrint(DEBUG_NCACHE, ("NCE->State is (0x%X).\n", NCE->State));
+
+  switch (NCE->State)
+    {
+      case NUD_INCOMPLETE:
+        /* Retransmission timer expired */
+        if (NCE->EventCount++ > MAX_MULTICAST_SOLICIT)
+          {
+            /* We have retransmitted too many times */
+
+            /* Calling IPSendComplete with cache lock held is not
+              a great thing to do. We don't get here very often
+              so maybe it's not that big a problem */
+
+            /* Flush packet queue */
+            NdisPacket = NCE->WaitQueue;
+            while (NdisPacket != NULL)
+              {
+                NextNdisPacket = (PNDIS_PACKET)PC(NdisPacket)->DLComplete;
+                IPSendComplete((PVOID)NCE->Interface,
+                  NdisPacket,
+                  NDIS_STATUS_REQUEST_ABORTED);
+                NdisPacket = NextNdisPacket;
+              }
+            NCE->WaitQueue = NULL;
+
+            NCE->EventCount = 0;
+
+            /* Remove route cache entries with references to this NCE.
+              Remember that neighbor cache lock is acquired before the
+              route cache lock */
+            RouteInvalidateNCE(NCE);
+          }
+        else
+          {
+            /* Retransmit request */
+            NBSendSolicit(NCE);
+          }
+        break;
+
+      case NUD_DELAY:
+        /* FIXME: Delayed state */
+        TI_DbgPrint(DEBUG_NCACHE, ("NCE delay state.\n"));
+        break;
+
+      case NUD_PROBE:
+        /* FIXME: Probe state */
+        TI_DbgPrint(DEBUG_NCACHE, ("NCE probe state.\n"));
+        break;
+
+      default:
+        /* Should not happen since the event timer is not used in the other states */
+        TI_DbgPrint(MIN_TRACE, ("Invalid NCE state (%d).\n", NCE->State));
+        break;
+    }
+}
+
+
+VOID NBTimeout(
+    VOID)
+/*
+ * FUNCTION: Neighbor address cache timeout handler
+ * NOTES:
+ *     This routine is called by IPTimeout to remove outdated cache
+ *     entries.
+ */
+{
+    UINT i;
+    KIRQL OldIrql;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+
+    for (i = 0; i <= NB_HASHMASK; i++) {
+        KeAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
+
+        for (NCE = NeighborCache[i].Cache;
+            NCE != NULL; NCE = NCE->Next) {
+            /* Check if event timer is running */
+            if (NCE->EventTimer > 0)  {
+                NCE->EventTimer--;
+                if (NCE->EventTimer == 0) {
+                    /* Call timeout handler for NCE */
+                    NCETimeout(NCE);
+                }
+            }
+        }
+
+        KeReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
+    }
+}
+
+VOID NBStartup(
+  VOID)
+/*
+ * FUNCTION: Starts the neighbor cache
+ */
+{
+  UINT i;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
+
+  for (i = 0; i <= NB_HASHMASK; i++)
+    {
+      NeighborCache[i].Cache = NULL;
+      KeInitializeSpinLock(&NeighborCache[i].Lock);
+    }
+}
+
+VOID NBShutdown(
+  VOID)
+/*
+ * FUNCTION: Shuts down the neighbor cache
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NextNCE;
+  PNEIGHBOR_CACHE_ENTRY CurNCE;
+  PNDIS_PACKET NextNdisPacket;
+  PNDIS_PACKET NdisPacket;
+  KIRQL OldIrql;
+  UINT i;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called.\n"));
+
+  /* Remove possible entries from the cache */
+  for (i = 0; i <= NB_HASHMASK; i++)
+    {
+      KeAcquireSpinLock(&NeighborCache[i].Lock, &OldIrql);
+
+      CurNCE = NeighborCache[i].Cache;
+      while (CurNCE)
+        {
+          NextNCE = CurNCE->Next;
+
+          /* Remove all references from route cache */
+          RouteInvalidateNCE(CurNCE);
+
+          /* Flush wait queue */
+          NdisPacket = CurNCE->WaitQueue;
+          while (NdisPacket)
+            {
+              NextNdisPacket = (PNDIS_PACKET)PC(NdisPacket)->DLComplete;
+              FreeNdisPacket(NdisPacket);
+              NdisPacket = NextNdisPacket;
+            }
+
+#ifdef DBG
+          if (CurNCE->RefCount != 1)
+            {
+              TI_DbgPrint(DEBUG_REFCOUNT, ("NCE at (0x%X) has (%d) references (should be 1).\n", CurNCE, CurNCE->RefCount));
+            }
+#endif
+
+        /* Remove reference for being alive */
+        DereferenceObject(CurNCE);
+
+        CurNCE = NextNCE;
+      }
+
+    NeighborCache[i].Cache = NULL;
+
+    KeReleaseSpinLock(&NeighborCache[i].Lock, OldIrql);
+  }
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+VOID NBSendSolicit(
+  PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Sends a neighbor solicitation message
+ * ARGUMENTS:
+ *   NCE = Pointer to NCE of neighbor to solicit
+ * NOTES:
+ *   May be called with lock held on NCE's table
+ */
+{
+  PLIST_ENTRY CurrentEntry;
+  PNET_TABLE_ENTRY NTE;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
+
+  if (NCE->State == NUD_INCOMPLETE)
+    {
+      /* This is the first solicitation of this neighbor. Broadcast
+          a request for the neighbor */
+
+      /* FIXME: Choose first NTE. We might want to give an NTE as argument */
+      if (!NCE->Interface || !NCE->Interface->NTEListHead.Flink) {
+         TI_DbgPrint(MID_TRACE, 
+                     ("NCE->Interface: %x, "
+                      "NCE->Interface->NTEListHead.Flink %x\n",
+                      NCE->Interface,
+                      NCE->Interface ? NCE->Interface->NTEListHead.Flink : 0));
+      }
+      if (!IsListEmpty(&NCE->Interface->NTEListHead))
+        {
+           CurrentEntry = NCE->Interface->NTEListHead.Flink;
+           NTE = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, 
+                                   IFListEntry);
+           ARPTransmit(&NCE->Address, NTE);
+        }
+      else
+        {
+           TI_DbgPrint(MIN_TRACE, ("Interface at 0x%X has zero NTE.\n", 
+                                   NCE->Interface));
+        }
+    }
+  else
+    {
+      /* FIXME: Unicast solicitation since we have a cached address */
+      TI_DbgPrint(MIN_TRACE, ("Uninplemented unicast solicitation.\n"));
+    }
+}
+
+PNEIGHBOR_CACHE_ENTRY NBAddNeighbor(
+  PIP_INTERFACE Interface,
+  PIP_ADDRESS Address,
+  PVOID LinkAddress,
+  UINT LinkAddressLength,
+  UCHAR State)
+/*
+ * FUNCTION: Adds a neighbor to the neighbor cache
+ * ARGUMENTS:
+ *   Interface         = Pointer to interface
+ *   Address           = Pointer to IP address
+ *   LinkAddress       = Pointer to link address (may be NULL)
+ *   LinkAddressLength = Length of link address
+ *   State             = State of NCE
+ * RETURNS:
+ *   Pointer to NCE, NULL there is not enough free resources
+ * NOTES:
+ *   The NCE if referenced for the caller if created. The NCE retains
+ *   a reference to the IP address if it is created, the caller is
+ *   responsible for providing this reference
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NCE;
+  ULONG HashValue;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X)  Address (0x%X)  "
+    "LinkAddress (0x%X)  LinkAddressLength (%d)  State (0x%X)\n",
+    Interface, Address, LinkAddress, LinkAddressLength, State));
+
+  ASSERT(Address->Type == IP_ADDRESS_V4);
+
+  NCE = ExAllocatePool(NonPagedPool, sizeof(NEIGHBOR_CACHE_ENTRY) + LinkAddressLength);
+  if (NCE == NULL)
+    {
+      TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      return NULL;
+    }
+
+  INIT_TAG(NCE, TAG('N','C','E',' '));
+
+  /* Initialize NCE free routine */
+  NCE->Free = FreeNCE;
+
+  /* Reference once for beeing alive and once for the caller */
+  NCE->RefCount = 2;
+  NCE->Interface = Interface;
+  NCE->Address = *Address;
+  NCE->LinkAddressLength = LinkAddressLength;
+  NCE->LinkAddress = (PVOID)&NCE[1];
+  if (LinkAddress != NULL)
+    {
+      RtlCopyMemory(NCE->LinkAddress, LinkAddress, LinkAddressLength);
+    }
+  NCE->State = State;
+  NCE->EventTimer = 0; /* Not in use */
+  NCE->WaitQueue = NULL;
+
+  HashValue = *(PULONG)&Address->Address;
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  NCE->Table = &NeighborCache[HashValue];
+
+  KeAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  NCE->Next = NeighborCache[HashValue].Cache;
+  NeighborCache[HashValue].Cache = NCE;
+
+  KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+  return NCE;
+}
+
+VOID NBUpdateNeighbor(
+  PNEIGHBOR_CACHE_ENTRY NCE,
+  PVOID LinkAddress,
+  UCHAR State)
+/*
+ * FUNCTION: Update link address information in NCE
+ * ARGUMENTS:
+ *   NCE         = Pointer to NCE to update
+ *   LinkAddress = Pointer to link address
+ *   State       = State of NCE
+ * NOTES:
+ *   The link address and state is updated. Any waiting packets are sent
+ */
+{
+  PNDIS_PACKET Current;
+  PNDIS_PACKET Next;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X)  LinkAddress (0x%X)  State (0x%X).\n", NCE, LinkAddress, State));
+
+  KeAcquireSpinLock(&NCE->Table->Lock, &OldIrql);
+
+  RtlCopyMemory(NCE->LinkAddress, LinkAddress, NCE->LinkAddressLength);
+  NCE->State = State;
+  Current = NCE->WaitQueue;
+  NCE->WaitQueue = NULL;
+
+  KeReleaseSpinLock(&NCE->Table->Lock, OldIrql);
+
+  /* Send any waiting packets */
+  while (Current != NULL)
+    {
+      /* Our link to the next packet is broken by the
+         datalink layer code so we must save it here */
+      Next = (PNDIS_PACKET)PC(Current)->DLComplete;
+      IPSendFragment(Current, NCE);
+      Current = Next;
+    }
+}
+
+PNEIGHBOR_CACHE_ENTRY NBLocateNeighbor(
+  PIP_ADDRESS Address)
+/*
+ * FUNCTION: Locates a neighbor in the neighbor cache
+ * ARGUMENTS:
+ *   Address = Pointer to IP address
+ * RETURNS:
+ *   Pointer to NCE, NULL if not found
+ * NOTES:
+ *   If the NCE is found, it is referenced. The caller is
+ *   responsible for dereferencing it again after use
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NCE;
+  UINT HashValue;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. Address (0x%X).\n", Address));
+
+  HashValue = *(PULONG)&Address->Address;
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  KeAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  NCE = NeighborCache[HashValue].Cache;
+
+  while ((NCE) && (!AddrIsEqual(Address, &NCE->Address)))
+    {
+      NCE = NCE->Next;
+    }
+
+  if (NCE)
+    {
+      ReferenceObject(NCE);
+    }
+
+  KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+  return NCE;
+}
+
+PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor(
+  PIP_INTERFACE Interface,
+  PIP_ADDRESS Address)
+/*
+ * FUNCTION: Tries to find a neighbor and if unsuccesful, creates a new NCE
+ * ARGUMENTS:
+ *   Interface = Pointer to interface to use (in case NCE is not found)
+ *   Address   = Pointer to IP address
+ * RETURNS:
+ *   Pointer to NCE, NULL if there is not enough free resources
+ * NOTES:
+ *   The NCE is referenced if found or created. The caller is
+ *   responsible for dereferencing it again after use
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY NCE;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. Interface (0x%X)  Address (0x%X).\n", Interface, Address));
+
+  NCE = NBLocateNeighbor(Address);
+  if (NCE == NULL)
+    {
+      ReferenceObject(Address);
+      NCE = NBAddNeighbor(Interface, Address, NULL, 
+                         Interface->AddressLength, NUD_INCOMPLETE);
+      NCE->EventTimer = 1;
+      NCE->EventCount = 0;
+    }
+
+  return NCE;
+}
+
+BOOLEAN NBQueuePacket(
+  PNEIGHBOR_CACHE_ENTRY NCE,
+  PNDIS_PACKET NdisPacket)
+/*
+ * FUNCTION: Queues a packet on an NCE for later transmission
+ * ARGUMENTS:
+ *   NCE        = Pointer to NCE to queue packet on
+ *   NdisPacket = Pointer to NDIS packet to queue
+ * RETURNS:
+ *   TRUE if the packet was successfully queued, FALSE if not
+ */
+{
+  PKSPIN_LOCK Lock;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X)  NdisPacket (0x%X).\n", NCE, NdisPacket));
+
+  /* FIXME: Should we limit the number of queued packets? */
+
+  Lock = &NCE->Table->Lock;
+
+  KeAcquireSpinLock(Lock, &OldIrql);
+
+  /* Use data link level completion handler pointer to link
+     queued packets together */
+  PC(NdisPacket)->DLComplete = (PACKET_COMPLETION_ROUTINE)NCE->WaitQueue;
+  NCE->WaitQueue = NdisPacket;
+
+  KeReleaseSpinLock(Lock, OldIrql);
+
+  return TRUE;
+}
+
+
+VOID NBRemoveNeighbor(
+  PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Removes a neighbor from the neighbor cache
+ * ARGUMENTS:
+ *   NCE = Pointer to NCE to remove from cache
+ * NOTES:
+ *   The NCE must be in a safe state
+ */
+{
+  PNEIGHBOR_CACHE_ENTRY *PrevNCE;
+  PNEIGHBOR_CACHE_ENTRY CurNCE;
+  PNDIS_PACKET NextNdisPacket;
+  PNDIS_PACKET NdisPacket;
+  ULONG HashValue;
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_NCACHE, ("Called. NCE (0x%X).\n", NCE));
+
+  HashValue  = *(PULONG)(&NCE->Address.Address);
+  HashValue ^= HashValue >> 16;
+  HashValue ^= HashValue >> 8;
+  HashValue ^= HashValue >> 4;
+  HashValue &= NB_HASHMASK;
+
+  KeAcquireSpinLock(&NeighborCache[HashValue].Lock, &OldIrql);
+
+  /* Search the list and remove the NCE from the list if found */
+  for (PrevNCE = &NeighborCache[HashValue].Cache;
+    (CurNCE = *PrevNCE) != NULL;
+    PrevNCE = &CurNCE->Next)
+    {
+      if (CurNCE == NCE)
+        {
+          /* Found it, now unlink it from the list */
+          *PrevNCE = CurNCE->Next;
+
+          /* Purge wait queue */
+          NdisPacket = CurNCE->WaitQueue;
+          while (NdisPacket != NULL)
+            {
+              NextNdisPacket = (PNDIS_PACKET)PC(NdisPacket)->DLComplete;
+              FreeNdisPacket(NdisPacket);
+              NdisPacket = NextNdisPacket;
+            }
+
+          /* Remove all references from route cache */
+          RouteInvalidateNCE(CurNCE);
+
+#ifdef DBG
+          CurNCE->RefCount--;
+
+          if (CurNCE->RefCount != 0)
+            {
+              TI_DbgPrint(DEBUG_REFCOUNT, ("NCE at (0x%X) has (%d) references (should be 0).\n",
+                CurNCE, CurNCE->RefCount));
+            }
+#endif
+          ExFreePool(CurNCE);
+
+          KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+
+          return;
+        }
+    }
+
+  KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql);
+}
diff --git a/reactos/drivers/lib/ip/network/prefix.c b/reactos/drivers/lib/ip/network/prefix.c
new file mode 100644 (file)
index 0000000..c409c20
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/ip.c
+ * PURPOSE:     Internet Protocol module
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *              Art Yerkes (arty@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+LIST_ENTRY PrefixListHead;
+KSPIN_LOCK PrefixListLock;
+
+/* --------- The Prefix List ---------- */
+
+VOID InitPLE() {
+    /* Initialize the prefix list and protecting lock */
+    InitializeListHead(&PrefixListHead);
+    KeInitializeSpinLock(&PrefixListLock);
+}
+
+
+PPREFIX_LIST_ENTRY CreatePLE(PIP_INTERFACE IF, PIP_ADDRESS Prefix, UINT Length)
+/*
+ * FUNCTION: Creates a prefix list entry and binds it to an interface
+ * ARGUMENTS:
+ *     IF     = Pointer to interface
+ *     Prefix = Pointer to prefix
+ *     Length = Length of prefix
+ * RETURNS:
+ *     Pointer to PLE, NULL if there was not enough free resources
+ * NOTES:
+ *     The prefix list entry retains a reference to the interface and
+ *     the provided address.  The caller is responsible for providing
+ *     these references
+ */
+{
+    PPREFIX_LIST_ENTRY PLE;
+
+    TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X)  Prefix (0x%X)  Length (%d).\n", IF, Prefix, Length));
+
+    TI_DbgPrint(DEBUG_IP, ("Prefix (%s).\n", A2S(Prefix)));
+
+    /* Allocate space for an PLE and set it up */
+    PLE = ExAllocatePool(NonPagedPool, sizeof(PREFIX_LIST_ENTRY));
+    if (!PLE) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+    INIT_TAG(PLE, TAG('P','L','E',' '));
+    PLE->RefCount     = 1;
+    PLE->Interface    = IF;
+    PLE->Prefix       = Prefix;
+    PLE->PrefixLength = Length;
+
+    /* Add PLE to the global prefix list */
+    ExInterlockedInsertTailList(&PrefixListHead, &PLE->ListEntry, &PrefixListLock);
+
+    return PLE;
+}
+
+
+VOID DestroyPLE(
+    PPREFIX_LIST_ENTRY PLE)
+/*
+ * FUNCTION: Destroys an prefix list entry
+ * ARGUMENTS:
+ *     PLE = Pointer to prefix list entry
+ * NOTES:
+ *     The prefix list lock must be held when called
+ */
+{
+    TI_DbgPrint(DEBUG_IP, ("Called. PLE (0x%X).\n", PLE));
+
+    TI_DbgPrint(DEBUG_IP, ("PLE (%s).\n", PLE->Prefix));
+
+    /* Unlink the prefix list entry from the list */
+    RemoveEntryList(&PLE->ListEntry);
+
+    /* Dereference the address */
+    DereferenceObject(PLE->Prefix);
+
+    /* Dereference the interface */
+    DereferenceObject(PLE->Interface);
+
+#ifdef DBG
+    PLE->RefCount--;
+
+    if (PLE->RefCount != 0) {
+        TI_DbgPrint(MIN_TRACE, ("Prefix list entry at (0x%X) has (%d) references (should be 0).\n", PLE, PLE->RefCount));
+    }
+#endif
+
+    /* And free the PLE */
+    ExFreePool(PLE);
+}
+
+
+VOID DestroyPLEs(
+    VOID)
+/*
+ * FUNCTION: Destroys all prefix list entries
+ */
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+    PPREFIX_LIST_ENTRY Current;
+
+    TI_DbgPrint(DEBUG_IP, ("Called.\n"));
+
+    KeAcquireSpinLock(&PrefixListLock, &OldIrql);
+
+    /* Search the list and remove every PLE we find */
+    CurrentEntry = PrefixListHead.Flink;
+    while (CurrentEntry != &PrefixListHead) {
+        NextEntry = CurrentEntry->Flink;
+       Current = CONTAINING_RECORD(CurrentEntry, PREFIX_LIST_ENTRY, ListEntry);
+        /* Destroy the PLE */
+        DestroyPLE(Current);
+        CurrentEntry = NextEntry;
+    }
+    KeReleaseSpinLock(&PrefixListLock, OldIrql);
+}
+
diff --git a/reactos/drivers/lib/ip/network/receive.c b/reactos/drivers/lib/ip/network/receive.c
new file mode 100644 (file)
index 0000000..4c8c9f3
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/receive.c
+ * PURPOSE:     Internet Protocol receive routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * NOTES:       The IP datagram reassembly algorithm is taken from
+ *              from RFC 815
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+LIST_ENTRY ReassemblyListHead;
+KSPIN_LOCK ReassemblyListLock;
+NPAGED_LOOKASIDE_LIST IPDRList;
+NPAGED_LOOKASIDE_LIST IPFragmentList;
+NPAGED_LOOKASIDE_LIST IPHoleList;
+
+
+PIPDATAGRAM_HOLE CreateHoleDescriptor(
+  ULONG First,
+  ULONG Last)
+/*
+ * FUNCTION: Returns a pointer to a IP datagram hole descriptor
+ * ARGUMENTS:
+ *     First = Offset of first octet of the hole
+ *     Last  = Offset of last octet of the hole
+ * RETURNS:
+ *     Pointer to descriptor, NULL if there was not enough free
+ *     resources
+ */
+{
+       PIPDATAGRAM_HOLE Hole;
+
+       TI_DbgPrint(DEBUG_IP, ("Called. First (%d)  Last (%d).\n", First, Last));
+
+       Hole = ExAllocateFromNPagedLookasideList(&IPHoleList);
+       if (!Hole) {
+           TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+           return NULL;
+       }
+
+       Hole->First = First;
+       Hole->Last  = Last;
+
+       TI_DbgPrint(DEBUG_IP, ("Returning hole descriptor at (0x%X).\n", Hole));
+
+       return Hole;
+}
+
+
+VOID FreeIPDR(
+  PIPDATAGRAM_REASSEMBLY IPDR)
+/*
+ * FUNCTION: Frees an IP datagram reassembly structure
+ * ARGUMENTS:
+ *     IPDR = Pointer to IP datagram reassembly structure
+ */
+{
+  PLIST_ENTRY CurrentEntry;
+  PLIST_ENTRY NextEntry;
+  PIPDATAGRAM_HOLE CurrentH;
+  PIP_FRAGMENT CurrentF;
+
+  TI_DbgPrint(DEBUG_IP, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR));
+
+  /* Free all descriptors */
+  CurrentEntry = IPDR->HoleListHead.Flink;
+  while (CurrentEntry != &IPDR->HoleListHead) {
+    NextEntry = CurrentEntry->Flink;
+         CurrentH = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
+    /* Unlink it from the list */
+    RemoveEntryList(CurrentEntry);
+
+    TI_DbgPrint(DEBUG_IP, ("Freeing hole descriptor at (0x%X).\n", CurrentH));
+
+    /* And free the hole descriptor */
+    ExFreeToNPagedLookasideList(&IPHoleList, CurrentH);
+
+    CurrentEntry = NextEntry;
+  }
+
+  /* Free all fragments */
+  CurrentEntry = IPDR->FragmentListHead.Flink;
+  while (CurrentEntry != &IPDR->FragmentListHead) {
+    NextEntry = CurrentEntry->Flink;
+         CurrentF = CONTAINING_RECORD(CurrentEntry, IP_FRAGMENT, ListEntry);
+    /* Unlink it from the list */
+    RemoveEntryList(CurrentEntry);
+
+    TI_DbgPrint(DEBUG_IP, ("Freeing fragment data at (0x%X).\n", CurrentF->Data));
+
+    /* Free the fragment data buffer */
+    exFreePool(CurrentF->Data);
+
+    TI_DbgPrint(DEBUG_IP, ("Freeing fragment at (0x%X).\n", CurrentF));
+
+    /* And free the fragment descriptor */
+    ExFreeToNPagedLookasideList(&IPFragmentList, CurrentF);
+    CurrentEntry = NextEntry;
+  }
+
+  /* Free resources for the header, if it exists */
+  if (IPDR->IPv4Header) {
+    TI_DbgPrint(DEBUG_IP, ("Freeing IPv4 header data at (0x%X).\n", IPDR->IPv4Header));
+    exFreePool(IPDR->IPv4Header);
+  }
+
+  TI_DbgPrint(DEBUG_IP, ("Freeing IPDR data at (0x%X).\n", IPDR));
+
+  ExFreeToNPagedLookasideList(&IPDRList, IPDR);
+}
+
+
+VOID RemoveIPDR(
+  PIPDATAGRAM_REASSEMBLY IPDR)
+/*
+ * FUNCTION: Removes an IP datagram reassembly structure from the global list
+ * ARGUMENTS:
+ *     IPDR = Pointer to IP datagram reassembly structure
+ */
+{
+  KIRQL OldIrql;
+
+  TI_DbgPrint(DEBUG_IP, ("Removing IPDR at (0x%X).\n", IPDR));
+
+  KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
+  RemoveEntryList(&IPDR->ListEntry);
+  KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+}
+
+
+PIPDATAGRAM_REASSEMBLY GetReassemblyInfo(
+  PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Returns a pointer to an IP datagram reassembly structure
+ * ARGUMENTS:
+ *     IPPacket = Pointer to IP packet
+ * NOTES:
+ *     A datagram is identified by four paramters, which are
+ *     Source and destination address, protocol number and
+ *     identification number
+ */
+{
+  KIRQL OldIrql;
+  PLIST_ENTRY CurrentEntry;
+  PIPDATAGRAM_REASSEMBLY Current;
+  PIPv4_HEADER Header = (PIPv4_HEADER)IPPacket->Header;
+
+  TI_DbgPrint(DEBUG_IP, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket));
+
+  KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
+
+  /* FIXME: Assume IPv4 */
+
+  CurrentEntry = ReassemblyListHead.Flink;
+  while (CurrentEntry != &ReassemblyListHead) {
+         Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
+    if (AddrIsEqual(&IPPacket->SrcAddr, &Current->SrcAddr) &&
+      (Header->Id == Current->Id) &&
+      (Header->Protocol == Current->Protocol) &&
+      (AddrIsEqual(&IPPacket->DstAddr, &Current->DstAddr))) {
+      KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+
+      return Current;
+    }
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+
+  return NULL;
+}
+
+
+PIP_PACKET ReassembleDatagram(
+  PIPDATAGRAM_REASSEMBLY IPDR)
+/*
+ * FUNCTION: Reassembles an IP datagram
+ * ARGUMENTS:
+ *     IPDR = Pointer to IP datagram reassembly structure
+ * NOTES:
+ *     This routine concatenates fragments into a complete IP datagram.
+ *     The lock is held when this routine is called
+ * RETURNS:
+ *     Pointer to IP packet, NULL if there was not enough free resources
+ * NOTES:
+ *     At this point, header is expected to point to the IP header
+ */
+{
+  PIP_PACKET IPPacket;
+  PLIST_ENTRY CurrentEntry;
+  PIP_FRAGMENT Current;
+  PVOID Data;
+
+  TI_DbgPrint(DEBUG_IP, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR));
+  TI_DbgPrint(DEBUG_IP, ("IPDR->HeaderSize = %d\n", IPDR->HeaderSize));
+  TI_DbgPrint(DEBUG_IP, ("IPDR->DataSize = %d\n", IPDR->DataSize));
+
+  TI_DbgPrint(DEBUG_IP, ("Fragment header:\n"));
+  //OskitDumpBuffer((PCHAR)IPDR->IPv4Header, IPDR->HeaderSize);
+
+  /* FIXME: Assume IPv4 */
+  IPPacket = IPCreatePacket(IP_ADDRESS_V4);
+  if (!IPPacket)
+    return NULL;
+
+  IPPacket->TotalSize  = IPDR->HeaderSize + IPDR->DataSize;
+  IPPacket->ContigSize = IPPacket->TotalSize;
+  IPPacket->HeaderSize = IPDR->HeaderSize;
+  /*IPPacket->Position   = IPDR->HeaderSize;*/
+
+  RtlCopyMemory(&IPPacket->SrcAddr, &IPDR->SrcAddr, sizeof(IP_ADDRESS));
+  RtlCopyMemory(&IPPacket->DstAddr, &IPDR->DstAddr, sizeof(IP_ADDRESS));
+
+  /* Allocate space for full IP datagram */
+  IPPacket->Header = exAllocatePool(NonPagedPool, IPPacket->TotalSize);
+  if (!IPPacket->Header) {
+    TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+    (*IPPacket->Free)(IPPacket);
+    return NULL;
+  }
+
+  /* Copy the header into the buffer */
+  RtlCopyMemory(IPPacket->Header, IPDR->IPv4Header, IPDR->HeaderSize);  
+  
+  Data = IPPacket->Header + IPDR->HeaderSize;
+  IPPacket->Data = Data;
+
+  /* Copy data from all fragments into buffer */
+  CurrentEntry = IPDR->FragmentListHead.Flink;
+  while (CurrentEntry != &IPDR->FragmentListHead) {
+    Current = CONTAINING_RECORD(CurrentEntry, IP_FRAGMENT, ListEntry);
+
+    TI_DbgPrint(DEBUG_IP, ("Copying (%d) bytes of fragment data from (0x%X) to offset (%d).\n",
+      Current->Size, Data, Current->Offset));
+    /* Copy fragment data to the destination buffer at the correct offset */
+    RtlCopyMemory((PVOID)((ULONG_PTR)Data + Current->Offset),
+                 Current->Data,
+                 Current->Size);
+    //OskitDumpBuffer( Data, Current->Offset + Current->Size );
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  return IPPacket;
+}
+
+
+__inline VOID Cleanup(
+  PKSPIN_LOCK Lock,
+  KIRQL OldIrql,
+  PIPDATAGRAM_REASSEMBLY IPDR,
+  PVOID Buffer OPTIONAL)
+/*
+ * FUNCTION: Performs cleaning operations on errors
+ * ARGUMENTS:
+ *     Lock     = Pointer to spin lock to be released
+ *     OldIrql  = Value of IRQL when spin lock was acquired
+ *     IPDR     = Pointer to IP datagram reassembly structure to free
+ *     Buffer   = Optional pointer to a buffer to free
+ */
+{
+  TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+
+  KeReleaseSpinLock(Lock, OldIrql);
+  RemoveIPDR(IPDR);
+  FreeIPDR(IPDR);
+  if (Buffer)
+    exFreePool(Buffer);
+}
+
+
+VOID ProcessFragment(
+  PIP_INTERFACE IF,
+  PIP_PACKET IPPacket,
+  PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Processes an IP datagram or fragment
+ * ARGUMENTS:
+ *     IF       = Pointer to IP interface packet was receive on
+ *     IPPacket = Pointer to IP packet
+ *     NTE      = Pointer to NTE packet was received on
+ * NOTES:
+ *     This routine reassembles fragments and, if a whole datagram can
+ *     be assembled, passes the datagram on to the IP protocol dispatcher
+ */
+{
+  KIRQL OldIrql;
+  PIPDATAGRAM_REASSEMBLY IPDR;
+  PLIST_ENTRY CurrentEntry;
+  PIPDATAGRAM_HOLE Hole, NewHole;
+  USHORT FragFirst;
+  USHORT FragLast;
+  BOOLEAN MoreFragments;
+  PIPv4_HEADER IPv4Header;
+  PIP_PACKET Datagram;
+  PIP_FRAGMENT Fragment;
+
+  /* FIXME: Assume IPv4 */
+
+  IPv4Header = (PIPv4_HEADER)IPPacket->Header;
+
+  /* Check if we already have an reassembly structure for this datagram */
+  IPDR = GetReassemblyInfo(IPPacket);
+  if (IPDR) {
+    TI_DbgPrint(DEBUG_IP, ("Continueing assembly.\n"));
+    /* We have a reassembly structure */
+    KeAcquireSpinLock(&IPDR->Lock, &OldIrql);
+    CurrentEntry = IPDR->HoleListHead.Flink;
+    Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
+  } else {
+    TI_DbgPrint(DEBUG_IP, ("Starting new assembly.\n"));
+
+    /* We don't have a reassembly structure, create one */
+    IPDR = ExAllocateFromNPagedLookasideList(&IPDRList);
+    if (!IPDR)
+      /* We don't have the resources to process this packet, discard it */
+      return;
+
+    /* Create a descriptor spanning from zero to infinity.
+       Actually, we use a value slightly greater than the
+       maximum number of octets an IP datagram can contain */
+    Hole = CreateHoleDescriptor(0, 65536);
+    if (!Hole) {
+      /* We don't have the resources to process this packet, discard it */
+      ExFreeToNPagedLookasideList(&IPDRList, IPDR);
+      return;
+    }
+    AddrInitIPv4(&IPDR->SrcAddr, IPv4Header->SrcAddr);
+    AddrInitIPv4(&IPDR->DstAddr, IPv4Header->DstAddr);
+    IPDR->Id         = IPv4Header->Id;
+    IPDR->Protocol   = IPv4Header->Protocol;
+    IPDR->IPv4Header = NULL;
+    InitializeListHead(&IPDR->FragmentListHead);
+    InitializeListHead(&IPDR->HoleListHead);
+    InsertTailList(&IPDR->HoleListHead, &Hole->ListEntry);
+    CurrentEntry = IPDR->HoleListHead.Flink;
+
+    KeInitializeSpinLock(&IPDR->Lock);
+
+    KeAcquireSpinLock(&IPDR->Lock, &OldIrql);
+
+    /* Update the reassembly list */
+    ExInterlockedInsertTailList(
+                 &ReassemblyListHead,
+      &IPDR->ListEntry,
+      &ReassemblyListLock);
+  }
+
+  FragFirst     = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_FRAGOFS_MASK) << 3;
+  FragLast      = FragFirst + WN2H(IPv4Header->TotalLength);
+  MoreFragments = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_MF_MASK) > 0;
+
+  for (;;) {
+    if (CurrentEntry == &IPDR->HoleListHead)
+      /* No more entries */
+      break;
+
+    TI_DbgPrint(DEBUG_IP, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
+      FragFirst, FragLast, Hole->First, Hole->Last));
+
+    if ((FragFirst > Hole->Last) || (FragLast < Hole->First)) {
+      TI_DbgPrint(MID_TRACE, ("No overlap.\n"));
+      /* The fragment does not overlap with the hole, try next
+         descriptor in the list */
+
+      CurrentEntry = CurrentEntry->Flink;
+      if (CurrentEntry != &IPDR->HoleListHead)
+          Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
+      continue;
+    }
+
+    /* The fragment overlap with the hole, unlink the descriptor */
+    RemoveEntryList(CurrentEntry);
+
+    if (FragFirst > Hole->First) {
+      NewHole = CreateHoleDescriptor(Hole->First, FragLast - 1);
+      if (!NewHole) {
+        /* We don't have the resources to process this packet, discard it */
+        Cleanup(&IPDR->Lock, OldIrql, IPDR, Hole);
+        return;
+      }
+
+      /* Put the new descriptor in the list */
+      InsertTailList(&IPDR->HoleListHead, &NewHole->ListEntry);
+    }
+
+    if ((FragLast < Hole->Last) && (MoreFragments)) {
+      /* We can reuse the descriptor for the new hole */
+                 Hole->First = FragLast + 1;
+
+                 /* Put the new hole descriptor in the list */
+      InsertTailList(&IPDR->HoleListHead, &Hole->ListEntry);
+    } else
+      ExFreeToNPagedLookasideList(&IPHoleList, Hole);
+
+    /* If this is the first fragment, save the IP header */
+    if (FragFirst == 0) {
+       IPDR->IPv4Header = exAllocatePool(NonPagedPool, IPPacket->HeaderSize);
+      if (!IPDR->IPv4Header) {
+        /* We don't have the resources to process this packet, discard it */
+        Cleanup(&IPDR->Lock, OldIrql, IPDR, NULL);
+        return;
+      }
+
+      TI_DbgPrint(DEBUG_IP, ("First fragment found. Header buffer is at (0x%X). "
+        "Header size is (%d).\n", IPDR->IPv4Header, IPPacket->HeaderSize));
+
+      RtlCopyMemory(IPDR->IPv4Header, IPPacket->Header, IPPacket->HeaderSize);
+      IPDR->HeaderSize = IPPacket->HeaderSize;
+    }
+
+    /* Create a buffer, copy the data into it and put it
+       in the fragment list */
+
+    Fragment = ExAllocateFromNPagedLookasideList(&IPFragmentList);
+    if (!Fragment) {
+      /* We don't have the resources to process this packet, discard it */
+      Cleanup(&IPDR->Lock, OldIrql, IPDR, NULL);
+      return;
+    }
+
+    TI_DbgPrint(DEBUG_IP, ("Fragment descriptor allocated at (0x%X).\n", Fragment));
+
+    Fragment->Size = IPPacket->TotalSize - IPPacket->HeaderSize;
+    Fragment->Data = exAllocatePool(NonPagedPool, Fragment->Size);
+    if (!Fragment->Data) {
+      /* We don't have the resources to process this packet, discard it */
+      Cleanup(&IPDR->Lock, OldIrql, IPDR, Fragment);
+      return;
+    }
+
+    /* Position here is an offset from the NdisPacket start, not the header */
+    TI_DbgPrint(DEBUG_IP, ("Fragment data buffer allocated at (0x%X)  Size (%d) Pos (%d).\n",
+                          Fragment->Data, Fragment->Size, IPPacket->Position));
+    
+    /* Copy datagram data into fragment buffer */
+    CopyPacketToBuffer(Fragment->Data,
+                      IPPacket->NdisPacket,
+                      IPPacket->Position,
+                      Fragment->Size);
+    Fragment->Offset = FragFirst;
+    
+    /* If this is the last fragment, compute and save the datagram data size */
+    if (!MoreFragments)
+      IPDR->DataSize = FragFirst + Fragment->Size;
+
+    /* Put the fragment in the list */
+    InsertTailList(&IPDR->FragmentListHead, &Fragment->ListEntry);
+         break;
+  }
+
+  TI_DbgPrint(DEBUG_IP, ("Done searching for hole descriptor.\n"));
+
+  if (IsListEmpty(&IPDR->HoleListHead)) {
+    /* Hole list is empty which means a complete datagram can be assembled.
+       Assemble the datagram and pass it to an upper layer protocol */
+
+    TI_DbgPrint(DEBUG_IP, ("Complete datagram received.\n"));
+
+    Datagram = ReassembleDatagram(IPDR);
+
+    KeReleaseSpinLock(&IPDR->Lock, OldIrql);
+
+    RemoveIPDR(IPDR);
+    FreeIPDR(IPDR);
+
+    if (!Datagram)
+      /* Not enough free resources, discard the packet */
+      return;
+
+    DISPLAY_IP_PACKET(Datagram);
+
+    /* Give the packet to the protocol dispatcher */
+    IPDispatchProtocol(NTE, Datagram);
+
+    /* We're done with this datagram */
+    exFreePool(Datagram->Header);
+    TI_DbgPrint(MAX_TRACE, ("Freeing datagram at (0x%X).\n", Datagram));
+    (*Datagram->Free)(Datagram);
+  } else
+    KeReleaseSpinLock(&IPDR->Lock, OldIrql);
+}
+
+
+VOID IPFreeReassemblyList(
+  VOID)
+/*
+ * FUNCTION: Frees all IP datagram reassembly structures in the list
+ */
+{
+  KIRQL OldIrql;
+  PLIST_ENTRY CurrentEntry;
+  PIPDATAGRAM_REASSEMBLY Current;
+
+  KeAcquireSpinLock(&ReassemblyListLock, &OldIrql);
+
+  CurrentEntry = ReassemblyListHead.Flink;
+  while (CurrentEntry != &ReassemblyListHead) {
+         Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
+    /* Unlink it from the list */
+    RemoveEntryList(CurrentEntry);
+
+    /* And free the descriptor */
+    FreeIPDR(Current);
+
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  KeReleaseSpinLock(&ReassemblyListLock, OldIrql);
+}
+
+
+VOID IPDatagramReassemblyTimeout(
+  VOID)
+/*
+ * FUNCTION: IP datagram reassembly timeout handler
+ * NOTES:
+ *     This routine is called by IPTimeout to free any resources used
+ *     to hold IP fragments that have taken too long to reassemble
+ */
+{
+}
+
+VOID IPv4Receive(PIP_INTERFACE IF, PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Receives an IPv4 datagram (or fragment)
+ * ARGUMENTS:
+ *     Context  = Pointer to context information (IP_INTERFACE)
+ *     IPPacket = Pointer to IP packet
+ */
+{
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    PNET_TABLE_ENTRY NTE;
+    UINT AddressType;
+    
+    TI_DbgPrint(DEBUG_IP, ("Received IPv4 datagram.\n"));
+    
+    IPPacket->HeaderSize = (((PIPv4_HEADER)IPPacket->Header)->VerIHL & 0x0F) << 2;
+    TI_DbgPrint(DEBUG_IP, ("IPPacket->HeaderSize = %d\n", IPPacket->HeaderSize));
+
+    if (IPPacket->HeaderSize > IPv4_MAX_HEADER_SIZE) {
+       TI_DbgPrint
+           (MIN_TRACE, 
+            ("Datagram received with incorrect header size (%d).\n",
+             IPPacket->HeaderSize));
+       /* Discard packet */
+       return;
+    }
+    
+    /* Checksum IPv4 header */
+    if (!IPv4CorrectChecksum(IPPacket->Header, IPPacket->HeaderSize)) {
+       TI_DbgPrint
+           (MIN_TRACE, 
+            ("Datagram received with bad checksum. Checksum field (0x%X)\n",
+             WN2H(((PIPv4_HEADER)IPPacket->Header)->Checksum)));
+       /* Discard packet */
+       return;
+    }
+    
+    IPPacket->TotalSize = WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength);
+    
+    AddrInitIPv4(&IPPacket->SrcAddr, ((PIPv4_HEADER)IPPacket->Header)->SrcAddr);
+    AddrInitIPv4(&IPPacket->DstAddr, ((PIPv4_HEADER)IPPacket->Header)->DstAddr);
+    
+    IPPacket->Position += IPPacket->HeaderSize;
+    IPPacket->Data     = (PVOID)((ULONG_PTR)IPPacket->Header + IPPacket->HeaderSize);
+    
+    TI_DbgPrint(MID_TRACE,("IPPacket->Position = %d\n",
+                          IPPacket->Position));
+
+    //OskitDumpBuffer(IPPacket->Header, IPPacket->TotalSize);
+
+    /* FIXME: Possibly forward packets with multicast addresses */
+    
+    /* FIXME: Should we allow packets to be received on the wrong interface? */
+    NTE = IPLocateNTEOnInterface(IF, &IPPacket->DstAddr, &AddressType);
+    
+    if (NTE) {
+       /* This packet is destined for us */
+       ProcessFragment(IF, IPPacket, NTE);
+       
+       /* Done with this NTE */
+       DereferenceObject(NTE);
+    } else {
+       /* This packet is not destined for us. If we are a router,
+          try to find a route and forward the packet */
+       
+       /* FIXME: Check if acting as a router */
+       NCE = NULL;
+       if (NCE) {
+           /* FIXME: Possibly fragment datagram */
+           /* Forward the packet */
+           IPSendFragment(IPPacket->NdisPacket, NCE);
+       } else {
+           TI_DbgPrint(MIN_TRACE, ("No route to destination (0x%X).\n",
+                                   IPPacket->DstAddr.Address.IPv4Address));
+           
+           /* FIXME: Send ICMP error code */
+       }
+    }
+}
+
+
+VOID IPReceive( PIP_INTERFACE IF, PIP_PACKET IPPacket )
+/*
+ * FUNCTION: Receives an IP datagram (or fragment)
+ * ARGUMENTS:
+ *     IF       = Interface
+ *     IPPacket = Pointer to IP packet
+ */
+{
+  UINT Version;
+
+  /* Check that IP header has a supported version */
+  Version = (((PIPv4_HEADER)IPPacket->Header)->VerIHL >> 4);
+
+  switch (Version) {
+  case 4:
+    IPPacket->Type = IP_ADDRESS_V4;
+    IPv4Receive(IF, IPPacket);
+    break;
+  case 6:
+    IPPacket->Type = IP_ADDRESS_V6;
+    TI_DbgPrint(MAX_TRACE, ("Datagram of type IPv6 discarded.\n"));
+    return;
+  default:
+         TI_DbgPrint(MIN_TRACE, ("Datagram has an unsupported IP version %d.\n", Version));
+    return;
+  }
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/route.c b/reactos/drivers/lib/ip/network/route.c
new file mode 100644 (file)
index 0000000..5a5829d
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/route.c
+ * PURPOSE:     Route cache
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * NOTES:       The route cache is implemented as a binary search
+ *              tree to obtain fast searches
+ *
+ *   This data is not authoritative.  It is a searchable cache that allows
+ *   quick access to route information to selected hosts.  This information
+ *   should always defer to the FIB.
+ *
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+/* This RCN is shared by all external nodes. It complicates things,
+   but the memory requirements are reduced by approximately 50%.
+   The RCN is protected by the route cache spin lock */
+PROUTE_CACHE_NODE ExternalRCN;
+PROUTE_CACHE_NODE RouteCache;
+KSPIN_LOCK RouteCacheLock;
+NPAGED_LOOKASIDE_LIST IPRCNList;
+
+
+#ifdef DBG
+VOID PrintTree(
+    PROUTE_CACHE_NODE Node)
+/*
+ * FUNCTION: Prints all nodes on tree
+ * ARGUMENTS:
+ *     Node = Pointer to root node of tree
+ * NOTES:
+ *     This function must be called with the route cache lock held.
+ */
+{
+    if (IsInternalRCN(Node)) {
+        /* Traverse left subtree */
+        PrintTree(Node->Left);
+
+        /* Traverse right subtree */
+        PrintTree(Node->Right);
+
+        /* Finally check the node itself */
+        TI_DbgPrint(MIN_TRACE, ("(Internal) Self,Parent,Left,Right,Data = (%08X, %08X, %08X, %08X, %08X).\n",
+            Node, Node->Parent, Node->Left, Node->Right, (ULONG_PTR)Node->Destination.Address.IPv4Address));
+    } else
+        TI_DbgPrint(MIN_TRACE, ("(External) Self,Parent,Left,Right = (%08X, %08X, %08X, %08X).\n",
+            Node, Node->Parent, Node->Left, Node->Right));
+}
+#endif
+
+UINT CountRouteNodes( PROUTE_CACHE_NODE Node ) {
+    if( !Node ) Node = RouteCache;
+    if( IsInternalRCN(Node) )
+        return 
+           /* Traverse left subtree */
+           CountRouteNodes(Node->Left) + 
+           /* Traverse right subtree */
+           CountRouteNodes(Node->Right) + 1;
+    else
+       return 0;
+}
+
+VOID FreeRCN(
+    PVOID Object)
+/*
+ * FUNCTION: Frees an route cache node object
+ * ARGUMENTS:
+ *     Object = Pointer to an route cache node structure
+ */
+{
+  ExFreeToNPagedLookasideList(&IPRCNList, Object);
+}
+
+
+VOID RemoveAboveExternal(VOID)
+/*
+ * FUNCTION: Removes the parent node of the selected external node from the route cache tree
+ * NOTES:
+ *     This function must be called with the route cache lock held.
+ *     ExternalRCN->Parent must be initialized
+ */
+{
+    PROUTE_CACHE_NODE Parent;
+    PROUTE_CACHE_NODE Sibling;
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
+
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree (before).\n"));
+    PrintTree(RouteCache);
+#endif
+
+    Parent = ExternalRCN->Parent;
+    /* Find sibling of external node */
+    if (ExternalRCN == Parent->Left)
+        Sibling = Parent->Right;
+    else
+        Sibling = Parent->Left;
+
+    /* Replace parent node with sibling of external node */
+    if (Parent != RouteCache) {
+        if (Parent->Parent->Left == Parent)
+            Parent->Parent->Left = Sibling;
+        else
+            Parent->Parent->Right = Sibling;
+        /* Give sibling a new parent */
+        Sibling->Parent = Parent->Parent;
+    } else {
+        /* This is the root we're removing */
+        RouteCache      = Sibling;
+        Sibling->Parent = NULL;
+    }
+
+    DereferenceObject(Parent);
+
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree (after).\n"));
+    PrintTree(RouteCache);
+#endif
+}
+
+
+PROUTE_CACHE_NODE SearchRouteCache(
+    PIP_ADDRESS Destination,
+    PROUTE_CACHE_NODE Node)
+/*
+ * FUNCTION: Searches route cache for a RCN for a destination address
+ * ARGUMENTS:
+ *     Destination = Pointer to destination address (key)
+ *     Node        = Pointer to start route cache node
+ * NOTES:
+ *     This function must be called with the route cache lock held
+ * RETURNS:
+ *     Pointer to internal node if a matching node was found, or
+ *     external node where it should be if none was found
+ */
+{
+    INT Value;
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X)  Node (0x%X)\n", Destination, Node));
+
+    /* Is this an external node? */
+    if (IsExternalRCN(Node))
+        return Node;
+
+    /* Is it this node we are looking for? */
+    Value = AddrCompare(Destination, &Node->Destination);
+    if (Value == 0)
+        return Node;
+
+    /* Traverse down the left subtree if the key is smaller than
+       the key of the node, otherwise traverse the right subtree */
+    if (Value < 0) {
+        Node->Left->Parent = Node;
+        ExternalRCN->Left  = (PROUTE_CACHE_NODE)&Node->Left;
+        return SearchRouteCache(Destination, Node->Left);
+    } else {
+        Node->Right->Parent = Node;
+        ExternalRCN->Left   = (PROUTE_CACHE_NODE)&Node->Right;
+        return SearchRouteCache(Destination, Node->Right);
+    }
+}
+
+
+PROUTE_CACHE_NODE ExpandExternalRCN(VOID)
+/*
+ * FUNCTION: Expands an external route cache node
+ * NOTES:
+ *     This function must be called with the route cache lock held.
+ *     We cheat a little here to save memory. We don't actually allocate memory
+ *     for external nodes. We wait until they're turned into internal nodes.
+ *     ExternalRCN->Parent must be initialized
+ *     ExternalRCN->Left must be a pointer to the correct child link of it's parent
+ * RETURNS:
+ *     Pointer to new internal node if the external node was expanded, NULL if not
+ */
+{
+    PROUTE_CACHE_NODE RCN;
+
+    MTMARK();
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
+
+    RCN = ExAllocateFromNPagedLookasideList(&IPRCNList);
+    if (!RCN) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+    MTMARK();
+
+    RCN->Free = FreeRCN;
+
+    if (ExternalRCN->Left)
+        /* Register RCN as a child with it's parent */
+        *(PROUTE_CACHE_NODE*)ExternalRCN->Left = RCN;
+
+    RCN->Parent = ExternalRCN->Parent;
+    RCN->Left   = ExternalRCN;
+    RCN->Right  = ExternalRCN;
+
+    MTMARK();
+
+    return RCN;
+}
+
+#if 0
+VOID SwapRCN(
+    PROUTE_CACHE_NODE *Node1,
+    PROUTE_CACHE_NODE *Node2)
+/*
+ * FUNCTION: Swaps two nodes
+ * ARGUMENTS:
+ *     Node1 = Address of pointer to first node
+ *     Node2 = Address of pointer to second node
+ */
+{
+    PROUTE_CACHE_NODE Temp;
+
+    Temp  = *Node2;
+    *Node2 = *Node1;
+    *Node1 = Temp;
+}
+#endif
+
+/*
+ * FUNCTION: Removes a route to a destination
+ * ARGUMENTS:
+ *     RCN = Pointer to route cache node to remove
+ * NOTES:
+ *     Internal version. Route cache lock must be held
+ */
+VOID RemoveRouteToDestination(
+    PROUTE_CACHE_NODE RCN)
+{
+    PROUTE_CACHE_NODE RemNode, Parent, SwapNode;
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. RCN (0x%X).\n", RCN));
+
+    if (IsExternalRCN(RCN->Left)) {
+        /* Left node is external */
+        RemNode         = RCN->Left;
+        RemNode->Parent = RCN;
+    } else if (IsExternalRCN(RCN->Right)) {
+        /* Right node is external */
+        RemNode         = RCN->Right;
+        RemNode->Parent = RCN;
+    } else {
+        /* The node has internal children */
+
+        /* Normally we would replace the item of RCN with the item
+           of the leftmost external node on the right subtree of
+           RCN. This we cannot do here because there may be
+           references directly to that node. Instead we swap pointer
+           values (parent, left and right) of the two nodes */
+        RemNode = RCN->Right;
+        do {
+            Parent  = RemNode;
+            RemNode = RemNode->Left;
+        } while (IsInternalRCN(RemNode));
+        RemNode->Parent = Parent;
+
+        SwapNode = RemNode->Parent;
+#if 0
+        if (RCN != RouteCache) {
+            /* Set SwapNode to be child of RCN's parent instead of RCN */
+            Parent = RCN->Parent;
+            if (RCN == Parent->Left)
+                Parent->Left = SwapNode;
+            else
+                Parent->Right = SwapNode;
+        } else
+            /* SwapNode is the new cache root */
+            RouteCache = SwapNode;
+
+        /* Set RCN to be child of SwapNode's parent instead of SwapNode */
+        Parent = SwapNode->Parent;
+        if (SwapNode == Parent->Left)
+            Parent->Left = RCN;
+        else
+            Parent->Right = RCN;
+
+        /* Swap parents */
+        SwapRCN(&SwapNode->Parent, &RCN->Parent);
+        /* Swap children */
+        SwapRCN(&SwapNode->Left, &RCN->Left);
+        SwapRCN(&SwapNode->Right, &RCN->Right);
+#endif
+    }
+    
+    /* Dereference NTE and NCE */
+    DereferenceObject(RCN->NTE);
+    DereferenceObject(RCN->NCE);
+
+    ExternalRCN->Parent = RemNode->Parent;
+
+    RemoveAboveExternal();
+}
+
+
+VOID InvalidateNTEOnSubtree(
+    PNET_TABLE_ENTRY NTE,
+    PROUTE_CACHE_NODE Node)
+/*
+ * FUNCTION: Removes all RCNs with references to an NTE on a subtree
+ * ARGUMENNTS:
+ *     NTE  = Pointer to NTE to invalidate
+ *     Node = Pointer to RCN to start removing nodes at
+ * NOTES:
+ *     This function must be called with the route cache lock held.
+ */
+{
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. NTE (0x%X)  Node (0x%X).\n", NTE, Node));
+
+    if (IsInternalRCN(Node)) {
+        /* Traverse left subtree */
+        InvalidateNTEOnSubtree(NTE, Node->Left);
+
+        /* Traverse right subtree */
+        InvalidateNTEOnSubtree(NTE, Node->Right);
+
+        /* Finally check the node itself */
+        if (Node->NTE == NTE)
+            RemoveRouteToDestination(Node);
+    }
+}
+
+
+VOID InvalidateNCEOnSubtree(
+    PNEIGHBOR_CACHE_ENTRY NCE,
+    PROUTE_CACHE_NODE Node)
+/*
+ * FUNCTION: Removes all RCNs with references to an NCE on a subtree
+ * ARGUMENNTS:
+ *     NCE  = Pointer to NCE to invalidate
+ *     Node = Pointer to RCN to start removing nodes at
+ * NOTES:
+ *     This function must be called with the route cache lock held
+ */
+{
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. NCE (0x%X)  Node (0x%X).\n", NCE, Node));
+
+    if (IsInternalRCN(Node)) {
+        /* Traverse left subtree */
+        InvalidateNCEOnSubtree(NCE, Node->Left);
+
+        /* Traverse right subtree */
+        InvalidateNCEOnSubtree(NCE, Node->Right);
+
+        /* Finally check the node itself */
+        if (Node->NCE == NCE)
+            RemoveRouteToDestination(Node);
+    }
+}
+
+
+VOID RemoveSubtree(
+    PROUTE_CACHE_NODE Node)
+/*
+ * FUNCTION: Removes a subtree from the tree using recursion
+ * ARGUMENNTS:
+ *     Node = Pointer to RCN to start removing nodes at
+ * NOTES:
+ *     This function must be called with the route cache lock held
+ */
+{
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. Node (0x%X).\n", Node));
+
+    if (IsInternalRCN(Node)) {
+        /* Traverse left subtree */
+        RemoveSubtree(Node->Left);
+
+        /* Traverse right subtree */
+        RemoveSubtree(Node->Right);
+
+        /* Finally remove the node itself */
+
+        /* It's an internal node, so dereference NTE and NCE */
+        DereferenceObject(Node->NTE);
+        DereferenceObject(Node->NCE);
+
+#ifdef DBG
+        if (Node->RefCount != 1)
+            TI_DbgPrint(MIN_TRACE, ("RCN at (0x%X) has (%d) references (should be 1).\n", Node, Node->RefCount));
+#endif
+
+        /* Remove reference for being alive */
+        DereferenceObject(Node);
+    }
+}
+
+
+NTSTATUS RouteStartup(
+    VOID)
+/*
+ * FUNCTION: Initializes the routing subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
+
+    ExInitializeNPagedLookasideList(
+      &IPRCNList,                     /* Lookaside list */
+           NULL,                           /* Allocate routine */
+           NULL,                           /* Free routine */
+           0,                              /* Flags */
+           sizeof(ROUTE_CACHE_NODE),       /* Size of each entry */
+           TAG('I','P','R','C'),           /* Tag */
+           0);                             /* Depth */
+
+    /* Initialize the pseudo external route cache node */
+    ExternalRCN = ExAllocateFromNPagedLookasideList(&IPRCNList);
+    if (!ExternalRCN) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    INIT_TAG(ExternalRCN, TAG('R','C','N',' '));
+
+    ExternalRCN->Free   = FreeRCN;
+    ExternalRCN->Parent = NULL;
+    ExternalRCN->Left   = NULL;
+    ExternalRCN->Right  = NULL;
+
+    /* Initialize the route cache root */
+    RouteCache = ExternalRCN;
+
+    KeInitializeSpinLock(&RouteCacheLock);
+
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree.\n"));
+    PrintTree(RouteCache);
+#endif
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS RouteShutdown(
+    VOID)
+/*
+ * FUNCTION: Shuts down the routing subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    KIRQL OldIrql;
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called.\n"));
+
+    KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree.\n"));
+    PrintTree(RouteCache);
+#endif
+    /* Clear route cache */
+    RemoveSubtree(RouteCache);
+
+    FreeRCN(ExternalRCN);
+
+    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+
+    ExDeleteNPagedLookasideList(&IPRCNList);
+
+    return STATUS_SUCCESS;
+}
+
+
+UINT RouteGetRouteToDestination(
+    PIP_ADDRESS Destination,
+    PNET_TABLE_ENTRY NTE,
+    PROUTE_CACHE_NODE *RCN)
+/*
+ * FUNCTION: Locates an RCN describing a route to a destination address
+ * ARGUMENTS:
+ *     Destination = Pointer to destination address to find route to
+ *     NTE         = Pointer to NTE describing net to send on
+ *                   (NULL means routing module choose NTE to send on)
+ *     RCN         = Address of pointer to an RCN
+ * RETURNS:
+ *     Status of operation
+ * NOTES:
+ *     The RCN is referenced for the caller. The caller is responsible
+ *     for dereferencing it after use
+ */
+{
+    KIRQL OldIrql;
+    PROUTE_CACHE_NODE RCN2;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    PIP_INTERFACE Interface;
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X)  NTE (0x%X).\n",
+        Destination, NTE));
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Destination (%s)  NTE (%s).\n",
+                              A2S(Destination), NTE ? A2S(NTE->Address) : ""));
+
+    KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
+
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree (before).\n"));
+    PrintTree(RouteCache);
+#endif
+
+    ExternalRCN->Left = NULL;
+    RCN2 = SearchRouteCache(Destination, RouteCache);
+    if (IsExternalRCN(RCN2)) {
+        /* No route was found in the cache */
+
+        /* Check if the destination is on-link */
+        Interface = RouterFindOnLinkInterface(Destination, NTE);
+        if (Interface) {
+            if (!NTE) {
+                NTE = RouterFindBestNTE(Interface, Destination);
+                if (!NTE) {
+                    /* We cannot get to the specified destination. Return error */
+                    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+                    return IP_NO_ROUTE_TO_DESTINATION;
+                }
+            } else
+                ReferenceObject(NTE);
+
+            /* The destination address is on-link. Check our neighbor cache */
+            NCE = NBFindOrCreateNeighbor(Interface, Destination);
+            if (!NCE) {
+                DereferenceObject(NTE);
+                KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+                return IP_NO_RESOURCES;
+            }
+        } else {
+            /* Destination is not on any subnets we're on. Find a router to use */
+            NCE = RouterGetRoute(Destination, NTE);
+            if (!NCE) {
+                /* We cannot get to the specified destination. Return error */
+                KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+                return IP_NO_ROUTE_TO_DESTINATION;
+            }
+        }
+
+        /* Add the new route to the route cache */
+        if (RCN2 == RouteCache) {
+            RCN2       = ExpandExternalRCN();
+            RouteCache = RCN2;
+        } else
+            RCN2 = ExpandExternalRCN();
+        if (!RCN2) {
+            DereferenceObject(NTE);
+            DereferenceObject(NCE);
+            KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+            return IP_NO_RESOURCES;
+        }
+
+        RCN2->RefCount    = 1;
+        RCN2->State       = RCN_STATE_COMPUTED;
+        RCN2->NTE         = NTE;
+        RtlCopyMemory(&RCN2->Destination, Destination, sizeof(IP_ADDRESS));
+        RCN2->PathMTU     = NCE->Interface->MTU;
+        RCN2->NCE         = NCE;
+
+        /* The route cache node references the NTE and the NCE. The
+           NTE was referenced before and NCE is already referenced by
+           RouteGetRoute() or NBFindOrCreateNeighbor() so we don't
+           reference them here */
+    }
+
+    /* Reference the RCN for the user */
+    ReferenceObject(RCN2);
+
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree (after).\n"));
+    PrintTree(RouteCache);
+#endif
+
+    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+
+    *RCN = RCN2;
+
+    return IP_SUCCESS;
+}
+
+
+PROUTE_CACHE_NODE RouteAddRouteToDestination(
+    PIP_ADDRESS Destination,
+    PNET_TABLE_ENTRY NTE,
+    PIP_INTERFACE IF,
+    PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Adds a (permanent) route to a destination
+ * ARGUMENTS:
+ *     Destination = Pointer to destination address
+ *     NTE         = Pointer to net table entry
+ *     IF          = Pointer to interface to use
+ *     NCE         = Pointer to first hop to destination
+ * RETURNS:
+ *     Pointer to RCN if the route was added, NULL if not.
+ *     There can be at most one RCN per destination address / interface pair
+ */
+{
+    KIRQL OldIrql;
+    PROUTE_CACHE_NODE RCN;
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. Destination (0x%X)  NTE (0x%X)  IF (0x%X)  NCE (0x%X).\n",
+        Destination, NTE, IF, NCE));
+
+    TI_DbgPrint(DEBUG_RCACHE, ("Destination (%s)  NTE (%s)  NCE (%s).\n",
+                              A2S(Destination), 
+                              A2S(NTE->Address), 
+                              A2S(&NCE->Address)));
+
+    KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
+
+    /* Locate an external RCN we can expand */
+    RCN = RouteCache;
+    ExternalRCN->Left = NULL;
+    for (;;) {
+        RCN = SearchRouteCache(Destination, RCN);
+        if (IsInternalRCN(RCN)) {
+            ExternalRCN->Left = (PROUTE_CACHE_NODE)&RCN->Right;
+            /* This is an internal node, continue the search to the right */
+            RCN = RCN->Right;
+        } else
+            /* This is an external node, we've found an empty spot */
+            break;
+    }
+
+    /* Expand the external node */
+    if (RCN == RouteCache) {
+        RCN = ExpandExternalRCN();
+        RouteCache = RCN;
+    } else
+        RCN = ExpandExternalRCN();
+    if (!RCN) {
+        KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+        return NULL;
+    }
+
+    /* Initialize the newly created internal node */
+
+    INIT_TAG(RCN, TAG('R','C','N',' '));
+
+    /* Reference once for beeing alive */
+    RCN->RefCount    = 1;
+    RCN->State       = RCN_STATE_PERMANENT;
+    RCN->NTE         = NTE;
+    RtlCopyMemory(&RCN->Destination, Destination, sizeof(IP_ADDRESS));
+    RCN->PathMTU     = IF->MTU;
+    RCN->NCE         = NCE;
+
+    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+
+    /* The route cache node references the NTE and the NCE */
+    ReferenceObject(NTE);
+    if (NCE)
+        ReferenceObject(NCE);
+
+#if 0
+    TI_DbgPrint(MIN_TRACE, ("Displaying tree.\n"));
+    PrintTree(RouteCache);
+#endif
+
+    return RCN;
+}
+
+
+VOID RouteRemoveRouteToDestination(
+    PROUTE_CACHE_NODE RCN)
+/*
+ * FUNCTION: Removes a route to a destination
+ * ARGUMENTS:
+ *     RCN = Pointer to route cache node to remove
+ */
+{
+    KIRQL OldIrql;
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. RCN (0x%X).\n", RCN));
+
+    KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
+
+    RemoveRouteToDestination(RCN);
+
+    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+}
+
+
+VOID RouteInvalidateNTE(
+    PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Removes all RCNs with references to an NTE
+ * ARGUMENTS:
+ *     NTE = Pointer to net table entry to invalidate
+ */
+{
+    KIRQL OldIrql;
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. NTE (0x%X).\n", NTE));
+
+    KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
+    InvalidateNTEOnSubtree(NTE, RouteCache);
+    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+}
+
+
+VOID RouteInvalidateNCE(
+    PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Removes all RCNs with references to an NCE
+ * ARGUMENTS:
+ *     NCE = Pointer to neighbor cache entry to invalidate
+ */
+{
+    KIRQL OldIrql;
+    TI_DbgPrint(DEBUG_RCACHE, ("Called. NCE (0x%X).\n", NCE));
+
+    KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
+    InvalidateNCEOnSubtree(NCE, RouteCache);
+    KeReleaseSpinLock(&RouteCacheLock, OldIrql);
+}
+
+NTSTATUS
+RouteFriendlyAddRoute( PIPROUTE_ENTRY ire ) {
+    KIRQL OldIrql;
+    
+
+    /* Find IF */
+    KeAcquireSpinLock(&InterfaceListLock, &OldIrql);
+    //RouteAddRouteToDestination(&Dest,Nte,If,Nce);
+    KeReleaseSpinLock(&InterfaceListLock, OldIrql);
+
+    return STATUS_SUCCESS;
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/router.c b/reactos/drivers/lib/ip/network/router.c
new file mode 100644 (file)
index 0000000..bc4c211
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/router.c
+ * PURPOSE:     IP routing subsystem
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * NOTES:
+ *   This file holds authoritative routing information.
+ *   Information queries on the route table should be handled here.
+ *   This information should always override the route cache info.
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+LIST_ENTRY FIBListHead;
+KSPIN_LOCK FIBLock;
+
+
+VOID FreeFIB(
+    PVOID Object)
+/*
+ * FUNCTION: Frees an forward information base object
+ * ARGUMENTS:
+ *     Object = Pointer to an forward information base structure
+ */
+{
+    ExFreePool(Object);
+}
+
+
+VOID DestroyFIBE(
+    PFIB_ENTRY FIBE)
+/*
+ * FUNCTION: Destroys an forward information base entry
+ * ARGUMENTS:
+ *     FIBE = Pointer to FIB entry
+ * NOTES:
+ *     The forward information base lock must be held when called
+ */
+{
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. FIBE (0x%X).\n", FIBE));
+
+    /* Unlink the FIB entry from the list */
+    RemoveEntryList(&FIBE->ListEntry);
+
+    /* Dereference the referenced objects */
+    DereferenceObject(FIBE->NetworkAddress);
+    DereferenceObject(FIBE->Netmask);
+    DereferenceObject(FIBE->Router);
+
+#ifdef DBG
+    FIBE->RefCount--;
+
+    if (FIBE->RefCount != 0) {
+        TI_DbgPrint(MIN_TRACE, ("FIB entry at (0x%X) has (%d) references (Should be 0).\n", FIBE, FIBE->RefCount));
+    }
+#endif
+
+    /* And free the FIB entry */
+    FreeFIB(FIBE);
+}
+
+
+VOID DestroyFIBEs(
+    VOID)
+/*
+ * FUNCTION: Destroys all forward information base entries
+ * NOTES:
+ *     The forward information base lock must be held when called
+ */
+{
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+    PFIB_ENTRY Current;
+
+    /* Search the list and remove every FIB entry we find */
+    CurrentEntry = FIBListHead.Flink;
+    while (CurrentEntry != &FIBListHead) {
+        NextEntry = CurrentEntry->Flink;
+       Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
+        /* Destroy the FIB entry */
+        DestroyFIBE(Current);
+        CurrentEntry = NextEntry;
+    }
+}
+
+
+UINT CountFIBs() {
+    UINT FibCount = 0;
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+
+    /* Search the list and remove every FIB entry we find */
+    CurrentEntry = FIBListHead.Flink;
+    while (CurrentEntry != &FIBListHead) {
+        NextEntry = CurrentEntry->Flink;
+        CurrentEntry = NextEntry;
+       FibCount++;
+    }
+
+    return FibCount;
+}
+
+
+UINT CopyFIBs( PFIB_ENTRY Target ) {
+    UINT FibCount = 0;
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+    PFIB_ENTRY Current;
+
+    /* Search the list and remove every FIB entry we find */
+    CurrentEntry = FIBListHead.Flink;
+    while (CurrentEntry != &FIBListHead) {
+        NextEntry = CurrentEntry->Flink;
+       Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
+       Target[FibCount] = *Current;
+        CurrentEntry = NextEntry;
+       FibCount++;
+    }
+
+    return FibCount;    
+}
+
+
+UINT CommonPrefixLength(
+    PIP_ADDRESS Address1,
+    PIP_ADDRESS Address2)
+/*
+ * FUNCTION: Computes the length of the longest prefix common to two addresses
+ * ARGUMENTS:
+ *     Address1 = Pointer to first address
+ *     Address2 = Pointer to second address
+ * NOTES:
+ *     The two addresses must be of the same type
+ * RETURNS:
+ *     Length of longest common prefix
+ */
+{
+    PUCHAR Addr1, Addr2;
+    UINT Size;
+    UINT i, j;
+    UINT Bitmask;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. Address1 (0x%X)  Address2 (0x%X).\n", Address1, Address2));
+
+    /*TI_DbgPrint(DEBUG_ROUTER, ("Target  (%s) \n", A2S(Address1)));*/
+    /*TI_DbgPrint(DEBUG_ROUTER, ("Adapter (%s).\n", A2S(Address2)));*/
+
+    if (Address1->Type == IP_ADDRESS_V4)
+        Size = sizeof(IPv4_RAW_ADDRESS);
+    else
+        Size = sizeof(IPv6_RAW_ADDRESS);
+
+    Addr1 = (PUCHAR)&Address1->Address.IPv4Address;
+    Addr2 = (PUCHAR)&Address2->Address.IPv4Address;
+
+    /* Find first non-matching byte */
+    for (i = 0; i < Size && Addr1[i] == Addr2[i]; i++);
+    if( i == Size ) return 8 * i;
+
+    /* Find first non-matching bit */
+    Bitmask = 0x80;
+    for (j = 0; (Addr1[i] & Bitmask) != (Addr2[i] & Bitmask); j++)
+        Bitmask >>= 1;
+
+    return 8 * i + j;
+}
+
+
+BOOLEAN HasPrefix(
+    PIP_ADDRESS Address,
+    PIP_ADDRESS Prefix,
+    UINT Length)
+/*
+ * FUNCTION: Determines wether an address has an given prefix
+ * ARGUMENTS:
+ *     Address = Pointer to address to use
+ *     Prefix  = Pointer to prefix to check for
+ *     Length  = Length of prefix
+ * RETURNS:
+ *     TRUE if the address has the prefix, FALSE if not
+ * NOTES:
+ *     The two addresses must be of the same type
+ */
+{
+    PUCHAR pAddress = (PUCHAR)&Address->Address;
+    PUCHAR pPrefix  = (PUCHAR)&Prefix->Address;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. Address (0x%X)  Prefix (0x%X)  Length (%d).\n", Address, Prefix, Length));
+
+#if 0
+    TI_DbgPrint(DEBUG_ROUTER, ("Address (%s)  Prefix (%s).\n",
+        A2S(Address), A2S(Prefix)));
+#endif
+
+    /* Check that initial integral bytes match */
+    while (Length > 8) {
+        if (*pAddress++ != *pPrefix++)
+            return FALSE;
+        Length -= 8;
+    } 
+
+    /* Check any remaining bits */
+    if ((Length > 0) && ((*pAddress >> (8 - Length)) != (*pPrefix >> (8 - Length))))
+        return FALSE;
+
+    return TRUE;
+}
+
+
+PNET_TABLE_ENTRY RouterFindBestNTE(
+    PIP_INTERFACE Interface,
+    PIP_ADDRESS Destination)
+/*
+ * FUNCTION: Checks all on-link prefixes to find out if an address is on-link
+ * ARGUMENTS:
+ *     Interface   = Pointer to interface to use
+ *     Destination = Pointer to destination address
+ * NOTES:
+ *     If found the NTE if referenced
+ * RETURNS:
+ *     Pointer to NTE if found, NULL if not
+ */
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY CurrentEntry;
+    PNET_TABLE_ENTRY Current;
+    UINT Length, BestLength  = 0;
+    PNET_TABLE_ENTRY BestNTE = NULL;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. Interface (0x%X)  Destination (0x%X).\n", Interface, Destination));
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Destination (%s).\n", A2S(Destination)));
+
+    KeAcquireSpinLock(&Interface->Lock, &OldIrql);
+
+    CurrentEntry = Interface->NTEListHead.Flink;
+    while (CurrentEntry != &Interface->NTEListHead) {
+       Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry);
+       TI_DbgPrint(DEBUG_ROUTER, ("Looking at NTE %s\n", A2S(Current->Address)));
+
+        Length = CommonPrefixLength(Destination, Current->Address);
+        if (BestNTE) {
+            if (Length > BestLength) {
+                /* This seems to be a better NTE */
+                DereferenceObject(BestNTE);
+                ReferenceObject(Current);
+                BestNTE    = Current;
+                BestLength = Length;
+            }
+        } else {
+            /* First suitable NTE found, save it */
+            ReferenceObject(Current);
+            BestNTE    = Current;
+            BestLength = Length;
+        }
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeReleaseSpinLock(&Interface->Lock, OldIrql);
+
+    return BestNTE;
+}
+
+
+PIP_INTERFACE RouterFindOnLinkInterface(
+    PIP_ADDRESS Address,
+    PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Checks all on-link prefixes to find out if an address is on-link
+ * ARGUMENTS:
+ *     Address = Pointer to address to check
+ *     NTE     = Pointer to NTE to check (NULL = check all interfaces)
+ * RETURNS:
+ *     Pointer to interface if address is on-link, NULL if not
+ */
+{
+    PLIST_ENTRY CurrentEntry;
+    PPREFIX_LIST_ENTRY Current;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. Address (0x%X)  NTE (0x%X).\n", Address, NTE));
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Address (%s)  NTE (%s).\n",
+                              A2S(Address), NTE ? A2S(NTE->Address) : ""));
+
+    CurrentEntry = PrefixListHead.Flink;
+    while (CurrentEntry != &PrefixListHead) {
+           Current = CONTAINING_RECORD(CurrentEntry, PREFIX_LIST_ENTRY, ListEntry);
+
+        if (HasPrefix(Address, Current->Prefix, Current->PrefixLength) &&
+            ((!NTE) || (NTE->Interface == Current->Interface)))
+            return Current->Interface;
+
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    return NULL;
+}
+
+
+PFIB_ENTRY RouterAddRoute(
+    PIP_ADDRESS NetworkAddress,
+    PIP_ADDRESS Netmask,
+    PNEIGHBOR_CACHE_ENTRY Router,
+    UINT Metric)
+/*
+ * FUNCTION: Adds a route to the Forward Information Base (FIB)
+ * ARGUMENTS:
+ *     NetworkAddress = Pointer to address of network
+ *     Netmask        = Pointer to netmask of network
+ *     Router         = Pointer to NCE of router to use
+ *     Metric         = Cost of this route
+ * RETURNS:
+ *     Pointer to FIB entry if the route was added, NULL if not
+ * NOTES:
+ *     The FIB entry references the NetworkAddress, Netmask and
+ *     the NCE of the router. The caller is responsible for providing
+ *     these references
+ */
+{
+    PFIB_ENTRY FIBE;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. NetworkAddress (0x%X)  Netmask (0x%X) "
+        "Router (0x%X)  Metric (%d).\n", NetworkAddress, Netmask, Router, Metric));
+
+    TI_DbgPrint(DEBUG_ROUTER, ("NetworkAddress (%s)  Netmask (%s)  Router (%s).\n",
+                              A2S(NetworkAddress), 
+                              A2S(Netmask), 
+                              A2S(&Router->Address)));
+
+    FIBE = ExAllocatePool(NonPagedPool, sizeof(FIB_ENTRY));
+    if (!FIBE) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+   INIT_TAG(Router, TAG('R','O','U','T'));
+
+    FIBE->Free           = FreeFIB;
+    FIBE->NetworkAddress = NetworkAddress;
+    FIBE->Netmask        = Netmask;
+    FIBE->Router         = Router;
+    FIBE->Metric         = Metric;
+
+    /* Add FIB to the forward information base */
+    ExInterlockedInsertTailList(&FIBListHead, &FIBE->ListEntry, &FIBLock);
+
+    return FIBE;
+}
+
+
+PNEIGHBOR_CACHE_ENTRY RouterGetRoute(
+    PIP_ADDRESS Destination,
+    PNET_TABLE_ENTRY NTE)
+/*
+ * FUNCTION: Finds a router to use to get to Destination
+ * ARGUMENTS:
+ *     Destination = Pointer to destination address (NULL means don't care)
+ *     NTE         = Pointer to NTE describing net to send on
+ *                   (NULL means don't care)
+ * RETURNS:
+ *     Pointer to NCE for router, NULL if none was found
+ * NOTES:
+ *     If found the NCE is referenced
+ */
+{
+    KIRQL OldIrql;
+    PLIST_ENTRY CurrentEntry;
+    PLIST_ENTRY NextEntry;
+    PFIB_ENTRY Current;
+    UCHAR State, BestState = 0;
+    UINT Length, BestLength = 0;
+    PNEIGHBOR_CACHE_ENTRY NCE, BestNCE = NULL;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. Destination (0x%X)  NTE (0x%X).\n", Destination, NTE));
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Destination (%s)\n", A2S(Destination)));
+    if( NTE )
+       TI_DbgPrint(DEBUG_ROUTER, ("NTE (%s).\n", A2S(NTE->Address)));
+
+    KeAcquireSpinLock(&FIBLock, &OldIrql);
+
+    CurrentEntry = FIBListHead.Flink;
+    while (CurrentEntry != &FIBListHead) {
+        NextEntry = CurrentEntry->Flink;
+           Current = CONTAINING_RECORD(CurrentEntry, FIB_ENTRY, ListEntry);
+
+        NCE   = Current->Router;
+        State = NCE->State;
+
+        if ((!NTE) || (NTE->Interface == NCE->Interface)) {
+            if (Destination)
+                Length = CommonPrefixLength(Destination, &NCE->Address);
+            else
+                Length = 0;
+
+            if (BestNCE) {
+                if ((State  > BestState)  || 
+                    ((State == BestState) &&
+                    (Length > BestLength))) {
+                    /* This seems to be a better router */
+                    DereferenceObject(BestNCE);
+                    ReferenceObject(NCE);
+                    BestNCE    = NCE;
+                    BestLength = Length;
+                    BestState  = State;
+                }
+            } else {
+                /* First suitable router found, save it */
+                ReferenceObject(NCE);
+                BestNCE    = NCE;
+                BestLength = Length;
+                BestState  = State;
+            }
+        }
+        CurrentEntry = NextEntry;
+    }
+
+    KeReleaseSpinLock(&FIBLock, OldIrql);
+
+    return BestNCE;
+}
+
+
+VOID RouterRemoveRoute(
+    PFIB_ENTRY FIBE)
+/*
+ * FUNCTION: Removes a route from the Forward Information Base (FIB)
+ * ARGUMENTS:
+ *     FIBE = Pointer to FIB entry describing route
+ */
+{
+    KIRQL OldIrql;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called. FIBE (0x%X).\n", FIBE));
+
+    TI_DbgPrint(DEBUG_ROUTER, ("FIBE (%s).\n", A2S(FIBE->NetworkAddress)));
+
+    KeAcquireSpinLock(&FIBLock, &OldIrql);
+    DestroyFIBE(FIBE);
+    KeReleaseSpinLock(&FIBLock, OldIrql);
+}
+
+
+PFIB_ENTRY RouterCreateRouteIPv4(
+    IPv4_RAW_ADDRESS NetworkAddress,
+    IPv4_RAW_ADDRESS Netmask,
+    IPv4_RAW_ADDRESS RouterAddress,
+    PNET_TABLE_ENTRY NTE,
+    UINT Metric)
+/*
+ * FUNCTION: Creates a route with IPv4 addresses as parameters
+ * ARGUMENTS:
+ *     NetworkAddress = Address of network
+ *     Netmask        = Netmask of network
+ *     RouterAddress  = Address of router to use
+ *     NTE            = Pointer to NTE to use
+ *     Metric         = Cost of this route
+ * RETURNS:
+ *     Pointer to FIB entry if the route was created, NULL if not.
+ *     The FIB entry references the NTE. The caller is responsible
+ *     for providing this reference
+ */
+{
+    PIP_ADDRESS pNetworkAddress;
+    PIP_ADDRESS pNetmask;
+    PIP_ADDRESS pRouterAddress;
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    PFIB_ENTRY FIBE;
+
+    pNetworkAddress = AddrBuildIPv4(NetworkAddress);
+    if (!pNetworkAddress) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        return NULL;
+    }
+
+    pNetmask = AddrBuildIPv4(Netmask);
+    if (!pNetmask) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        DereferenceObject(pNetworkAddress);
+        return NULL;
+    }
+
+    pRouterAddress = AddrBuildIPv4(RouterAddress);
+    if (!pRouterAddress) {
+        TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        DereferenceObject(pNetworkAddress);
+        DereferenceObject(pNetmask);
+        return NULL;
+    }
+
+    /* The NCE references RouterAddress. The NCE is referenced for us */
+    NCE = NBAddNeighbor(NTE->Interface,
+                        pRouterAddress,
+                        NULL,
+                        NTE->Interface->AddressLength,
+                        NUD_PROBE);
+    if (!NCE) {
+        /* Not enough free resources */
+        DereferenceObject(pNetworkAddress);
+        DereferenceObject(pNetmask);
+        DereferenceObject(pRouterAddress);
+        return NULL;
+    }
+
+    ReferenceObject(pNetworkAddress);
+    ReferenceObject(pNetmask);
+    FIBE = RouterAddRoute(pNetworkAddress, pNetmask, NCE, 1);
+    if (!FIBE) {
+        /* Not enough free resources */
+        NBRemoveNeighbor(NCE);
+        DereferenceObject(pNetworkAddress);
+        DereferenceObject(pNetmask);
+        DereferenceObject(pRouterAddress);
+
+        (pNetworkAddress->Free)(pNetworkAddress);
+        (pNetmask->Free)(pNetmask);
+        (pRouterAddress->Free)(pRouterAddress);
+    }
+
+    return FIBE;
+}
+
+
+NTSTATUS RouterStartup(
+    VOID)
+/*
+ * FUNCTION: Initializes the routing subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    TI_DbgPrint(DEBUG_ROUTER, ("Called.\n"));
+
+    /* Initialize the Forward Information Base */
+    InitializeListHead(&FIBListHead);
+    KeInitializeSpinLock(&FIBLock);
+
+#if 0
+    /* TEST: Create a test route */
+    /* Network is 10.0.0.0  */
+    /* Netmask is 255.0.0.0 */
+    /* Router is 10.0.0.1   */
+    RouterCreateRouteIPv4(0x0000000A, 0x000000FF, 0x0100000A, NTE?, 1);
+#endif
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS RouterShutdown(
+    VOID)
+/*
+ * FUNCTION: Shuts down the routing subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    KIRQL OldIrql;
+
+    TI_DbgPrint(DEBUG_ROUTER, ("Called.\n"));
+
+    /* Clear Forward Information Base */
+    KeAcquireSpinLock(&FIBLock, &OldIrql);
+    DestroyFIBEs();
+    KeReleaseSpinLock(&FIBLock, OldIrql);
+
+    return STATUS_SUCCESS;
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/network/routines.c b/reactos/drivers/lib/ip/network/routines.c
new file mode 100644 (file)
index 0000000..8683192
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        tcpip/routines.c
+ * PURPOSE:     Common routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+static UINT RandomNumber = 0x12345678;
+
+
+UINT Random(
+    VOID)
+/*
+ * FUNCTION: Returns a pseudo random number
+ * RETURNS:
+ *     Pseudo random number
+ */
+{
+    RandomNumber ^= 0x78563412;
+
+    return RandomNumber;
+}
+
+
+__inline INT SkipToOffset(
+    PNDIS_BUFFER Buffer,
+    UINT Offset,
+    PCHAR *Data,
+    PUINT Size)
+/*
+ * FUNCTION: Skip Offset bytes into a buffer chain
+ * ARGUMENTS:
+ *     Buffer = Pointer to NDIS buffer
+ *     Offset = Number of bytes to skip
+ *     Data   = Address of a pointer that on return will contain the
+ *              address of the offset in the buffer
+ *     Size   = Address of a pointer that on return will contain the
+ *              size of the destination buffer
+ * RETURNS:
+ *     Offset into buffer, -1 if buffer chain was smaller than Offset bytes
+ * NOTES:
+ *     Buffer may be NULL
+ */
+{
+    for (;;) {
+
+        if (!Buffer)
+            return -1;
+
+        NdisQueryBuffer(Buffer, (PVOID)Data, Size);
+
+        if (Offset < *Size) {
+            *Data = (PCHAR)((ULONG_PTR) *Data + Offset);
+            *Size              -= Offset;
+            break;
+        }
+
+        Offset -= *Size;
+
+        NdisGetNextBuffer(Buffer, &Buffer);
+    }
+
+    return Offset;
+}
+
+
+UINT CopyBufferToBufferChain(
+    PNDIS_BUFFER DstBuffer,
+    UINT DstOffset,
+    PCHAR SrcData,
+    UINT Length)
+/*
+ * FUNCTION: Copies data from a buffer to an NDIS buffer chain
+ * ARGUMENTS:
+ *     DstBuffer = Pointer to destination NDIS buffer 
+ *     DstOffset = Destination start offset
+ *     SrcData   = Pointer to source buffer
+ *     Length    = Number of bytes to copy
+ * RETURNS:
+ *     Number of bytes copied to destination buffer
+ * NOTES:
+ *     The number of bytes copied may be limited by the destination
+ *     buffer size
+ */
+{
+    UINT BytesCopied, BytesToCopy, DstSize;
+    PCHAR DstData;
+
+    TI_DbgPrint(DEBUG_PBUFFER, ("DstBuffer (0x%X)  DstOffset (0x%X)  SrcData (0x%X)  Length (%d)\n", DstBuffer, DstOffset, SrcData, Length));
+
+    /* Skip DstOffset bytes in the destination buffer chain */
+    if (SkipToOffset(DstBuffer, DstOffset, &DstData, &DstSize) == -1)
+        return 0;
+
+    /* Start copying the data */
+    BytesCopied = 0;
+    for (;;) {
+        BytesToCopy = MIN(DstSize, Length);
+
+        RtlCopyMemory((PVOID)DstData, (PVOID)SrcData, BytesToCopy);
+        BytesCopied += BytesToCopy;
+        SrcData      = (PCHAR)((ULONG_PTR)SrcData + BytesToCopy);
+
+        Length -= BytesToCopy;
+        if (Length == 0)
+            break;
+
+        DstSize -= BytesToCopy;
+        if (DstSize == 0) {
+            /* No more bytes in desination buffer. Proceed to
+               the next buffer in the destination buffer chain */
+            NdisGetNextBuffer(DstBuffer, &DstBuffer);
+            if (!DstBuffer)
+                break;
+
+            NdisQueryBuffer(DstBuffer, (PVOID)&DstData, &DstSize);
+        }
+    }
+
+    return BytesCopied;
+}
+
+
+UINT CopyBufferChainToBuffer(
+    PCHAR DstData,
+    PNDIS_BUFFER SrcBuffer,
+    UINT SrcOffset,
+    UINT Length)
+/*
+ * FUNCTION: Copies data from an NDIS buffer chain to a buffer
+ * ARGUMENTS:
+ *     DstData   = Pointer to destination buffer
+ *     SrcBuffer = Pointer to source NDIS buffer
+ *     SrcOffset = Source start offset
+ *     Length    = Number of bytes to copy
+ * RETURNS:
+ *     Number of bytes copied to destination buffer
+ * NOTES:
+ *     The number of bytes copied may be limited by the source
+ *     buffer size
+ */
+{
+    UINT BytesCopied, BytesToCopy, SrcSize;
+    PCHAR SrcData;
+
+    TI_DbgPrint(DEBUG_PBUFFER, ("DstData 0x%X  SrcBuffer 0x%X  SrcOffset 0x%X  Length %d\n",DstData,SrcBuffer, SrcOffset, Length));
+    
+    /* Skip SrcOffset bytes in the source buffer chain */
+    if (SkipToOffset(SrcBuffer, SrcOffset, &SrcData, &SrcSize) == -1)
+        return 0;
+
+    /* Start copying the data */
+    BytesCopied = 0;
+    for (;;) {
+        BytesToCopy = MIN(SrcSize, Length);
+
+        TI_DbgPrint(DEBUG_PBUFFER, ("Copying (%d) bytes from 0x%X to 0x%X\n", BytesToCopy, SrcData, DstData));
+
+        RtlCopyMemory((PVOID)DstData, (PVOID)SrcData, BytesToCopy);
+        BytesCopied += BytesToCopy;
+        DstData      = (PCHAR)((ULONG_PTR)DstData + BytesToCopy);
+
+        Length -= BytesToCopy;
+        if (Length == 0)
+            break;
+
+        SrcSize -= BytesToCopy;
+        if (SrcSize == 0) {
+            /* No more bytes in source buffer. Proceed to
+               the next buffer in the source buffer chain */
+            NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
+            if (!SrcBuffer)
+                break;
+
+            NdisQueryBuffer(SrcBuffer, (PVOID)&SrcData, &SrcSize);
+        }
+    }
+
+    return BytesCopied;
+}
+
+
+UINT CopyPacketToBuffer(
+    PCHAR DstData,
+    PNDIS_PACKET SrcPacket,
+    UINT SrcOffset,
+    UINT Length)
+/*
+ * FUNCTION: Copies data from an NDIS packet to a buffer
+ * ARGUMENTS:
+ *     DstData   = Pointer to destination buffer
+ *     SrcPacket = Pointer to source NDIS packet
+ *     SrcOffset = Source start offset
+ *     Length    = Number of bytes to copy
+ * RETURNS:
+ *     Number of bytes copied to destination buffer
+ * NOTES:
+ *     The number of bytes copied may be limited by the source
+ *     buffer size
+ */
+{
+    PNDIS_BUFFER FirstBuffer;
+    PVOID Address;
+    UINT FirstLength;
+    UINT TotalLength;
+
+    TI_DbgPrint(DEBUG_PBUFFER, ("DstData (0x%X)  SrcPacket (0x%X)  SrcOffset (0x%X)  Length (%d)\n", DstData, SrcPacket, SrcOffset, Length));
+
+    NdisGetFirstBufferFromPacket(SrcPacket,
+                                 &FirstBuffer,
+                                 &Address,
+                                 &FirstLength,
+                                 &TotalLength);
+
+    return CopyBufferChainToBuffer(DstData, FirstBuffer, SrcOffset, Length);
+}
+
+
+UINT CopyPacketToBufferChain(
+    PNDIS_BUFFER DstBuffer,
+    UINT DstOffset,
+    PNDIS_PACKET SrcPacket,
+    UINT SrcOffset,
+    UINT Length)
+/*
+ * FUNCTION: Copies data from an NDIS packet to an NDIS buffer chain
+ * ARGUMENTS:
+ *     DstBuffer = Pointer to destination NDIS buffer
+ *     DstOffset = Destination start offset
+ *     SrcPacket = Pointer to source NDIS packet
+ *     SrcOffset = Source start offset
+ *     Length    = Number of bytes to copy
+ * RETURNS:
+ *     Number of bytes copied to destination buffer
+ * NOTES:
+ *     The number of bytes copied may be limited by the source and
+ *     destination buffer sizes
+ */
+{
+    PNDIS_BUFFER SrcBuffer;
+    PCHAR DstData, SrcData;
+    UINT DstSize, SrcSize;
+    UINT Count, Total;
+
+    TI_DbgPrint(DEBUG_PBUFFER, ("DstBuffer (0x%X)  DstOffset (0x%X)  SrcPacket (0x%X)  SrcOffset (0x%X)  Length (%d)\n", DstBuffer, DstOffset, SrcPacket, SrcOffset, Length));
+
+    /* Skip DstOffset bytes in the destination buffer chain */
+    NdisQueryBuffer(DstBuffer, (PVOID)&DstData, &DstSize);
+    if (SkipToOffset(DstBuffer, DstOffset, &DstData, &DstSize) == -1)
+        return 0;
+
+    /* Skip SrcOffset bytes in the source packet */
+    NdisGetFirstBufferFromPacket(SrcPacket, &SrcBuffer, &SrcData, &SrcSize, &Total);
+    if (SkipToOffset(SrcBuffer, SrcOffset, &SrcData, &SrcSize) == -1)
+        return 0;
+
+    /* Copy the data */
+    for (Total = 0;;) {
+        /* Find out how many bytes we can copy at one time */
+        if (Length < SrcSize)
+            Count = Length;
+        else
+            Count = SrcSize;
+        if (DstSize < Count)
+            Count = DstSize;
+
+        RtlCopyMemory((PVOID)DstData, (PVOID)SrcData, Count);
+
+        Total  += Count;
+        Length -= Count;
+        if (Length == 0)
+            break;
+
+        DstSize -= Count;
+        if (DstSize == 0) {
+            /* No more bytes in destination buffer. Proceed to
+               the next buffer in the destination buffer chain */
+            NdisGetNextBuffer(DstBuffer, &DstBuffer);
+            if (!DstBuffer)
+                break;
+
+            NdisQueryBuffer(DstBuffer, (PVOID)&DstData, &DstSize);
+        }
+
+        SrcSize -= Count;
+        if (SrcSize == 0) {
+            /* No more bytes in source buffer. Proceed to
+               the next buffer in the source buffer chain */
+            NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
+            if (!SrcBuffer)
+                break;
+
+            NdisQueryBuffer(SrcBuffer, (PVOID)&SrcData, &SrcSize);
+        }
+    }
+
+    return Total;
+}
+
+
+PVOID AdjustPacket(
+    PNDIS_PACKET Packet,
+    UINT Available,
+    UINT Needed)
+/*
+ * FUNCTION: Adjusts the amount of unused space at the beginning of the packet
+ * ARGUMENTS:
+ *     Packet    = Pointer to packet
+ *     Available = Number of bytes available at start of first buffer
+ *     Needed    = Number of bytes needed for the header
+ * RETURNS:
+ *     Pointer to start of packet
+ */
+{
+    PNDIS_BUFFER NdisBuffer;
+    INT Adjust;
+
+    TI_DbgPrint(DEBUG_PBUFFER, ("Available = %d, Needed = %d.\n", Available, Needed));
+
+    Adjust = Available - Needed;
+
+    NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
+
+    /* If Adjust is zero there is no need to adjust this packet as
+       there is no additional space at start the of first buffer */
+    if (Adjust != 0) {
+        NdisBuffer->MappedSystemVa  = (PVOID) ((ULONG_PTR)(NdisBuffer->MappedSystemVa) + Adjust);
+        NdisBuffer->ByteOffset     += Adjust;
+        NdisBuffer->ByteCount      -= Adjust;
+    }
+
+    return NdisBuffer->MappedSystemVa;
+}
+
+
+UINT ResizePacket(
+    PNDIS_PACKET Packet,
+    UINT Size)
+/*
+ * FUNCTION: Resizes an NDIS packet
+ * ARGUMENTS:
+ *     Packet = Pointer to packet
+ *     Size   = Number of bytes in first buffer
+ * RETURNS:
+ *     Previous size of first buffer
+ */
+{
+    PNDIS_BUFFER NdisBuffer;
+    UINT OldSize;
+
+    NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
+
+    OldSize = NdisBuffer->ByteCount;
+
+    if (Size != OldSize)
+        NdisBuffer->ByteCount = Size;
+
+    return OldSize;
+}
+
+#ifdef DBG
+
+static VOID DisplayIPHeader(
+    PCHAR Header,
+    UINT Length)
+{
+    /* FIXME: IPv4 only */
+    PIPv4_HEADER IPHeader = (PIPv4_HEADER)Header;
+
+    DbgPrint("IPv4 header:\n");
+    DbgPrint("VerIHL: 0x%x (version 0x%x, length %d 32-bit words)\n",
+      IPHeader->VerIHL, (IPHeader->VerIHL & 0xF0) >> 4, IPHeader->VerIHL & 0x0F);
+    DbgPrint("  Tos: %d\n", IPHeader->Tos);
+    DbgPrint("  TotalLength: %d\n", WN2H(IPHeader->TotalLength));
+    DbgPrint("  Id: %d\n", WN2H(IPHeader->Id));
+    DbgPrint("  FlagsFragOfs: 0x%x (offset 0x%x)\n", WN2H(IPHeader->FlagsFragOfs), WN2H(IPHeader->FlagsFragOfs) & IPv4_FRAGOFS_MASK);
+    if ((WN2H(IPHeader->FlagsFragOfs) & IPv4_DF_MASK) > 0) DbgPrint("    IPv4_DF - Don't fragment\n");
+    if ((WN2H(IPHeader->FlagsFragOfs) & IPv4_MF_MASK) > 0) DbgPrint("    IPv4_MF - More fragments\n");
+    DbgPrint("  Ttl: %d\n", IPHeader->Ttl);
+    DbgPrint("  Protocol: %d\n", IPHeader->Protocol);
+    DbgPrint("  Checksum: 0x%x\n", WN2H(IPHeader->Checksum));
+    DbgPrint("  SrcAddr: %d.%d.%d.%d\n",
+      ((IPHeader->SrcAddr >> 0) & 0xFF), ((IPHeader->SrcAddr >> 8) & 0xFF),
+      ((IPHeader->SrcAddr >> 16) & 0xFF), ((IPHeader->SrcAddr >> 24) & 0xFF));
+    DbgPrint("  DstAddr: %d.%d.%d.%d\n",
+      ((IPHeader->DstAddr >> 0) & 0xFF), ((IPHeader->DstAddr >> 8) & 0xFF),
+      ((IPHeader->DstAddr >> 16) & 0xFF), ((IPHeader->DstAddr >> 24) & 0xFF));
+}
+
+VOID DisplayIPPacket(
+    PIP_PACKET IPPacket)
+{
+    PCHAR p;
+    UINT Length;
+    PNDIS_BUFFER Buffer;
+    PNDIS_BUFFER NextBuffer;
+    PCHAR CharBuffer;
+
+    if ((DebugTraceLevel & (DEBUG_PBUFFER | DEBUG_IP)) != (DEBUG_PBUFFER | DEBUG_IP)) {
+        return;
+    }
+
+    if (!IPPacket) {
+        TI_DbgPrint(MIN_TRACE, ("Cannot display null packet.\n"));
+        return;
+    }
+
+         TI_DbgPrint(MIN_TRACE, ("IPPacket is at (0x%X).\n", IPPacket));
+    TI_DbgPrint(MIN_TRACE, ("Header buffer is at (0x%X).\n", IPPacket->Header));
+    TI_DbgPrint(MIN_TRACE, ("Header size is (%d).\n", IPPacket->HeaderSize));
+    TI_DbgPrint(MIN_TRACE, ("TotalSize (%d).\n", IPPacket->TotalSize));
+    TI_DbgPrint(MIN_TRACE, ("ContigSize (%d).\n", IPPacket->ContigSize));
+    TI_DbgPrint(MIN_TRACE, ("NdisPacket (0x%X).\n", IPPacket->NdisPacket));
+
+    if (IPPacket->NdisPacket) {
+        NdisQueryPacket(IPPacket->NdisPacket, NULL, NULL, &Buffer, NULL);
+        for (; Buffer != NULL; Buffer = NextBuffer) {
+            NdisGetNextBuffer(Buffer, &NextBuffer);
+            NdisQueryBuffer(Buffer, (PVOID)&p, &Length);
+           //OskitDumpBuffer( p, Length );
+        }
+    } else {
+        p      = IPPacket->Header;
+        Length = IPPacket->ContigSize;
+       //OskitDumpBuffer( p, Length );
+    }
+
+    if (IPPacket->NdisPacket) {
+        NdisQueryPacket(IPPacket->NdisPacket, NULL, NULL, NULL, &Length);
+        Length -= MaxLLHeaderSize;
+        CharBuffer = exAllocatePool(NonPagedPool, Length);
+        Length = CopyPacketToBuffer(CharBuffer, IPPacket->NdisPacket, MaxLLHeaderSize, Length);
+        DisplayIPHeader(CharBuffer, Length);
+        exFreePool(CharBuffer);
+    } else {
+        CharBuffer = IPPacket->Header;
+        Length = IPPacket->ContigSize;
+        DisplayIPHeader(CharBuffer, Length);
+    }
+}
+
+
+static VOID DisplayTCPHeader(
+    PCHAR Header,
+    UINT Length)
+{
+    /* FIXME: IPv4 only */
+    PIPv4_HEADER IPHeader = (PIPv4_HEADER)Header;
+    PTCPv4_HEADER TCPHeader;
+
+    if (IPHeader->Protocol != IPPROTO_TCP) {
+        DbgPrint("This is not a TCP datagram. Protocol is %d\n", IPHeader->Protocol);
+        return;
+    }
+
+    TCPHeader = (PTCPv4_HEADER)((PCHAR)IPHeader + (IPHeader->VerIHL & 0x0F) * 4);
+
+    DbgPrint("TCP header:\n");
+    DbgPrint("  SourcePort: %d\n", WN2H(TCPHeader->SourcePort));
+    DbgPrint("  DestinationPort: %d\n", WN2H(TCPHeader->DestinationPort));
+    DbgPrint("  SequenceNumber: 0x%x\n", DN2H(TCPHeader->SequenceNumber));
+    DbgPrint("  AckNumber: 0x%x\n", DN2H(TCPHeader->AckNumber));
+    DbgPrint("  DataOffset: 0x%x (0x%x) 32-bit words\n", TCPHeader->DataOffset, TCPHeader->DataOffset >> 4);
+    DbgPrint("  Flags: 0x%x (0x%x)\n", TCPHeader->Flags, TCPHeader->Flags & 0x3F);
+    if ((TCPHeader->Flags & TCP_URG) > 0) DbgPrint("    TCP_URG - Urgent Pointer field significant\n");
+    if ((TCPHeader->Flags & TCP_ACK) > 0) DbgPrint("    TCP_ACK - Acknowledgement field significant\n");
+    if ((TCPHeader->Flags & TCP_PSH) > 0) DbgPrint("    TCP_PSH - Push Function\n");
+    if ((TCPHeader->Flags & TCP_RST) > 0) DbgPrint("    TCP_RST - Reset the connection\n");
+    if ((TCPHeader->Flags & TCP_SYN) > 0) DbgPrint("    TCP_SYN - Synchronize sequence numbers\n");
+    if ((TCPHeader->Flags & TCP_FIN) > 0) DbgPrint("    TCP_FIN - No more data from sender\n");
+    DbgPrint("  Window: 0x%x\n", WN2H(TCPHeader->Window));
+    DbgPrint("  Checksum: 0x%x\n", WN2H(TCPHeader->Checksum));
+    DbgPrint("  Urgent: 0x%x\n", WN2H(TCPHeader->Urgent));
+}
+
+
+VOID DisplayTCPPacket(
+    PIP_PACKET IPPacket)
+{
+    UINT Length;
+    PCHAR Buffer;
+
+    if ((DebugTraceLevel & (DEBUG_PBUFFER | DEBUG_TCP)) != (DEBUG_PBUFFER | DEBUG_TCP)) {
+        return;
+    }
+
+    if (!IPPacket) {
+        TI_DbgPrint(MIN_TRACE, ("Cannot display null packet.\n"));
+        return;
+    }
+
+    DisplayIPPacket(IPPacket);
+
+         TI_DbgPrint(MIN_TRACE, ("IPPacket is at (0x%X).\n", IPPacket));
+    TI_DbgPrint(MIN_TRACE, ("Header buffer is at (0x%X).\n", IPPacket->Header));
+    TI_DbgPrint(MIN_TRACE, ("Header size is (%d).\n", IPPacket->HeaderSize));
+    TI_DbgPrint(MIN_TRACE, ("TotalSize (%d).\n", IPPacket->TotalSize));
+    TI_DbgPrint(MIN_TRACE, ("ContigSize (%d).\n", IPPacket->ContigSize));
+    TI_DbgPrint(MIN_TRACE, ("NdisPacket (0x%X).\n", IPPacket->NdisPacket));
+
+    if (IPPacket->NdisPacket) {
+        NdisQueryPacket(IPPacket->NdisPacket, NULL, NULL, NULL, &Length);
+        Length -= MaxLLHeaderSize;
+        Buffer = exAllocatePool(NonPagedPool, Length);
+        Length = CopyPacketToBuffer(Buffer, IPPacket->NdisPacket, MaxLLHeaderSize, Length);
+        DisplayTCPHeader(Buffer, Length);
+        exFreePool(Buffer);
+    } else {
+        Buffer = IPPacket->Header;
+        Length = IPPacket->ContigSize;
+        DisplayTCPHeader(Buffer, Length);
+    }
+}
+
+#endif/* DBG */
+
+void GetDataPtr( PNDIS_PACKET Packet,
+                UINT Offset, 
+                PCHAR *DataOut,
+                PUINT Size ) {
+    PNDIS_BUFFER Buffer;
+
+    NdisQueryPacket(Packet, NULL, NULL, &Buffer, NULL);
+    if( !Buffer ) return;
+    SkipToOffset( Buffer, Offset, DataOut, Size );
+}
+
+NDIS_STATUS AllocatePacketWithBufferX( PNDIS_PACKET *NdisPacket,
+                                      PCHAR Data, UINT Len,
+                                      PCHAR File, UINT Line ) {
+    PNDIS_PACKET Packet;
+    PNDIS_BUFFER Buffer;
+    NDIS_STATUS Status;
+    PCHAR NewData;
+
+    NewData = ExAllocatePool( NonPagedPool, Len );
+    if( !NewData ) return NDIS_STATUS_NOT_ACCEPTED; // XXX 
+    TrackWithTag(EXALLOC_TAG, NewData, File, Line);
+
+    if( Data ) 
+       RtlCopyMemory(NewData, Data, Len);
+
+    NdisAllocatePacket( &Status, &Packet, GlobalPacketPool );
+    if( Status != NDIS_STATUS_SUCCESS ) {
+       ExFreePool( NewData );
+       return Status;
+    }
+    TrackWithTag(NDIS_PACKET_TAG, Packet, File, Line);
+
+    NdisAllocateBuffer( &Status, &Buffer, GlobalBufferPool, NewData, Len );
+    if( Status != NDIS_STATUS_SUCCESS ) {
+       ExFreePool( NewData );
+       FreeNdisPacket( Packet );
+    }
+    TrackWithTag(NDIS_BUFFER_TAG, Buffer, File, Line);
+
+    NdisChainBufferAtFront( Packet, Buffer );
+    *NdisPacket = Packet;
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+
+VOID FreeNdisPacketX
+( PNDIS_PACKET Packet,
+  PCHAR File,
+  UINT Line )
+/*
+ * FUNCTION: Frees an NDIS packet
+ * ARGUMENTS:
+ *     Packet = Pointer to NDIS packet to be freed
+ */
+{
+    PNDIS_BUFFER Buffer, NextBuffer;
+
+    TI_DbgPrint(DEBUG_PBUFFER, ("Packet (0x%X)\n", Packet));
+
+    /* Free all the buffers in the packet first */
+    NdisQueryPacket(Packet, NULL, NULL, &Buffer, NULL);
+    for (; Buffer != NULL; Buffer = NextBuffer) {
+        PVOID Data;
+        UINT Length;
+
+        NdisGetNextBuffer(Buffer, &NextBuffer);
+        NdisQueryBuffer(Buffer, &Data, &Length);
+       UntrackFL(File,Line,Buffer);
+        NdisFreeBuffer(Buffer);
+       UntrackFL(File,Line,Data);
+        ExFreePool(Data);
+    }
+
+    /* Finally free the NDIS packet discriptor */
+    UntrackFL(File,Line,Packet);
+    NdisFreePacket(Packet);
+}
diff --git a/reactos/drivers/lib/ip/network/transmit.c b/reactos/drivers/lib/ip/network/transmit.c
new file mode 100644 (file)
index 0000000..2c2fd4e
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        network/transmit.c
+ * PURPOSE:     Internet Protocol transmit routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+BOOLEAN PrepareNextFragment(
+    PIPFRAGMENT_CONTEXT IFC)
+/*
+ * FUNCTION: Prepares the next fragment of an IP datagram for transmission
+ * ARGUMENTS:
+ *     IFC = Pointer to IP fragment context
+ * RETURNS:
+ *     TRUE if a fragment was prepared for transmission, FALSE if
+ *     there are no more fragments to send
+ */
+{
+    UINT MaxData;
+    UINT DataSize;
+    PIPv4_HEADER Header;
+    BOOLEAN MoreFragments;
+    USHORT FragOfs;
+
+    TI_DbgPrint(MAX_TRACE, ("Called. IFC (0x%X)\n", IFC));
+
+    if (IFC->BytesLeft != 0) {
+
+        TI_DbgPrint(MAX_TRACE, ("Preparing 1 fragment.\n"));
+
+        MaxData  = IFC->PathMTU - IFC->HeaderSize;
+        /* Make fragment a multiplum of 64bit */
+        MaxData -= MaxData % 8;
+        if (IFC->BytesLeft > MaxData) {
+            DataSize      = MaxData;
+            MoreFragments = TRUE;
+        } else {
+            DataSize      = IFC->BytesLeft;
+            MoreFragments = FALSE;
+        }
+
+        RtlCopyMemory(IFC->Data, IFC->DatagramData, DataSize);
+
+        FragOfs = (USHORT)IFC->Position; // Swap?
+        if (MoreFragments)
+            FragOfs |= IPv4_MF_MASK;
+        else
+            FragOfs &= ~IPv4_MF_MASK;
+
+        Header = IFC->Header;
+        Header->FlagsFragOfs = FragOfs;
+
+        /* FIXME: Handle options */
+
+        /* Calculate checksum of IP header */
+        Header->Checksum = 0;
+        Header->Checksum = (USHORT)IPv4Checksum(Header, IFC->HeaderSize, 0);
+       TI_DbgPrint(MID_TRACE,("IP Check: %x\n", Header->Checksum));
+
+        /* Update pointers */
+        IFC->DatagramData = (PVOID)((ULONG_PTR)IFC->DatagramData + DataSize);
+        IFC->Position  += DataSize;
+        IFC->BytesLeft -= DataSize;
+
+        return TRUE;
+    } else {
+        TI_DbgPrint(MAX_TRACE, ("No more fragments.\n"));
+        return FALSE;
+    }
+}
+
+
+NTSTATUS SendFragments(
+    PIP_PACKET IPPacket,
+    PNEIGHBOR_CACHE_ENTRY NCE,
+    UINT PathMTU)
+/*
+ * FUNCTION: Fragments and sends the first fragment of an IP datagram
+ * ARGUMENTS:
+ *     IPPacket  = Pointer to an IP packet
+ *     NCE       = Pointer to NCE for first hop to destination
+ *     PathMTU   = Size of Maximum Transmission Unit of path
+ * RETURNS:
+ *     Status of operation
+ * NOTES:
+ *     IP datagram is larger than PathMTU when this is called
+ */
+{
+    PIPFRAGMENT_CONTEXT IFC;
+    NDIS_STATUS NdisStatus;
+    PVOID Data;
+
+    TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X)  NCE (0x%X)  PathMTU (%d).\n",
+        IPPacket, NCE, PathMTU));
+
+    IFC = exAllocatePool(NonPagedPool, sizeof(IPFRAGMENT_CONTEXT));
+    if (IFC == NULL)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* We allocate a buffer for a PathMTU sized packet and reuse
+       it for all fragments */
+    Data = exAllocatePool(NonPagedPool, MaxLLHeaderSize + PathMTU);
+    if (Data == NULL) {
+        exFreePool(IFC);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Allocate NDIS packet */
+    NdisAllocatePacket(&NdisStatus, &IFC->NdisPacket, GlobalPacketPool);
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        exFreePool(Data);
+        exFreePool(IFC);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Allocate NDIS buffer */
+    NdisAllocateBuffer(&NdisStatus, &IFC->NdisBuffer,
+        GlobalBufferPool, Data, MaxLLHeaderSize + PathMTU);
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+        FreeNdisPacket(IFC->NdisPacket);
+        exFreePool(Data);
+        exFreePool(IFC);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Link NDIS buffer into packet */
+    NdisChainBufferAtFront(IFC->NdisPacket, IFC->NdisBuffer);
+
+    IFC->Header       = (PVOID)((ULONG_PTR)Data + MaxLLHeaderSize);
+    IFC->Datagram     = IPPacket->NdisPacket;
+    IFC->DatagramData = IPPacket->Header;
+    IFC->HeaderSize   = IPPacket->HeaderSize;
+    IFC->PathMTU      = PathMTU;
+    IFC->NCE          = NCE;
+    IFC->Position     = 0;
+    IFC->BytesLeft    = IPPacket->TotalSize - IPPacket->HeaderSize;
+    IFC->Data         = (PVOID)((ULONG_PTR)IFC->Header + IPPacket->HeaderSize);
+
+    PC(IFC->NdisPacket)->DLComplete = IPSendComplete;
+    /* Set upper layer completion function to NULL to indicate that
+       this packet is an IP datagram fragment and thus we should
+       check for more fragments to send. If this is NULL the
+       Context field is a pointer to an IPFRAGMENT_CONTEXT structure */
+    PC(IFC->NdisPacket)->Complete = NULL;
+    PC(IFC->NdisPacket)->Context  = IFC;
+
+    /* Copy IP datagram header to fragment buffer */
+    RtlCopyMemory(IFC->Header, IPPacket->Header, IPPacket->HeaderSize);
+
+    /* Prepare next fragment for transmission and send it */
+
+    PrepareNextFragment(IFC);
+
+    IPSendFragment(IFC->NdisPacket, NCE);
+
+    return STATUS_SUCCESS;
+}
+
+
+VOID IPSendComplete(
+    PVOID Context,
+    PNDIS_PACKET NdisPacket,
+    NDIS_STATUS NdisStatus)
+/*
+ * FUNCTION: IP datagram fragment send completion handler
+ * ARGUMENTS:
+ *     Context    = Pointer to context information (IP_INTERFACE)
+ *     Packet     = Pointer to NDIS packet that was sent
+ *     NdisStatus = NDIS status of operation
+ * NOTES:
+ *    This routine is called when an IP datagram fragment has been sent
+ */
+{
+    TI_DbgPrint(MAX_TRACE, ("Called. Context (0x%X)  NdisPacket (0x%X)  NdisStatus (0x%X)\n",
+        Context, NdisPacket, NdisStatus));
+
+    /* FIXME: Stop sending fragments and cleanup datagram buffers if
+       there was an error */
+
+    if (PC(NdisPacket) && PC(NdisPacket)->Complete)
+        /* This datagram was only one fragment long so call completion handler now */
+        (*PC(NdisPacket)->Complete)(PC(NdisPacket)->Context, NdisPacket, NdisStatus);
+    else {
+        /* This was one of many fragments of an IP datagram. Prepare
+           next fragment and send it or if there are no more fragments,
+           call upper layer completion routine */
+
+        PIPFRAGMENT_CONTEXT IFC = (PIPFRAGMENT_CONTEXT)PC(NdisPacket)->Context;
+
+        if (PrepareNextFragment(IFC)) {
+            /* A fragment was prepared for transmission, so send it */
+            IPSendFragment(IFC->NdisPacket, IFC->NCE);
+        } else {
+            TI_DbgPrint(MAX_TRACE, ("Calling completion handler.\n"));
+
+            /* There are no more fragments to transmit, so call completion handler */
+            NdisPacket = IFC->Datagram;
+            FreeNdisPacket(IFC->NdisPacket);
+            exFreePool(IFC);
+            (*PC(NdisPacket)->Complete)
+               (PC(NdisPacket)->Context, 
+                NdisPacket, 
+                NdisStatus);
+        }
+    }
+}
+
+
+NTSTATUS IPSendFragment(
+    PNDIS_PACKET NdisPacket,
+    PNEIGHBOR_CACHE_ENTRY NCE)
+/*
+ * FUNCTION: Sends an IP datagram fragment to a neighbor
+ * ARGUMENTS:
+ *     NdisPacket = Pointer to an NDIS packet containing fragment
+ *     NCE        = Pointer to NCE for first hop to destination
+ * RETURNS:
+ *     Status of operation
+ * NOTES:
+ *     Lowest level IP send routine
+ */
+{
+    TI_DbgPrint(MAX_TRACE, ("Called. NdisPacket (0x%X)  NCE (0x%X).\n", NdisPacket, NCE));
+
+    TI_DbgPrint(MAX_TRACE, ("NCE->State = %d.\n", NCE->State));
+
+    PC(NdisPacket)->DLComplete = IPSendComplete;
+
+    switch (NCE->State) {
+    case NUD_PERMANENT:
+        /* Neighbor is always valid */
+        break;
+
+    case NUD_REACHABLE:
+        /* Neighbor is reachable */
+        
+        /* FIXME: Set reachable timer */
+
+        break;
+
+    case NUD_STALE:
+        /* Enter delay state and send packet */
+
+        /* FIXME: Enter delay state */
+
+        break;
+
+    case NUD_DELAY:
+    case NUD_PROBE:
+        /* In these states we send the packet and hope the neighbor
+           hasn't changed hardware address */
+        break;
+
+    case NUD_INCOMPLETE:
+        TI_DbgPrint(MAX_TRACE, ("Queueing packet.\n"));
+
+        /* We don't know the hardware address of the first hop to
+           the destination. Queue the packet on the NCE and return */
+        NBQueuePacket(NCE, NdisPacket);
+
+        return STATUS_SUCCESS;
+    default:
+        /* Should not happen */
+        TI_DbgPrint(MIN_TRACE, ("Unknown NCE state.\n"));
+
+        return STATUS_SUCCESS;
+    }
+
+    (*NCE->Interface->Transmit)(NCE->Interface->Context,
+                                NdisPacket,
+                                MaxLLHeaderSize,
+                                NCE->LinkAddress,
+                                LAN_PROTO_IPv4);
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS IPSendDatagram(
+    PIP_PACKET IPPacket,
+    PROUTE_CACHE_NODE RCN)
+/*
+ * FUNCTION: Sends an IP datagram to a remote address
+ * ARGUMENTS:
+ *     IPPacket = Pointer to an IP packet
+ *     RCN      = Pointer to route cache node
+ * RETURNS:
+ *     Status of operation
+ * NOTES:
+ *     This is the highest level IP send routine. It possibly breaks the packet
+ *     into two or more fragments before passing it on to the next lower level
+ *     send routine (IPSendFragment)
+ */
+{
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    UINT PathMTU;
+
+    TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X)  RCN (0x%X)\n", IPPacket, RCN));
+
+    DISPLAY_IP_PACKET(IPPacket);
+    /*OskitDumpBuffer( IPPacket->Header, IPPacket->TotalSize );*/
+
+    NCE = RCN->NCE;
+
+#ifdef DBG
+    if (!NCE) {
+        TI_DbgPrint(MIN_TRACE, ("No NCE to use.\n"));
+        FreeNdisPacket(IPPacket->NdisPacket);
+        return STATUS_SUCCESS;
+    }
+#endif
+
+    /* Fetch path MTU now, because it may change */
+    PathMTU = RCN->PathMTU;
+    TI_DbgPrint(MID_TRACE,("PathMTU: %d\n", PathMTU));
+
+    if (IPPacket->TotalSize > PathMTU) {
+       TI_DbgPrint(MID_TRACE,("Doing SendFragments\n"));
+        return SendFragments(IPPacket, NCE, PathMTU);
+    } else {
+        if ((IPPacket->Flags & IP_PACKET_FLAG_RAW) == 0) {
+            /* Calculate checksum of IP header */
+           TI_DbgPrint(MID_TRACE,("-> not IP_PACKET_FLAG_RAW\n"));
+            ((PIPv4_HEADER)IPPacket->Header)->Checksum = 0;
+
+            ((PIPv4_HEADER)IPPacket->Header)->Checksum = (USHORT)
+                IPv4Checksum(IPPacket->Header, IPPacket->HeaderSize, 0);
+           TI_DbgPrint(MID_TRACE,("IP Check: %x\n", ((PIPv4_HEADER)IPPacket->Header)->Checksum));
+
+            TI_DbgPrint(MAX_TRACE, ("Sending packet (length is %d).\n",
+                WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength)));
+        } else {
+            TI_DbgPrint(MAX_TRACE, ("Sending raw packet (flags are 0x%X).\n",
+              IPPacket->Flags));
+        }
+
+        return IPSendFragment(IPPacket->NdisPacket, NCE);
+    }
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/transport/datagram/datagram.c b/reactos/drivers/lib/ip/transport/datagram/datagram.c
new file mode 100644 (file)
index 0000000..2223a39
--- /dev/null
@@ -0,0 +1,705 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        transport/datagram/datagram.c
+ * PURPOSE:     Routines for sending and receiving datagrams
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+/* Pending request queue */
+LIST_ENTRY DGPendingListHead;
+KSPIN_LOCK DGPendingListLock;
+/* Work queue item for pending requests */
+WORK_QUEUE_ITEM DGWorkItem;
+
+
+VOID STDCALL DatagramWorker(
+  PVOID Context)
+/*
+ * FUNCTION: Handles pending send requests
+ * ARGUMENTS:
+ *     Context = Pointer to context information (unused)
+ * NOTES:
+ *     This routine is called after the driver has run out of resources.
+ *     It processes send requests or shedules them to be processed
+ */
+{
+  PLIST_ENTRY CurrentADFEntry;
+  PLIST_ENTRY CurrentSREntry;
+  PADDRESS_FILE CurrentADF;
+  PDATAGRAM_SEND_REQUEST CurrentSR;
+  KIRQL OldIrql1;
+  KIRQL OldIrql2;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  KeAcquireSpinLock(&DGPendingListLock, &OldIrql1);
+
+  CurrentADFEntry = DGPendingListHead.Flink;
+  while (CurrentADFEntry != &DGPendingListHead)
+    {
+      RemoveEntryList(CurrentADFEntry);
+      CurrentADF = CONTAINING_RECORD(CurrentADFEntry,
+        ADDRESS_FILE,
+        ListEntry);
+
+      KeAcquireSpinLock(&CurrentADF->Lock, &OldIrql2);
+
+      if (AF_IS_BUSY(CurrentADF))
+        {
+          /* The send worker function is already running so we just
+             set the pending send flag on the address file object */
+
+          AF_SET_PENDING(CurrentADF, AFF_SEND);
+          KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
+        }
+      else
+        {
+          if (!IsListEmpty(&CurrentADF->TransmitQueue))
+            {
+              /* The transmit queue is not empty. Dequeue a send
+                 request and process it */
+
+              CurrentSREntry = RemoveHeadList(&CurrentADF->TransmitQueue);
+              CurrentSR = CONTAINING_RECORD(CurrentADFEntry,
+                DATAGRAM_SEND_REQUEST,
+                ListEntry);
+
+              KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
+
+              DGSend(CurrentADF, CurrentSR);
+            }
+          else
+            {
+              KeReleaseSpinLock(&CurrentADF->Lock, OldIrql2);
+            }
+        }
+      CurrentADFEntry = CurrentADFEntry->Flink;
+    }
+
+  KeReleaseSpinLock(&DGPendingListLock, OldIrql1);
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+VOID SendDatagramComplete(
+  PVOID Context,
+  PNDIS_PACKET Packet,
+  NDIS_STATUS NdisStatus)
+/*
+ * FUNCTION: Datagram transmit completion handler
+ * ARGUMENTS:
+ *     Context    = Pointer to context infomation (DATAGRAM_SEND_REQUEST)
+ *     Packet     = Pointer to NDIS packet
+ *     NdisStatus = Status of transmit operation
+ * NOTES:
+ *     This routine is called by IP when a datagram send completes.
+ *     We shedule the out-of-resource worker function if there
+ *     are pending address files in the queue
+ */
+{
+  KIRQL OldIrql;
+  ULONG BytesSent;
+  PVOID CompleteContext;
+  PDATAGRAM_SEND_REQUEST SendRequest;
+  DATAGRAM_COMPLETION_ROUTINE Complete;
+  BOOLEAN QueueWorkItem;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  SendRequest     = (PDATAGRAM_SEND_REQUEST)Context;
+  Complete        = SendRequest->Complete;
+  CompleteContext = SendRequest->Context;
+  BytesSent       = SendRequest->BufferSize;
+
+  /* If there are pending send requests, shedule worker function */
+  KeAcquireSpinLock(&DGPendingListLock, &OldIrql);
+  QueueWorkItem = (!IsListEmpty(&DGPendingListHead));
+  KeReleaseSpinLock(&DGPendingListLock, OldIrql);
+  if (QueueWorkItem)
+    ExQueueWorkItem(&DGWorkItem, CriticalWorkQueue);
+
+  TI_DbgPrint(MAX_TRACE, ("Calling 0x%X.\n", Complete));
+
+  /* Call completion routine for send request */
+  (*Complete)(Context, NdisStatus, BytesSent);
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+VOID DGSend(
+  PVOID Context,
+  PDATAGRAM_SEND_REQUEST SendRequest)
+/*
+ * FUNCTION: Sends a datagram to IP layer
+ * ARGUMENTS:
+ *     Context     = Pointer to context information (ADDRESS_FILE)
+ *     SendRequest = Pointer to send request
+ */
+{
+  KIRQL OldIrql;
+  NTSTATUS Status;
+  USHORT LocalPort;
+  PIP_PACKET IPPacket;
+  PROUTE_CACHE_NODE RCN;
+  PLIST_ENTRY CurrentEntry;
+  PADDRESS_FILE AddrFile = Context;
+  PADDRESS_ENTRY ADE;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  /* Get the information we need from the address file
+     now so we minimize the time we hold the spin lock */
+  KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+  ASSERT(AddrFile->ADE);
+  LocalPort = AddrFile->Port;
+  ADE       = AddrFile->ADE;
+  ReferenceObject(ADE);
+  KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+
+  /* Loop until there are no more send requests in the
+     transmit queue or until we run out of resources */
+  for (;;)
+  {
+      TI_DbgPrint(MIN_TRACE, ("Looping on DGSend !!!! WHEE!\n"));
+      if (!NT_SUCCESS(Status))
+      {
+          KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+          /* An error occurred, enqueue the send request again and return */
+          InsertHeadList(&AddrFile->TransmitQueue, &SendRequest->ListEntry);
+          DereferenceObject(ADE);
+          KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+         
+          TI_DbgPrint(MIN_TRACE, ("Leaving (insufficient resources).\n"));
+          return;
+      }
+      
+      /* Get a route to the destination address */
+      if (RouteGetRouteToDestination(&SendRequest->RemoteAddress, ADE->NTE, &RCN) == IP_SUCCESS)
+      {
+          /* Set completion routine and send the packet */
+         IPPacket = &SendRequest->Packet;
+          PC(IPPacket->NdisPacket)->Complete = SendDatagramComplete;
+          PC(IPPacket->NdisPacket)->Context  = SendRequest;
+          if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS)
+         {
+             TI_DbgPrint(MIN_TRACE, ("!! Datagram sent !! (completing)\n"));
+             SendDatagramComplete(SendRequest,
+                                  IPPacket->NdisPacket,
+                                  NDIS_STATUS_REQUEST_ABORTED);
+         }
+          /* We're done with the RCN */
+          DereferenceObject(RCN);
+      }
+      else
+      {
+         /* No route to destination */
+         /* FIXME: Which error code should we use here? */
+         TI_DbgPrint(MIN_TRACE, 
+                     ("No route to destination address (0x%X).\n",
+                      SendRequest->RemoteAddress.Address.IPv4Address));
+         SendDatagramComplete(SendRequest,
+                              IPPacket->NdisPacket,
+                              NDIS_STATUS_REQUEST_ABORTED);
+      }
+
+      /* Check transmit queue for more to send */
+      
+      KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+      
+      if (!IsListEmpty(&AddrFile->TransmitQueue))
+      {
+          /* Transmit queue is not empty, process one more request */
+          CurrentEntry = RemoveHeadList(&AddrFile->TransmitQueue);
+          SendRequest  = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
+         
+          KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+         TI_DbgPrint(MIN_TRACE, ("List is not empty\n"));
+      }
+      else
+      {
+          /* Transmit queue is empty */
+          AF_CLR_PENDING(AddrFile, AFF_SEND);
+          DereferenceObject(ADE);
+          KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+         
+          TI_DbgPrint(MAX_TRACE, ("Leaving (empty queue).\n"));
+          return;
+      }
+  }
+}
+
+
+VOID DGDeliverData(
+  PADDRESS_FILE AddrFile,
+  PIP_ADDRESS Address,
+  PIP_PACKET IPPacket,
+  UINT DataSize)
+/*
+ * FUNCTION: Delivers datagram data to a user
+ * ARGUMENTS:
+ *     AddrFile = Address file to deliver data to
+ *     Address  = Remote address the packet came from
+ *     IPPacket = Pointer to IP packet to deliver
+ *     DataSize = Number of bytes in data area
+ *                (incl. IP header for raw IP file objects)
+ * NOTES:
+ *     If there is a receive request, then we copy the data to the
+ *     buffer supplied by the user and complete the receive request.
+ *     If no suitable receive request exists, then we call the event
+ *     handler if it exists, otherwise we drop the packet.
+ */
+{
+  KIRQL OldIrql;
+  PTDI_IND_RECEIVE_DATAGRAM ReceiveHandler;
+  PVOID HandlerContext;
+  LONG AddressLength;
+  PVOID SourceAddress;
+  ULONG BytesTaken;
+  NTSTATUS Status;
+  PVOID DataBuffer;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+
+  if (AddrFile->Protocol == IPPROTO_UDP)
+    {
+      DataBuffer = IPPacket->Data;
+    }
+  else
+    {
+      /* Give client the IP header too if it is a raw IP file object */
+      DataBuffer = IPPacket->Header;
+    }
+
+  if (!IsListEmpty(&AddrFile->ReceiveQueue))
+    {
+      PLIST_ENTRY CurrentEntry;
+      PDATAGRAM_RECEIVE_REQUEST Current;
+      BOOLEAN Found;
+  
+      TI_DbgPrint(MAX_TRACE, ("There is a receive request.\n"));
+  
+      /* Search receive request list to find a match */
+      Found = FALSE;
+      CurrentEntry = AddrFile->ReceiveQueue.Flink;
+      while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found))
+        {
+          Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
+         if (AddrIsEqual(Address, &Current->RemoteAddress))
+            Found = TRUE;
+    
+          if (Found)
+            {
+              /* FIXME: Maybe we should check if the buffer of this
+                 receive request is large enough and if not, search
+                 for another */
+    
+              /* Remove the request from the queue */
+              RemoveEntryList(&Current->ListEntry);
+              AddrFile->RefCount--;
+              break;
+            }
+          CurrentEntry = CurrentEntry->Flink;
+        }
+
+      KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+  
+      if (Found)
+        {
+          TI_DbgPrint(MAX_TRACE, ("Suitable receive request found.\n"));
+    
+          /* Copy the data into buffer provided by the user */
+          CopyBufferToBufferChain(Current->Buffer,
+            0,
+            DataBuffer,
+            DataSize);
+  
+          /* Complete the receive request */
+          (*Current->Complete)(Current->Context, STATUS_SUCCESS, DataSize);
+    
+          exFreePool(Current);
+        }
+    }
+  else if (AddrFile->RegisteredReceiveDatagramHandler)
+    {
+      TI_DbgPrint(MAX_TRACE, ("Calling receive event handler.\n"));
+
+      ReceiveHandler = AddrFile->ReceiveDatagramHandler;
+      HandlerContext = AddrFile->ReceiveDatagramHandlerContext;
+
+      KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+
+      if (Address->Type == IP_ADDRESS_V4)
+        {
+          AddressLength = sizeof(IPv4_RAW_ADDRESS);
+          SourceAddress = &Address->Address.IPv4Address;
+        }
+      else /* (Address->Type == IP_ADDRESS_V6) */
+        {
+          AddressLength = sizeof(IPv6_RAW_ADDRESS);
+          SourceAddress = Address->Address.IPv6Address;
+        }
+
+      Status = (*ReceiveHandler)(HandlerContext,
+        AddressLength,
+        SourceAddress,
+        0,
+        NULL,
+        TDI_RECEIVE_ENTIRE_MESSAGE,
+        DataSize,
+        DataSize,
+        &BytesTaken,
+        DataBuffer,
+        NULL);
+    }
+  else
+    {
+      TI_DbgPrint(MAX_TRACE, ("Discarding datagram.\n"));
+    }
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+VOID DGCancelSendRequest(
+  PADDRESS_FILE AddrFile,
+  PVOID Context)
+/*
+ * FUNCTION: Cancels a datagram send request
+ * ARGUMENTS:
+ *     AddrFile = Pointer to address file of the request
+ *     Context  = Pointer to context information for completion handler
+ */
+{
+  KIRQL OldIrql;
+  PLIST_ENTRY CurrentEntry;
+  PDATAGRAM_SEND_REQUEST Current = NULL;
+  BOOLEAN Found = FALSE;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+
+  /* Search the request list for the specified request and remove it */
+  CurrentEntry = AddrFile->TransmitQueue.Flink;
+  while ((CurrentEntry != &AddrFile->TransmitQueue) && (!Found))
+    {
+         Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry);
+      if (Context == Current->Context)
+        {
+          /* We've found the request, now remove it from the queue */
+          RemoveEntryList(CurrentEntry);
+          AddrFile->RefCount--;
+          Found = TRUE;
+          break;
+        }
+      CurrentEntry = CurrentEntry->Flink;
+    }
+
+  KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+
+  if (Found)
+    {
+      /* Complete the request and free its resources */
+      (*Current->Complete)(Current->Context, STATUS_CANCELLED, 0);
+      exFreePool(Current);
+    }
+  else
+    {
+      TI_DbgPrint(MID_TRACE, ("Cannot find send request.\n"));
+    }
+}
+
+
+VOID DGCancelReceiveRequest(
+  PADDRESS_FILE AddrFile,
+  PVOID Context)
+/*
+ * FUNCTION: Cancels a datagram receive request
+ * ARGUMENTS:
+ *     AddrFile = Pointer to address file of the request
+ *     Context  = Pointer to context information for completion handler
+ */
+{
+  KIRQL OldIrql;
+  PLIST_ENTRY CurrentEntry;
+  PDATAGRAM_RECEIVE_REQUEST Current = NULL;
+  BOOLEAN Found = FALSE;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+
+  /* Search the request list for the specified request and remove it */
+  CurrentEntry = AddrFile->ReceiveQueue.Flink;
+  while ((CurrentEntry != &AddrFile->ReceiveQueue) && (!Found))
+    {
+         Current = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry);
+      if (Context == Current->Context)
+        {
+          /* We've found the request, now remove it from the queue */
+          RemoveEntryList(CurrentEntry);
+          AddrFile->RefCount--;
+          Found = TRUE;
+          break;
+        }
+      CurrentEntry = CurrentEntry->Flink;
+    }
+
+  KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+
+  if (Found)
+    {
+      /* Complete the request and free its resources */
+      (*Current->Complete)(Current->Context, STATUS_CANCELLED, 0);
+
+      exFreePool(Current);
+    }
+  else
+    {
+      TI_DbgPrint(MID_TRACE, ("Cannot find receive request.\n"));
+    }
+}
+
+
+NTSTATUS DGTransmit(
+  PADDRESS_FILE AddressFile,
+  PDATAGRAM_SEND_REQUEST SendRequest)
+/*
+ * FUNCTION: Transmits or queues a send request
+ * ARGUMENTS:
+ *     AddressFile = Pointer to address file
+ *     SendRequest = Pointer to send request
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  KIRQL OldIrql;
+
+  KeAcquireSpinLock(&AddressFile->Lock, &OldIrql);
+  if (AF_IS_BUSY(AddressFile))
+    {
+      /* Queue send request on the transmit queue */
+      InsertTailList(&AddressFile->TransmitQueue, &SendRequest->ListEntry);
+      /* Reference address file and set pending send request flag */
+      ReferenceObject(AddressFile);
+      AF_SET_PENDING(AddressFile, AFF_SEND);
+      KeReleaseSpinLock(&AddressFile->Lock, OldIrql);
+      TI_DbgPrint(MAX_TRACE, ("Leaving (queued).\n"));
+    }
+  else
+    {
+      KeReleaseSpinLock(&AddressFile->Lock, OldIrql);
+      /* Send the datagram */
+      DGSend(AddressFile, SendRequest);
+      TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n")); 
+    }
+  return STATUS_PENDING;
+}
+
+NTSTATUS DGSendDatagram( PTDI_REQUEST Request,
+                        PTDI_CONNECTION_INFORMATION ConnInfo,
+                        PIP_PACKET Packet ) {
+/*
+ * FUNCTION: Sends a datagram to a remote address
+ * ARGUMENTS:
+ *     Request   = Pointer to TDI request
+ *     ConnInfo  = Pointer to connection information
+ *     Packet    = Pointer to NDIS buffer with data
+ * RETURNS:
+ *     Status of operation
+ */
+    PADDRESS_FILE AddrFile;
+    KIRQL OldIrql;
+    NTSTATUS Status;
+    PDATAGRAM_SEND_REQUEST SendRequest;
+    
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+    
+    AddrFile = Request->Handle.AddressHandle;
+    
+    KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+    
+    if (AF_IS_VALID(AddrFile)) {
+       /* Initialize a send request */
+       SendRequest = exAllocatePool( NonPagedPool,
+                                     sizeof( DATAGRAM_SEND_REQUEST ) );
+       
+       if( SendRequest ) {
+           KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+           return STATUS_INSUFFICIENT_RESOURCES;
+       }
+       
+       SendRequest->Complete = Request->RequestNotifyObject;
+       SendRequest->Context = Request->RequestContext;
+       NdisQueryPacketLength( Packet->NdisPacket,
+                              &SendRequest->BufferSize );
+       SendRequest->Packet = *Packet;
+       
+       if (NT_SUCCESS(Status)) {
+           Status = AddrGetAddress(ConnInfo->RemoteAddress,
+                                   &SendRequest->RemoteAddress,
+                                   &SendRequest->RemotePort);
+           if (NT_SUCCESS(Status))
+           {
+               KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+               return DGTransmit(AddrFile, SendRequest);
+           }
+           else
+           {
+               exFreePool(SendRequest);
+           }
+       }
+       else
+       {
+           Status = STATUS_INSUFFICIENT_RESOURCES;
+       }
+    }
+    else
+    {
+       Status = STATUS_ADDRESS_CLOSED;
+    }
+    
+    KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+    
+    TI_DbgPrint(MAX_TRACE, ("Leaving. Status (0x%X)\n", Status));
+    
+    return Status;
+}
+
+
+NTSTATUS DGReceiveDatagram(
+  PTDI_REQUEST Request,
+  PTDI_CONNECTION_INFORMATION ConnInfo,
+  PNDIS_BUFFER Buffer,
+  ULONG ReceiveLength,
+  ULONG ReceiveFlags,
+  PTDI_CONNECTION_INFORMATION ReturnInfo,
+  PULONG BytesReceived)
+/*
+ * FUNCTION: Attempts to receive a datagram from a remote address
+ * ARGUMENTS:
+ *     Request       = Pointer to TDI request
+ *     ConnInfo      = Pointer to connection information
+ *     Buffer        = Pointer to NDIS buffer chain to store received data
+ *     ReceiveLength = Maximum size to use of buffer (0 if all can be used)
+ *     ReceiveFlags  = Receive flags (None, Normal, Peek)
+ *     ReturnInfo    = Pointer to structure for return information
+ *     BytesReceive  = Pointer to structure for number of bytes received
+ * RETURNS:
+ *     Status of operation
+ * NOTES:
+ *     This is the high level interface for receiving datagrams
+ */
+{
+  PADDRESS_FILE AddrFile;
+  KIRQL OldIrql;
+  NTSTATUS Status;
+  PDATAGRAM_RECEIVE_REQUEST ReceiveRequest;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  AddrFile = Request->Handle.AddressHandle;
+
+  KeAcquireSpinLock(&AddrFile->Lock, &OldIrql);
+
+  if (AF_IS_VALID(AddrFile))
+    {
+      ReceiveRequest = exAllocatePool(NonPagedPool, sizeof(DATAGRAM_RECEIVE_REQUEST));
+      if (ReceiveRequest)
+        {
+          /* Initialize a receive request */
+    
+          /* Extract the remote address filter from the request (if any) */
+          if (((ConnInfo->RemoteAddressLength != 0)) && (ConnInfo->RemoteAddress))
+            {
+              Status = AddrGetAddress(ConnInfo->RemoteAddress,
+                                     &ReceiveRequest->RemoteAddress,
+                                     &ReceiveRequest->RemotePort);
+              if (!NT_SUCCESS(Status))
+                {
+                  KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+                  exFreePool(ReceiveRequest);
+                  return Status;
+                }
+            }
+          else
+            {
+              ReceiveRequest->RemotePort    = 0;
+            }
+          ReceiveRequest->ReturnInfo = ReturnInfo;
+          ReceiveRequest->Buffer = Buffer;
+          /* If ReceiveLength is 0, the whole buffer is available to us */
+          ReceiveRequest->BufferSize = (ReceiveLength == 0) ?
+            MmGetMdlByteCount(Buffer) : ReceiveLength;
+          ReceiveRequest->Complete = Request->RequestNotifyObject;
+          ReceiveRequest->Context = Request->RequestContext;
+    
+          /* Queue receive request */
+          InsertTailList(&AddrFile->ReceiveQueue, &ReceiveRequest->ListEntry);
+    
+          /* Reference address file and set pending receive request flag */
+          AddrFile->RefCount++;
+          AF_SET_PENDING(AddrFile, AFF_RECEIVE);
+    
+          KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+    
+          TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n"));
+    
+          return STATUS_PENDING;
+        }
+      else
+        {
+          Status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+  else
+    {
+      Status = STATUS_INVALID_ADDRESS;
+    }
+
+  KeReleaseSpinLock(&AddrFile->Lock, OldIrql);
+
+  TI_DbgPrint(MAX_TRACE, ("Leaving with errors (0x%X).\n", Status));
+
+  return Status;
+}
+
+
+NTSTATUS DGStartup(
+  VOID)
+/*
+ * FUNCTION: Initializes the datagram subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  InitializeListHead(&DGPendingListHead);
+
+  KeInitializeSpinLock(&DGPendingListLock);
+
+  ExInitializeWorkItem(&DGWorkItem, DatagramWorker, NULL);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS DGShutdown(
+  VOID)
+/*
+ * FUNCTION: Shuts down the datagram subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  return STATUS_SUCCESS;
+}
diff --git a/reactos/drivers/lib/ip/transport/rawip/rawip.c b/reactos/drivers/lib/ip/transport/rawip/rawip.c
new file mode 100644 (file)
index 0000000..e1a49aa
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        transport/rawip/rawip.c
+ * PURPOSE:     Raw IP routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+BOOLEAN RawIPInitialized = FALSE;
+
+
+NTSTATUS BuildRawIPPacket(
+    PIP_PACKET Packet,
+    UINT DataLength,
+    PIP_ADDRESS LocalAddress,
+    USHORT LocalPort )
+/*
+ * FUNCTION: Builds an UDP packet
+ * ARGUMENTS:
+ *     Context      = Pointer to context information (DATAGRAM_SEND_REQUEST)
+ *     LocalAddress = Pointer to our local address (NULL)
+ *     LocalPort    = The port we send this datagram from (0)
+ *     IPPacket     = Address of pointer to IP packet
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    PVOID Header;
+    NDIS_STATUS NdisStatus;
+    PNDIS_BUFFER HeaderBuffer;
+    PNDIS_PACKET NdisPacket = Packet->NdisPacket; 
+    /* Will be zeroed in packet by IPInitializePacket */
+
+    /* Prepare packet */
+    IPInitializePacket(Packet,IP_ADDRESS_V4);
+    Packet->Flags      = IP_PACKET_FLAG_RAW;    /* Don't touch IP header */
+    Packet->TotalSize  = DataLength;
+    Packet->NdisPacket = NdisPacket;
+
+    if (MaxLLHeaderSize != 0) {
+        Header = ExAllocatePool(NonPagedPool, MaxLLHeaderSize);
+        if (!Header) {
+            TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet headers.\n"));
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        TI_DbgPrint(MAX_TRACE, ("Allocated %d bytes for headers at 0x%X.\n",
+            MaxLLHeaderSize, Header));
+
+        /* Allocate NDIS buffer for maximum link level header */
+        NdisAllocateBuffer(&NdisStatus,
+                          &HeaderBuffer,
+                          GlobalBufferPool,
+                          Header,
+                          MaxLLHeaderSize);
+
+        if (NdisStatus != NDIS_STATUS_SUCCESS) {
+            TI_DbgPrint(MIN_TRACE, ("Cannot allocate NDIS buffer for packet headers. NdisStatus = (0x%X)\n", NdisStatus));
+            ExFreePool(Header);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* Chain header at front of packet */
+        NdisChainBufferAtFront(Packet->NdisPacket, HeaderBuffer);
+    }
+
+    DISPLAY_IP_PACKET(Packet);
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS RawIPSendDatagram(
+    PTDI_REQUEST Request,
+    PTDI_CONNECTION_INFORMATION ConnInfo,
+    PNDIS_BUFFER Buffer,
+    ULONG DataSize,
+    PULONG DataUsed )
+/*
+ * FUNCTION: Sends a raw IP datagram to a remote address
+ * ARGUMENTS:
+ *     Request   = Pointer to TDI request
+ *     ConnInfo  = Pointer to connection information
+ *     Buffer    = Pointer to NDIS buffer with data
+ *     DataSize  = Size in bytes of data to be sent
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    NDIS_STATUS Status;
+    PCHAR BufferData;
+    UINT BufferLen;
+    PADDRESS_FILE AddrFile = 
+       (PADDRESS_FILE)Request->Handle.AddressHandle;
+    IP_PACKET Packet;
+
+    NdisQueryBuffer( Buffer, &BufferData, &BufferLen );
+    Status = AllocatePacketWithBuffer( &Packet.NdisPacket,
+                                      BufferData,
+                                      BufferLen );
+    
+    TI_DbgPrint(MID_TRACE,("Packet.NdisPacket %x\n", Packet.NdisPacket));
+
+    *DataUsed = BufferLen;
+
+    if( Status == NDIS_STATUS_SUCCESS )
+       Status = BuildRawIPPacket( &Packet,
+                                  BufferLen,
+                                  AddrFile->ADE->Address,
+                                  AddrFile->Port );
+    if( Status == NDIS_STATUS_SUCCESS ) 
+       Status = DGSendDatagram(Request, ConnInfo, &Packet);
+    else
+       FreeNdisPacket( Packet.NdisPacket );
+
+    /* NdisFreeBuffer( Buffer ); */
+
+    return Status;
+}
+
+
+VOID RawIPReceive(
+    PNET_TABLE_ENTRY NTE,
+    PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Receives and queues a raw IP datagram
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry which the packet was received on
+ *     IPPacket = Pointer to an IP packet that was received
+ * NOTES:
+ *     This is the low level interface for receiving ICMP datagrams.
+ *     It delivers the packet header and data to anyone that wants it
+ *     When we get here the datagram has already passed sanity checks
+ */
+{
+    PIP_ADDRESS DstAddress;
+
+    TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    switch (IPPacket->Type) {
+    /* IPv4 packet */
+    case IP_ADDRESS_V4:
+        DstAddress = &IPPacket->DstAddr;
+        break;
+
+    /* IPv6 packet */
+    case IP_ADDRESS_V6:
+        TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 raw IP datagram (%i bytes).\n",
+          IPPacket->TotalSize));
+
+        /* FIXME: IPv6 is not supported */
+        return;
+
+    default:
+        return;
+    }
+
+    /* Locate a receive request on destination address file object
+       and deliver the packet if one is found. If there is no receive
+       request on the address file object, call the associated receive
+       handler. If no receive handler is registered, drop the packet */
+
+#if 0 /* Decide what to do here */
+    AddrFile = AddrSearchFirst(DstAddress,
+                               0,
+                               IPPROTO_ICMP,
+                               &SearchContext);
+    if (AddrFile) {
+        do {
+            DGDeliverData(AddrFile,
+                          DstAddress,
+                          IPPacket,
+                          IPPacket->TotalSize);
+        } while ((AddrFile = AddrSearchNext(&SearchContext)) != NULL);
+    } else {
+        /* There are no open address files that will take this datagram */
+        /* FIXME: IPv4 only */
+        TI_DbgPrint(MID_TRACE, ("Cannot deliver IPv4 ICMP datagram to address (0x%X).\n",
+            DN2H(DstAddress->Address.IPv4Address)));
+    }
+#endif
+    TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+NTSTATUS RawIPStartup(
+    VOID)
+/*
+ * FUNCTION: Initializes the Raw IP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    RawIPInitialized = TRUE;
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS RawIPShutdown(
+    VOID)
+/*
+ * FUNCTION: Shuts down the Raw IP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    if (!RawIPInitialized)
+        return STATUS_SUCCESS;
+
+    return STATUS_SUCCESS;
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/transport/tcp/event.c b/reactos/drivers/lib/ip/transport/tcp/event.c
new file mode 100644 (file)
index 0000000..f29f5bb
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        transport/tcp/event.c
+ * PURPOSE:     Transmission Control Protocol -- Events from oskittcp
+ * PROGRAMMERS: Art Yerkes
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+extern ULONG TCP_IPIdentification;
+extern LIST_ENTRY SleepingThreadsList;
+extern FAST_MUTEX SleepingThreadsLock;
+
+int TCPSocketState(void *ClientData,
+                  void *WhichSocket, 
+                  void *WhichConnection,
+                  OSK_UINT NewState ) {
+    PCONNECTION_ENDPOINT Connection = WhichConnection;
+    PTCP_COMPLETION_ROUTINE Complete;
+    PTDI_BUCKET Bucket;
+    PLIST_ENTRY Entry;
+
+    TI_DbgPrint(MID_TRACE,("Called: NewState %x (Conn %x)\n", 
+                          NewState, Connection));
+
+    if( !Connection ) {
+       TI_DbgPrint(MID_TRACE,("Socket closing.\n"));
+       return 0;
+    }
+
+    if( (NewState & SEL_CONNECT) && 
+       !(Connection->State & SEL_CONNECT) ) {
+       while( !IsListEmpty( &Connection->ConnectRequest ) ) {
+           Connection->State |= SEL_CONNECT;
+           Entry = RemoveHeadList( &Connection->ConnectRequest );
+           Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
+           Complete = Bucket->Request.RequestNotifyObject;
+           TI_DbgPrint(MID_TRACE,
+                       ("Completing Connect Request %x\n", Bucket->Request));
+           Complete( Bucket->Request.RequestContext, STATUS_SUCCESS, 0 );
+           /* Frees the bucket allocated in TCPConnect */
+           ExFreePool( Bucket );
+       }
+    } else if( (NewState & SEL_READ) || (NewState & SEL_FIN) ) {
+       TI_DbgPrint(MID_TRACE,("Readable (or closed): irp list %s\n",
+                              IsListEmpty(&Connection->ReceiveRequest) ?
+                              "empty" : "nonempty"));
+
+       while( !IsListEmpty( &Connection->ReceiveRequest ) ) {
+           PIRP Irp;
+           OSK_UINT RecvLen = 0, Received = 0;
+           OSK_PCHAR RecvBuffer = 0;
+           PMDL Mdl;
+           NTSTATUS Status;
+
+           Entry = RemoveHeadList( &Connection->ReceiveRequest );
+           Bucket = CONTAINING_RECORD( Entry, TDI_BUCKET, Entry );
+           Complete = Bucket->Request.RequestNotifyObject;
+
+           TI_DbgPrint(MID_TRACE,
+                       ("Readable, Completing read request %x\n", 
+                        Bucket->Request));
+
+           Irp = Bucket->Request.RequestContext;
+           Mdl = Irp->MdlAddress;
+
+           TI_DbgPrint(MID_TRACE,
+                       ("Getting the user buffer from %x\n", Mdl));
+
+           NdisQueryBuffer( Mdl, &RecvBuffer, &RecvLen );
+
+           TI_DbgPrint(MID_TRACE,
+                       ("Reading %d bytes to %x\n", RecvLen, RecvBuffer));
+
+           if( NewState & SEL_FIN && !RecvLen ) {
+               Status = STATUS_END_OF_FILE;
+               Received = 0;
+           } else 
+               Status = TCPTranslateError
+                   ( OskitTCPRecv( Connection->SocketContext,
+                                   RecvBuffer,
+                                   RecvLen,
+                                   &Received,
+                                   0 ) );
+
+           TI_DbgPrint(MID_TRACE,("TCP Bytes: %d\n", Received));
+
+           if( Status == STATUS_SUCCESS && Received != 0 ) {
+               TI_DbgPrint(MID_TRACE,("Received %d bytes with status %x\n",
+                                      Received, Status));
+               
+               TI_DbgPrint(MID_TRACE,
+                           ("Completing Receive Request: %x\n", 
+                            Bucket->Request));
+
+               Complete( Bucket->Request.RequestContext,
+                         STATUS_SUCCESS, Received );
+           } else if( Status == STATUS_PENDING || 
+                      (Status == STATUS_SUCCESS && Received == 0) ) {
+               InsertHeadList( &Connection->ReceiveRequest,
+                               &Bucket->Entry );
+               break;
+           } else {
+               TI_DbgPrint(MID_TRACE,
+                           ("Completing Receive request: %x %x\n",
+                            Bucket->Request, Status));
+               Complete( Bucket->Request.RequestContext, Status, 0 );
+           }
+       }
+    } 
+
+    return 0;
+}
+
+void TCPPacketSendComplete( PVOID Context,
+                           PNDIS_PACKET NdisPacket,
+                           NDIS_STATUS NdisStatus ) {
+    TI_DbgPrint(MID_TRACE,("called\n"));
+}
+
+#define STRINGIFY(x) #x
+
+int TCPPacketSend(void *ClientData, OSK_PCHAR data, OSK_UINT len ) {
+    NTSTATUS Status;
+    KIRQL OldIrql;
+    NDIS_STATUS NdisStatus;
+    ROUTE_CACHE_NODE *RCN;
+    IP_PACKET Packet = { 0 };
+    IP_ADDRESS RemoteAddress, LocalAddress;
+    PIPv4_HEADER Header;
+
+    TI_DbgPrint(MID_TRACE,("TCP OUTPUT (%x:%d):\n", data, len));
+    OskitDumpBuffer( data, len );
+
+    if( *data == 0x45 ) { /* IPv4 */
+       Header = (PIPv4_HEADER)data;
+       LocalAddress.Type = IP_ADDRESS_V4;
+       LocalAddress.Address.IPv4Address = Header->SrcAddr;
+       RemoteAddress.Type = IP_ADDRESS_V4;
+       RemoteAddress.Address.IPv4Address = Header->DstAddr;
+    } else {
+       DbgPrint("Don't currently handle IPv6\n");
+       KeBugCheck(4);
+    }
+
+    RemoteAddress.Type = LocalAddress.Type = IP_ADDRESS_V4;
+
+    DbgPrint("OSKIT SENDING PACKET *** %x -> %x\n",
+            LocalAddress.Address.IPv4Address,
+            RemoteAddress.Address.IPv4Address);
+    
+    ASSERT( (LocalAddress.Address.IPv4Address & 0xc0000000) != 0xc0000000 );
+       
+
+    Status = RouteGetRouteToDestination( &RemoteAddress,
+                                        NULL,
+                                        &RCN );
+    
+    if( !NT_SUCCESS(Status) || !RCN ) return OSK_EADDRNOTAVAIL;
+
+    KeRaiseIrql( DISPATCH_LEVEL, &OldIrql );
+
+    NdisStatus = 
+       AllocatePacketWithBuffer( &Packet.NdisPacket, data, len );
+    
+    if (NdisStatus != NDIS_STATUS_SUCCESS) {
+       TI_DbgPrint(MAX_TRACE, ("Error from NDIS: %08x\n", NdisStatus));
+       goto end;
+    }
+
+    AdjustPacket( Packet.NdisPacket, 0, MaxLLHeaderSize );
+    GetDataPtr( Packet.NdisPacket, 0, (PCHAR *)&Packet.Header, &Packet.ContigSize );
+    TI_DbgPrint(MAX_TRACE,("PC(Packet.NdisPacket) is %s (%x)\n", STRINGIFY(PC(Packet.NdisPacket)), PC(Packet.NdisPacket)));
+    PC(Packet.NdisPacket)->Complete = TCPPacketSendComplete;
+    OskitDumpBuffer((PCHAR)(PC(Packet.NdisPacket)),sizeof(*(PC(Packet.NdisPacket))));
+
+    Packet.HeaderSize = sizeof(IPv4_HEADER);
+    Packet.TotalSize = len;
+    Packet.SrcAddr = LocalAddress;
+    Packet.DstAddr = RemoteAddress;
+
+    IPSendFragment( Packet.NdisPacket, RCN->NCE );
+
+end:
+    KeLowerIrql( OldIrql );
+
+    if( !NT_SUCCESS(NdisStatus) ) return OSK_EINVAL;
+    else return 0;
+}
+
+void *TCPMalloc( void *ClientData,
+                OSK_UINT Bytes, OSK_PCHAR File, OSK_UINT Line ) {
+    void *v = ExAllocatePool( NonPagedPool, Bytes );
+    if( v ) TrackWithTag( FOURCC('f','b','s','d'), v, File, Line );
+    return v;
+}
+
+void TCPFree( void *ClientData,
+             void *data, OSK_PCHAR File, OSK_UINT Line ) {
+    UntrackFL( File, Line, data );
+    ExFreePool( data );
+}
+
+int TCPSleep( void *ClientData, void *token, int priority, char *msg,
+             int tmio ) {
+    PSLEEPING_THREAD SleepingThread;
+    
+    TI_DbgPrint(MID_TRACE,
+               ("Called TSLEEP: tok = %x, pri = %d, wmesg = %s, tmio = %x\n",
+                token, priority, msg, tmio));
+
+    SleepingThread = ExAllocatePool( NonPagedPool, sizeof( *SleepingThread ) );
+    if( SleepingThread ) {
+       KeInitializeEvent( &SleepingThread->Event, NotificationEvent, FALSE );
+       SleepingThread->SleepToken = token;
+
+       ExAcquireFastMutex( &SleepingThreadsLock );
+       InsertTailList( &SleepingThreadsList, &SleepingThread->Entry );
+       ExReleaseFastMutex( &SleepingThreadsLock );
+
+       TI_DbgPrint(MID_TRACE,("Waiting on %x\n", token));
+       KeWaitForSingleObject( &SleepingThread->Event,
+                              WrSuspended,
+                              KernelMode,
+                              TRUE,
+                              NULL );
+
+       ExAcquireFastMutex( &SleepingThreadsLock );
+       RemoveEntryList( &SleepingThread->Entry );
+       ExReleaseFastMutex( &SleepingThreadsLock );
+
+       ExFreePool( SleepingThread );
+    }
+    TI_DbgPrint(MID_TRACE,("Waiting finished: %x\n", token));
+    return 0;
+}
+
+void TCPWakeup( void *ClientData, void *token ) {
+    PLIST_ENTRY Entry;
+    PSLEEPING_THREAD SleepingThread;
+
+    ExAcquireFastMutex( &SleepingThreadsLock );
+    Entry = SleepingThreadsList.Flink;
+    while( Entry != &SleepingThreadsList ) {
+       SleepingThread = CONTAINING_RECORD(Entry, SLEEPING_THREAD, Entry);
+       TI_DbgPrint(MID_TRACE,("Sleeper @ %x\n", SleepingThread));
+       if( SleepingThread->SleepToken == token ) {
+           TI_DbgPrint(MID_TRACE,("Setting event to wake %x\n", token));
+           KeSetEvent( &SleepingThread->Event, IO_NETWORK_INCREMENT, FALSE );
+       }
+       Entry = Entry->Flink;
+    }
+    ExReleaseFastMutex( &SleepingThreadsLock );
+}
diff --git a/reactos/drivers/lib/ip/transport/tcp/if.c b/reactos/drivers/lib/ip/transport/tcp/if.c
new file mode 100644 (file)
index 0000000..a13070d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1997-1998 University of Utah and the Flux Group.
+ * All rights reserved.
+ * 
+ * This file is part of the Flux OSKit.  The OSKit is free software, also known
+ * as "open source;" you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License (GPL), version 2, as published by the Free
+ * Software Foundation (FSF).  To explore alternate licensing terms, contact
+ * the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
+ * 
+ * The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GPL for more details.  You should have
+ * received a copy of the GPL along with the OSKit; see the file COPYING.  If
+ * not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
+ */
+
+#include "precomp.h"
+
+
+#if 0
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <oskit/c/assert.h>
+#include <net/if.h>
+#endif
+
+int if_index = 0;
+struct ifaddr **ifnet_addrs;
+
+int    ifqmaxlen = OSK_IFQ_MAXLEN;
+struct ifnet *ifnet;
+
+/*
+ * Network interface utility routines.
+ *
+ * Routines with ifa_ifwith* names take sockaddr *'s as
+ * parameters.
+ */
+
+PVOID TCPPrepareInterface( PIP_INTERFACE IF ) {
+    NTSTATUS Status;
+    POSK_IFADDR ifaddr = exAllocatePool
+       ( NonPagedPool, sizeof(*ifaddr) + 2 * sizeof( struct sockaddr_in ) );
+    struct sockaddr_in *addr_in = (struct sockaddr_in *)&ifaddr[1];
+    struct sockaddr_in *dstaddr_in = (struct sockaddr_in *)&addr_in[1];
+    if( !ifaddr ) return NULL;
+
+    TI_DbgPrint(MID_TRACE,("Called\n"));
+
+    ifaddr->ifa_dstaddr = (struct sockaddr *)dstaddr_in;
+    /* XXX - Point-to-point interfaces not supported yet */
+    memset( &ifaddr->ifa_dstaddr, 0, sizeof( struct sockaddr ) );
+    
+    ifaddr->ifa_addr = (struct sockaddr *)addr_in;
+    Status = GetInterfaceIPv4Address( IF,
+                                     ADE_UNICAST,
+                                     (PULONG)&addr_in->sin_addr.s_addr );
+    
+    if( !NT_SUCCESS(Status) )
+       addr_in->sin_addr.s_addr = 0;
+
+    TI_DbgPrint(MID_TRACE,("Prepare interface %x : addr %x\n",
+                          IF, addr_in->sin_addr.s_addr));
+    
+    ifaddr->ifa_flags = 0; /* XXX what goes here? */
+    ifaddr->ifa_refcnt = 0; /* Anachronistic */
+    ifaddr->ifa_metric = 1; /* We can get it like in ninfo.c, if we want */
+    ifaddr->ifa_mtu = IF->MTU;
+
+    TI_DbgPrint(MID_TRACE,("Leaving\n"));
+
+    return ifaddr;
+}
+
+POSK_IFADDR TCPFindInterface( void *ClientData,
+                             OSK_UINT AddrType,
+                             OSK_UINT FindType,
+                             OSK_SOCKADDR *ReqAddr,
+                             OSK_IFADDR *Interface ) {
+    PNEIGHBOR_CACHE_ENTRY NCE;
+    IP_ADDRESS Destination;
+    struct sockaddr_in *addr_in = (struct sockaddr_in *)ReqAddr;
+    
+    TI_DbgPrint(MID_TRACE,("called for type %d\n", FindType));
+
+    if( !ReqAddr ) {
+       TI_DbgPrint(MID_TRACE,("no addr or no ifaddr (%x)\n", ReqAddr));
+       return NULL;
+    }
+
+    Destination.Type = IP_ADDRESS_V4;
+    Destination.Address.IPv4Address = addr_in->sin_addr.s_addr;
+
+    TI_DbgPrint(MID_TRACE,("Address is %x\n", addr_in->sin_addr.s_addr));
+
+    NCE = RouterGetRoute(&Destination, NULL);
+
+    if( !NCE || !NCE->Interface ) {
+       TI_DbgPrint(MID_TRACE,("no neighbor cache or no interface (%x %x)\n",
+                              NCE, NCE->Interface));
+       return NULL;
+    }
+
+    addr_in = (struct sockaddr_in *)
+       ((POSK_IFADDR)NCE->Interface->TCPContext)->ifa_addr;
+    TI_DbgPrint(MID_TRACE,("returning addr %x\n", addr_in->sin_addr.s_addr));
+    
+    return NCE->Interface->TCPContext;
+}
+
diff --git a/reactos/drivers/lib/ip/transport/tcp/tcp.c b/reactos/drivers/lib/ip/transport/tcp/tcp.c
new file mode 100644 (file)
index 0000000..c5071f1
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        transport/tcp/tcp.c
+ * PURPOSE:     Transmission Control Protocol
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+LONG TCP_IPIdentification = 0;
+static BOOLEAN TCPInitialized = FALSE;
+static NPAGED_LOOKASIDE_LIST TCPSegmentList;
+LIST_ENTRY SleepingThreadsList;
+FAST_MUTEX SleepingThreadsLock;
+RECURSIVE_MUTEX TCPLock;
+
+NTSTATUS TCPSocket( PCONNECTION_ENDPOINT Connection, 
+                   UINT Family, UINT Type, UINT Proto ) {
+    NTSTATUS Status;
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+    Status = TCPTranslateError( OskitTCPSocket( Connection,
+                                               &Connection->SocketContext,
+                                               Family,
+                                               Type,
+                                               Proto ) );
+    RecursiveMutexLeave( &TCPLock );
+
+    return Status;
+}
+
+VOID TCPReceive(PNET_TABLE_ENTRY NTE, PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Receives and queues TCP data
+ * ARGUMENTS:
+ *     NTE      = Pointer to net table entry which the packet was received on
+ *     IPPacket = Pointer to an IP packet that was received
+ * NOTES:
+ *     This is the low level interface for receiving TCP data
+ */
+{
+    TI_DbgPrint(MID_TRACE,("Sending packet %d (%d) to oskit\n", 
+                          IPPacket->TotalSize,
+                          IPPacket->HeaderSize));
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+
+    OskitTCPReceiveDatagram( IPPacket->Header, 
+                            IPPacket->TotalSize, 
+                            IPPacket->HeaderSize );
+
+    RecursiveMutexLeave( &TCPLock );
+}
+
+/* event.c */
+int TCPSocketState( void *ClientData,
+                   void *WhichSocket,
+                   void *WhichConnection,
+                   OSK_UINT NewState );
+
+int TCPPacketSend( void *ClientData,
+                  OSK_PCHAR Data,
+                  OSK_UINT Len );
+
+POSK_IFADDR TCPFindInterface( void *ClientData,
+                             OSK_UINT AddrType,
+                             OSK_UINT FindType,
+                             OSK_SOCKADDR *ReqAddr );
+
+void *TCPMalloc( void *ClientData,
+                OSK_UINT bytes, OSK_PCHAR file, OSK_UINT line );
+void TCPFree( void *ClientData,
+             void *data, OSK_PCHAR file, OSK_UINT line );
+
+int TCPSleep( void *ClientData, void *token, int priority, char *msg,
+             int tmio );
+
+void TCPWakeup( void *ClientData, void *token );
+
+OSKITTCP_EVENT_HANDLERS EventHandlers = {
+    NULL,             /* Client Data */
+    TCPSocketState,   /* SocketState */
+    TCPPacketSend,    /* PacketSend */
+    TCPFindInterface, /* FindInterface */
+    TCPMalloc,        /* Malloc */
+    TCPFree,          /* Free */
+    TCPSleep,         /* Sleep */
+    TCPWakeup         /* Wakeup */
+};
+
+NTSTATUS TCPStartup(VOID)
+/*
+ * FUNCTION: Initializes the TCP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    RecursiveMutexInit( &TCPLock );
+    ExInitializeFastMutex( &SleepingThreadsLock );
+    InitializeListHead( &SleepingThreadsList );    
+
+    RegisterOskitTCPEventHandlers( &EventHandlers );
+    InitOskitTCP();
+    
+    /* Register this protocol with IP layer */
+    IPRegisterProtocol(IPPROTO_TCP, TCPReceive);
+    
+    ExInitializeNPagedLookasideList(
+       &TCPSegmentList,                /* Lookaside list */
+       NULL,                           /* Allocate routine */
+       NULL,                           /* Free routine */
+       0,                              /* Flags */
+       sizeof(TCP_SEGMENT),            /* Size of each entry */
+       TAG('T','C','P','S'),           /* Tag */
+       0);                             /* Depth */
+    
+    TCPInitialized = TRUE;
+    
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS TCPShutdown(VOID)
+/*
+ * FUNCTION: Shuts down the TCP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    if (!TCPInitialized)
+       return STATUS_SUCCESS;
+    
+    /* Deregister this protocol with IP layer */
+    IPRegisterProtocol(IPPROTO_TCP, NULL);
+    
+    ExDeleteNPagedLookasideList(&TCPSegmentList);
+    
+    TCPInitialized = FALSE;
+
+    DeinitOskitTCP();
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS TCPTranslateError( int OskitError ) {
+    NTSTATUS Status = STATUS_UNSUCCESSFUL;
+
+    switch( OskitError ) {
+    case 0: Status = STATUS_SUCCESS; break;
+    case OSK_EADDRNOTAVAIL:
+    case OSK_EAFNOSUPPORT: Status = STATUS_INVALID_CONNECTION; break;
+    case OSK_ECONNREFUSED:
+    case OSK_ECONNRESET: Status = STATUS_REMOTE_NOT_LISTENING; break;
+    case OSK_EINPROGRESS:
+    case OSK_EAGAIN: Status = STATUS_PENDING; break;
+    default: Status = STATUS_INVALID_CONNECTION; break;
+    }
+
+    TI_DbgPrint(MID_TRACE,("Error %d -> %x\n", OskitError, Status));
+    return Status;
+}
+
+#if 0
+NTSTATUS TCPBind
+( PTDI_REQUEST Request,
+  PTDI_CONNECTION_INFORMATION ConnInfo ) {
+    NTSTATUS Status;
+    PCONNECTION_ENDPOINT Connection = Request->Handle.ConnectionContext;
+    SOCKADDR_IN AddressToConnect;
+    PIP_ADDRESS LocalAddress;
+    USHORT LocalPort;
+
+    TI_DbgPrint(MID_TRACE,("Called\n"));
+
+    Status = AddrBuildAddress
+       ((PTA_ADDRESS)ConnInfo->LocalAddress,
+        &LocalAddress,
+        &LocalPort);
+
+    AddressToBind.sin_family = AF_INET;
+    memcpy( &AddressToBind.sin_addr, 
+           &LocalAddress->Address.IPv4Address,
+           sizeof(AddressToBind.sin_addr) );
+    AddressToBind.sin_port = LocalPort;
+
+    Status = OskitTCPBind( Connection->SocketContext,
+                          Connection,
+                          &AddressToBind, 
+                          sizeof(AddressToBind));
+
+    TI_DbgPrint(MID_TRACE,("Leaving %x\n", Status));
+
+    return Status;
+}
+#endif
+
+NTSTATUS TCPConnect
+( PTDI_REQUEST Request,
+  PTDI_CONNECTION_INFORMATION ConnInfo,
+  PTDI_CONNECTION_INFORMATION ReturnInfo ) {
+    NTSTATUS Status;
+    SOCKADDR_IN AddressToConnect = { 0 }, AddressToBind = { 0 };
+    PCONNECTION_ENDPOINT Connection = Request->Handle.ConnectionContext;
+    PIP_ADDRESS RemoteAddress;
+    USHORT RemotePort;
+    PTDI_BUCKET Bucket;
+
+    DbgPrint("TCPConnect: Called\n");
+
+    Bucket = ExAllocatePool( NonPagedPool, sizeof(*Bucket) );
+    if( !Bucket ) return STATUS_NO_MEMORY;
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+
+    /* Freed in TCPSocketState */
+    Bucket->Request = *Request;
+    InsertHeadList( &Connection->ConnectRequest, &Bucket->Entry );
+
+    Status = AddrBuildAddress
+       ((PTA_ADDRESS)ConnInfo->RemoteAddress,
+        &RemoteAddress,
+        &RemotePort);
+
+    DbgPrint("Connecting to address %x:%x\n",
+            RemoteAddress->Address.IPv4Address,
+            RemotePort);
+
+    if (!NT_SUCCESS(Status)) {
+       TI_DbgPrint(MID_TRACE, ("Could not AddrBuildAddress in TCPConnect\n"));
+       return Status;
+    }
+    
+    AddressToConnect.sin_family = AF_INET;
+    AddressToBind = AddressToConnect;
+
+    OskitTCPBind( Connection->SocketContext,
+                 Connection,
+                 &AddressToBind,
+                 sizeof(AddressToBind) );
+
+    memcpy( &AddressToConnect.sin_addr, 
+           &RemoteAddress->Address.IPv4Address,
+           sizeof(AddressToConnect.sin_addr) );
+    AddressToConnect.sin_port = RemotePort;
+
+    Status = OskitTCPConnect(Connection->SocketContext,
+                            Connection,
+                            &AddressToConnect, 
+                            sizeof(AddressToConnect));
+
+    RecursiveMutexLeave( &TCPLock );
+    
+    if( Status == OSK_EINPROGRESS || Status == STATUS_SUCCESS ) 
+       return STATUS_PENDING;
+    else
+       return Status;
+}
+
+NTSTATUS TCPClose
+( PCONNECTION_ENDPOINT Connection ) {
+    NTSTATUS Status;
+    
+    TI_DbgPrint(MID_TRACE,("TCPClose started\n"));
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+
+    Status = TCPTranslateError( OskitTCPClose( Connection->SocketContext ) );
+
+    RecursiveMutexLeave( &TCPLock );
+    
+    TI_DbgPrint(MID_TRACE,("TCPClose finished %x\n", Status));
+
+    return Status;
+}
+
+NTSTATUS TCPListen
+( PTDI_REQUEST Request,
+  UINT Backlog ) {
+    PCONNECTION_ENDPOINT Connection;
+    NTSTATUS Status;
+
+    Connection = Request->Handle.ConnectionContext;
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+
+    Status =  TCPTranslateError( OskitTCPListen( Connection->SocketContext,
+                                                Backlog ) );
+
+    RecursiveMutexLeave( &TCPLock );
+
+    return Status;
+}
+
+NTSTATUS TCPAccept
+( PTDI_REQUEST Request,
+  VOID **NewSocketContext ) {
+    return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS TCPReceiveData
+( PTDI_REQUEST Request,
+  PNDIS_BUFFER Buffer,
+  ULONG ReceiveLength,
+  ULONG ReceiveFlags,
+  PULONG BytesReceived ) {
+    PCONNECTION_ENDPOINT Connection;
+    PCHAR DataBuffer;
+    UINT DataLen, Received = 0;
+    NTSTATUS Status;
+    PTDI_BUCKET Bucket;
+
+    TI_DbgPrint(MID_TRACE,("Called for %d bytes\n", ReceiveLength));
+
+    Connection = Request->Handle.ConnectionContext;
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+
+    NdisQueryBuffer( Buffer, &DataBuffer, &DataLen );
+
+    TI_DbgPrint(MID_TRACE,("TCP>|< Got an MDL %x (%x:%d)\n", Buffer, DataBuffer, DataLen));
+
+    Status = TCPTranslateError
+       ( OskitTCPRecv
+         ( Connection->SocketContext,
+           DataBuffer,
+           DataLen,
+           &Received,
+           ReceiveFlags ) );
+
+    TI_DbgPrint(MID_TRACE,("OskitTCPReceive: %x, %d\n", Status, Received));
+
+    /* Keep this request around ... there was no data yet */
+    if( Status == STATUS_PENDING || 
+       (Status == STATUS_SUCCESS && Received == 0) ) {
+       /* Freed in TCPSocketState */
+       Bucket = ExAllocatePool( NonPagedPool, sizeof(*Bucket) );
+       if( !Bucket ) {
+           TI_DbgPrint(MID_TRACE,("Failed to allocate bucket\n"));
+           return STATUS_NO_MEMORY;
+       }
+       
+       Bucket->Request = *Request;
+       *BytesReceived = 0;
+       InsertHeadList( &Connection->ReceiveRequest, &Bucket->Entry );
+       Status = STATUS_PENDING;
+       TI_DbgPrint(MID_TRACE,("Queued read irp\n"));
+    } else {
+       TI_DbgPrint(MID_TRACE,("Got status %x, bytes %d\n", Status, Received));
+       *BytesReceived = Received;
+    }
+
+    RecursiveMutexLeave( &TCPLock );
+
+    TI_DbgPrint(MID_TRACE,("Status %x\n", Status));
+
+    return Status;
+}
+
+NTSTATUS TCPSendData
+( PTDI_REQUEST Request,
+  PNDIS_BUFFER Buffer,
+  ULONG DataSize,
+  ULONG Flags,
+  PULONG DataUsed ) {
+    NTSTATUS Status;
+    PCONNECTION_ENDPOINT Connection;
+    PCHAR BufferData;
+    ULONG PacketSize;
+
+    Connection = Request->Handle.ConnectionContext;
+
+    RecursiveMutexEnter( &TCPLock, TRUE );
+
+    NdisQueryBuffer( Buffer, &BufferData, &PacketSize );
+    
+    TI_DbgPrint(MID_TRACE,("Connection = %x\n", Connection));
+    TI_DbgPrint(MID_TRACE,("Connection->SocketContext = %x\n",
+                          Connection->SocketContext));
+
+    OskitDumpBuffer( BufferData, PacketSize );
+
+    Status = OskitTCPSend( Connection->SocketContext, 
+                          BufferData, PacketSize, (PUINT)DataUsed, 0 );
+
+    RecursiveMutexLeave( &TCPLock );
+
+    return Status;
+}
+
+VOID TCPTimeout(VOID) { 
+    static int Times = 0;
+    if( (Times++ % 5) == 0 ) {
+       RecursiveMutexEnter( &TCPLock, TRUE );
+       TimerOskitTCP();
+       RecursiveMutexLeave( &TCPLock );
+    }
+}
+
+/* EOF */
diff --git a/reactos/drivers/lib/ip/transport/udp/udp.c b/reactos/drivers/lib/ip/transport/udp/udp.c
new file mode 100644 (file)
index 0000000..b02fad1
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TCP/IP protocol driver
+ * FILE:        transport/udp/udp.c
+ * PURPOSE:     User Datagram Protocol routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ */
+
+#include "precomp.h"
+
+
+BOOLEAN UDPInitialized = FALSE;
+
+
+NTSTATUS AddUDPHeaderIPv4(
+  PDATAGRAM_SEND_REQUEST SendRequest,
+  PIP_ADDRESS LocalAddress,
+  USHORT LocalPort,
+  PIP_PACKET IPPacket)
+/*
+ * FUNCTION: Adds an IPv4 and UDP header to an IP packet
+ * ARGUMENTS:
+ *     SendRequest  = Pointer to send request
+ *     LocalAddress = Pointer to our local address
+ *     LocalPort    = The port we send this datagram from
+ *     IPPacket     = Pointer to IP packet
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  PIPv4_HEADER IPHeader;
+  PUDP_HEADER UDPHeader;
+  PVOID Header;
+       ULONG BufferSize;
+  NDIS_STATUS NdisStatus;
+  PNDIS_BUFFER HeaderBuffer;
+
+       BufferSize = MaxLLHeaderSize + sizeof(IPv4_HEADER) + sizeof(UDP_HEADER);
+  Header     = ExAllocatePool(NonPagedPool, BufferSize);
+  if (!Header) {
+    TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet headers.\n"));
+    return STATUS_INSUFFICIENT_RESOURCES;
+  }
+
+  TI_DbgPrint(MAX_TRACE, ("Allocated %d bytes for headers at 0x%X.\n", BufferSize, Header));
+
+  /* Allocate NDIS buffer for maximum Link level, IP and UDP header */
+  NdisAllocateBuffer(&NdisStatus,
+                     &HeaderBuffer,
+                     GlobalBufferPool,
+                     Header,
+                     BufferSize);
+  if (NdisStatus != NDIS_STATUS_SUCCESS) {
+    TI_DbgPrint(MIN_TRACE, ("Cannot allocate NDIS buffer for packet headers. NdisStatus = (0x%X)\n", NdisStatus));
+    ExFreePool(Header);
+    return STATUS_INSUFFICIENT_RESOURCES;
+  }
+
+  /* Chain header at front of NDIS packet */
+  NdisChainBufferAtFront(IPPacket->NdisPacket, HeaderBuffer);
+  
+  IPPacket->Header     = (PVOID)((ULONG_PTR)Header + MaxLLHeaderSize);
+  IPPacket->HeaderSize = 20;
+
+  /* Build IPv4 header */
+  IPHeader = (PIPv4_HEADER)IPPacket->Header;
+  /* Version = 4, Length = 5 DWORDs */
+  IPHeader->VerIHL = 0x45;
+  /* Normal Type-of-Service */
+  IPHeader->Tos = 0;
+  /* Length of header and data */
+  IPHeader->TotalLength = WH2N((USHORT)IPPacket->TotalSize);
+  /* Identification */
+  IPHeader->Id = 0;
+  /* One fragment at offset 0 */
+  IPHeader->FlagsFragOfs = 0;
+  /* Time-to-Live is 128 */
+  IPHeader->Ttl = 128;
+  /* User Datagram Protocol */
+  IPHeader->Protocol = IPPROTO_UDP;
+  /* Checksum is 0 (for later calculation of this) */
+  IPHeader->Checksum = 0;
+  /* Source address */
+  IPHeader->SrcAddr = LocalAddress->Address.IPv4Address;
+  /* Destination address. FIXME: IPv4 only */
+  IPHeader->DstAddr = SendRequest->RemoteAddress.Address.IPv4Address;
+
+  /* Build UDP header */
+  UDPHeader = (PUDP_HEADER)((ULONG_PTR)IPHeader + sizeof(IPv4_HEADER));
+  /* Port values are already big-endian values */
+  UDPHeader->SourcePort = LocalPort;
+  UDPHeader->DestPort   = SendRequest->RemotePort;
+  /* FIXME: Calculate UDP checksum and put it in UDP header */
+  UDPHeader->Checksum   = 0;
+  /* Length of UDP header and data */
+  UDPHeader->Length     = WH2N((USHORT)IPPacket->TotalSize - IPPacket->HeaderSize);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS BuildUDPPacket(
+  PVOID Context,
+  PIP_ADDRESS LocalAddress,
+  USHORT LocalPort)
+/*
+ * FUNCTION: Builds an UDP packet
+ * ARGUMENTS:
+ *     Context      = Pointer to context information (DATAGRAM_SEND_REQUEST)
+ *     LocalAddress = Pointer to our local address
+ *     LocalPort    = The port we send this datagram from
+ *     IPPacket     = Address of pointer to IP packet
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  NTSTATUS Status;
+  PDATAGRAM_SEND_REQUEST SendRequest = (PDATAGRAM_SEND_REQUEST)Context;
+  PIP_PACKET Packet = &SendRequest->Packet;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  /* Prepare packet */
+
+  /* FIXME: Assumes IPv4 */
+  IPInitializePacket(&SendRequest->Packet, IP_ADDRESS_V4);
+  if (!Packet)
+    return STATUS_INSUFFICIENT_RESOURCES;
+
+  Packet->TotalSize = sizeof(IPv4_HEADER) +
+                      sizeof(UDP_HEADER)  +
+                      SendRequest->BufferSize;
+
+  switch (SendRequest->RemoteAddress.Type) {
+  case IP_ADDRESS_V4:
+    Status = AddUDPHeaderIPv4(SendRequest, LocalAddress, LocalPort, Packet);
+    break;
+  case IP_ADDRESS_V6:
+    /* FIXME: Support IPv6 */
+    TI_DbgPrint(MIN_TRACE, ("IPv6 UDP datagrams are not supported.\n"));
+  default:
+    Status = STATUS_UNSUCCESSFUL;
+    break;
+  }
+  if (!NT_SUCCESS(Status)) {
+    TI_DbgPrint(MIN_TRACE, ("Cannot add UDP header. Status = (0x%X)\n", Status));
+    FreeNdisPacket(Packet->NdisPacket);
+    return Status;
+  }
+
+  DISPLAY_IP_PACKET(Packet);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS UDPSendDatagram(
+  PTDI_REQUEST Request,
+  PTDI_CONNECTION_INFORMATION ConnInfo,
+  PNDIS_BUFFER Buffer,
+  ULONG DataSize,
+  PULONG DataUsed ) 
+/*
+ * FUNCTION: Sends an UDP datagram to a remote address
+ * ARGUMENTS:
+ *     Request   = Pointer to TDI request
+ *     ConnInfo  = Pointer to connection information
+ *     Buffer    = Pointer to NDIS buffer with data
+ *     DataSize  = Size in bytes of data to be sent
+ * RETURNS:
+ *     Status of operation
+ */
+{
+    PDATAGRAM_SEND_REQUEST SendRequest;
+    PADDRESS_FILE AddrFile = 
+       (PADDRESS_FILE)Request->Handle.AddressHandle;
+    PCHAR BufferData;
+    UINT BufferLen;
+
+    NdisQueryBuffer( Buffer, &BufferData, &BufferLen );
+
+    *DataUsed = BufferLen;
+    
+    BuildUDPPacket( SendRequest,
+                   (PIP_ADDRESS)&AddrFile->ADE->Address->Address.
+                   IPv4Address,
+                   AddrFile->Port );
+
+    return DGSendDatagram(Request,
+                         ConnInfo,
+                         &SendRequest->Packet);
+}
+
+
+NTSTATUS UDPReceiveDatagram(
+    PTDI_REQUEST Request,
+    PTDI_CONNECTION_INFORMATION ConnInfo,
+    PNDIS_BUFFER Buffer,
+    ULONG ReceiveLength,
+    ULONG ReceiveFlags,
+    PTDI_CONNECTION_INFORMATION ReturnInfo,
+    PULONG BytesReceived)
+/*
+ * FUNCTION: Attempts to receive an UDP datagram from a remote address
+ * ARGUMENTS:
+ *     Request       = Pointer to TDI request
+ *     ConnInfo      = Pointer to connection information
+ *     Buffer        = Pointer to NDIS buffer chain to store received data
+ *     ReceiveLength = Maximum size to use of buffer, 0 if all can be used
+ *     ReceiveFlags  = Receive flags (None, Normal, Peek)
+ *     ReturnInfo    = Pointer to structure for return information
+ *     BytesReceive  = Pointer to structure for number of bytes received
+ * RETURNS:
+ *     Status of operation
+ * NOTES:
+ *     This is the high level interface for receiving UDP datagrams
+ */
+{
+  return DGReceiveDatagram(Request,
+                           ConnInfo,
+                           Buffer,
+                           ReceiveLength,
+                           ReceiveFlags,
+                           ReturnInfo,
+                           BytesReceived);
+}
+
+
+VOID UDPReceive(
+   PNET_TABLE_ENTRY NTE,
+   PIP_PACKET IPPacket)
+/*
+* FUNCTION: Receives and queues a UDP datagram
+* ARGUMENTS:
+*     NTE      = Pointer to net table entry which the packet was received on
+*     IPPacket = Pointer to an IP packet that was received
+* NOTES:
+*     This is the low level interface for receiving UDP datagrams. It strips
+*     the UDP header from a packet and delivers the data to anyone that wants it
+*/
+{
+  AF_SEARCH SearchContext;
+  PIPv4_HEADER IPv4Header;
+  PADDRESS_FILE AddrFile;
+  PUDP_HEADER UDPHeader;
+  PIP_ADDRESS DstAddress;
+  UINT DataSize, i;
+
+  TI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  switch (IPPacket->Type) {
+  /* IPv4 packet */
+  case IP_ADDRESS_V4:
+    IPv4Header = IPPacket->Header;
+    DstAddress = &IPPacket->DstAddr;
+    break;
+
+  /* IPv6 packet */
+  case IP_ADDRESS_V6:
+    TI_DbgPrint(MIN_TRACE, ("Discarded IPv6 UDP datagram (%i bytes).\n", IPPacket->TotalSize));
+
+    /* FIXME: IPv6 is not supported */
+    return;
+
+  default:
+    return;
+  }
+
+  UDPHeader = (PUDP_HEADER)IPPacket->Data;
+
+  /* FIXME: Calculate and validate UDP checksum */
+
+  /* Sanity checks */
+  i = WH2N(UDPHeader->Length);
+  if ((i < sizeof(UDP_HEADER)) || (i > IPPacket->TotalSize - IPPacket->Position)) {
+    /* Incorrect or damaged packet received, discard it */
+    TI_DbgPrint(MIN_TRACE, ("Incorrect or damaged UDP packet received.\n"));
+    return;
+  }
+
+  DataSize = i - sizeof(UDP_HEADER);
+
+  /* Go to UDP data area */
+  IPPacket->Data = (PVOID)((ULONG_PTR)IPPacket->Data + sizeof(UDP_HEADER));
+
+  /* Locate a receive request on destination address file object
+     and deliver the packet if one is found. If there is no receive
+     request on the address file object, call the associated receive
+     handler. If no receive handler is registered, drop the packet */
+
+  AddrFile = AddrSearchFirst(DstAddress,
+                             UDPHeader->DestPort,
+                             IPPROTO_UDP,
+                             &SearchContext);
+  if (AddrFile) {
+    do {
+      DGDeliverData(AddrFile,
+                    DstAddress,
+                    IPPacket,
+                    DataSize);
+    } while ((AddrFile = AddrSearchNext(&SearchContext)) != NULL);
+  } else {
+    /* There are no open address files that will take this datagram */
+    /* FIXME: IPv4 only */
+    TI_DbgPrint(MID_TRACE, ("Cannot deliver IPv4 UDP datagram to address (0x%X).\n",
+      DN2H(DstAddress->Address.IPv4Address)));
+
+    /* FIXME: Send ICMP reply */
+  }
+  TI_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+}
+
+
+NTSTATUS UDPStartup(
+  VOID)
+/*
+ * FUNCTION: Initializes the UDP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+#ifdef __NTDRIVER__
+  RtlZeroMemory(&UDPStats, sizeof(UDP_STATISTICS));
+#endif
+
+  /* Register this protocol with IP layer */
+  IPRegisterProtocol(IPPROTO_UDP, UDPReceive);
+
+  UDPInitialized = TRUE;
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS UDPShutdown(
+  VOID)
+/*
+ * FUNCTION: Shuts down the UDP subsystem
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  if (!UDPInitialized)
+      return STATUS_SUCCESS;
+
+  /* Deregister this protocol with IP layer */
+  IPRegisterProtocol(IPPROTO_UDP, NULL);
+
+  return STATUS_SUCCESS;
+}
+
+/* EOF */