From b5ff23bb2f2c90488faacd3c9c974bf9936b338d Mon Sep 17 00:00:00 2001 From: David Welch Date: Wed, 2 Aug 2000 00:02:53 +0000 Subject: [PATCH] Added networking code from Casper Hornstrup svn path=/trunk/; revision=1282 --- reactos/drivers/net/tcpip/DIRS | 5 + reactos/drivers/net/tcpip/datalink/Makefile | 7 + reactos/drivers/net/tcpip/datalink/SOURCES | 13 + reactos/drivers/net/tcpip/datalink/arp.c | 277 +++++ reactos/drivers/net/tcpip/datalink/lan.c | 903 ++++++++++++++++ reactos/drivers/net/tcpip/datalink/loopback.c | 224 ++++ reactos/drivers/net/tcpip/include/address.h | 63 ++ reactos/drivers/net/tcpip/include/arp.h | 37 + reactos/drivers/net/tcpip/include/checksum.h | 27 + reactos/drivers/net/tcpip/include/datagram.h | 49 + reactos/drivers/net/tcpip/include/debug.h | 99 ++ reactos/drivers/net/tcpip/include/dispatch.h | 61 ++ reactos/drivers/net/tcpip/include/fileobjs.h | 32 + reactos/drivers/net/tcpip/include/icmp.h | 68 ++ reactos/drivers/net/tcpip/include/info.h | 93 ++ reactos/drivers/net/tcpip/include/ip.h | 245 +++++ reactos/drivers/net/tcpip/include/lan.h | 100 ++ reactos/drivers/net/tcpip/include/loopback.h | 25 + reactos/drivers/net/tcpip/include/neighbor.h | 101 ++ reactos/drivers/net/tcpip/include/pool.h | 19 + reactos/drivers/net/tcpip/include/rawip.h | 24 + reactos/drivers/net/tcpip/include/receive.h | 61 ++ reactos/drivers/net/tcpip/include/route.h | 75 ++ reactos/drivers/net/tcpip/include/router.h | 62 ++ reactos/drivers/net/tcpip/include/routines.h | 53 + reactos/drivers/net/tcpip/include/tcp.h | 18 + reactos/drivers/net/tcpip/include/tcpip.h | 131 +++ reactos/drivers/net/tcpip/include/ticonsts.h | 56 + reactos/drivers/net/tcpip/include/titypes.h | 215 ++++ reactos/drivers/net/tcpip/include/transmit.h | 47 + reactos/drivers/net/tcpip/include/udp.h | 68 ++ reactos/drivers/net/tcpip/makefile | 132 +++ reactos/drivers/net/tcpip/network/Makefile | 7 + reactos/drivers/net/tcpip/network/SOURCES | 20 + reactos/drivers/net/tcpip/network/icmp.c | 299 ++++++ reactos/drivers/net/tcpip/network/ip.c | 979 ++++++++++++++++++ reactos/drivers/net/tcpip/network/neighbor.c | 506 +++++++++ reactos/drivers/net/tcpip/network/receive.c | 638 ++++++++++++ reactos/drivers/net/tcpip/network/route.c | 665 ++++++++++++ reactos/drivers/net/tcpip/network/router.c | 500 +++++++++ reactos/drivers/net/tcpip/network/transmit.c | 328 ++++++ reactos/drivers/net/tcpip/readme.txt | 19 + reactos/drivers/net/tcpip/tcpip.def | 32 + reactos/drivers/net/tcpip/tcpip.edf | 32 + reactos/drivers/net/tcpip/tcpip.rc | 38 + .../drivers/net/tcpip/tcpip/Copy of info.c | 335 ++++++ reactos/drivers/net/tcpip/tcpip/Makefile | 7 + reactos/drivers/net/tcpip/tcpip/SOURCES | 28 + reactos/drivers/net/tcpip/tcpip/address.c | 312 ++++++ reactos/drivers/net/tcpip/tcpip/checksum.c | 50 + reactos/drivers/net/tcpip/tcpip/dispatch.c | 871 ++++++++++++++++ reactos/drivers/net/tcpip/tcpip/fileobjs.c | 396 +++++++ reactos/drivers/net/tcpip/tcpip/info.c | 339 ++++++ reactos/drivers/net/tcpip/tcpip/main.c | 805 ++++++++++++++ reactos/drivers/net/tcpip/tcpip/pool.c | 50 + reactos/drivers/net/tcpip/tcpip/resource.rc | 10 + reactos/drivers/net/tcpip/tcpip/routines.c | 403 +++++++ reactos/drivers/net/tcpip/transport/DIRS | 5 + .../net/tcpip/transport/datagram/Makefile | 7 + .../net/tcpip/transport/datagram/SOURCES | 13 + .../net/tcpip/transport/datagram/datagram.c | 520 ++++++++++ .../net/tcpip/transport/rawip/Makefile | 7 + .../drivers/net/tcpip/transport/rawip/SOURCES | 13 + .../drivers/net/tcpip/transport/rawip/rawip.c | 113 ++ .../drivers/net/tcpip/transport/tcp/Makefile | 7 + .../drivers/net/tcpip/transport/tcp/SOURCES | 13 + reactos/drivers/net/tcpip/transport/tcp/tcp.c | 44 + .../drivers/net/tcpip/transport/udp/Makefile | 7 + .../drivers/net/tcpip/transport/udp/SOURCES | 12 + reactos/drivers/net/tcpip/transport/udp/udp.c | 437 ++++++++ 70 files changed, 12257 insertions(+) create mode 100644 reactos/drivers/net/tcpip/DIRS create mode 100644 reactos/drivers/net/tcpip/datalink/Makefile create mode 100644 reactos/drivers/net/tcpip/datalink/SOURCES create mode 100644 reactos/drivers/net/tcpip/datalink/arp.c create mode 100644 reactos/drivers/net/tcpip/datalink/lan.c create mode 100644 reactos/drivers/net/tcpip/datalink/loopback.c create mode 100644 reactos/drivers/net/tcpip/include/address.h create mode 100644 reactos/drivers/net/tcpip/include/arp.h create mode 100644 reactos/drivers/net/tcpip/include/checksum.h create mode 100644 reactos/drivers/net/tcpip/include/datagram.h create mode 100644 reactos/drivers/net/tcpip/include/debug.h create mode 100644 reactos/drivers/net/tcpip/include/dispatch.h create mode 100644 reactos/drivers/net/tcpip/include/fileobjs.h create mode 100644 reactos/drivers/net/tcpip/include/icmp.h create mode 100644 reactos/drivers/net/tcpip/include/info.h create mode 100644 reactos/drivers/net/tcpip/include/ip.h create mode 100644 reactos/drivers/net/tcpip/include/lan.h create mode 100644 reactos/drivers/net/tcpip/include/loopback.h create mode 100644 reactos/drivers/net/tcpip/include/neighbor.h create mode 100644 reactos/drivers/net/tcpip/include/pool.h create mode 100644 reactos/drivers/net/tcpip/include/rawip.h create mode 100644 reactos/drivers/net/tcpip/include/receive.h create mode 100644 reactos/drivers/net/tcpip/include/route.h create mode 100644 reactos/drivers/net/tcpip/include/router.h create mode 100644 reactos/drivers/net/tcpip/include/routines.h create mode 100644 reactos/drivers/net/tcpip/include/tcp.h create mode 100644 reactos/drivers/net/tcpip/include/tcpip.h create mode 100644 reactos/drivers/net/tcpip/include/ticonsts.h create mode 100644 reactos/drivers/net/tcpip/include/titypes.h create mode 100644 reactos/drivers/net/tcpip/include/transmit.h create mode 100644 reactos/drivers/net/tcpip/include/udp.h create mode 100644 reactos/drivers/net/tcpip/makefile create mode 100644 reactos/drivers/net/tcpip/network/Makefile create mode 100644 reactos/drivers/net/tcpip/network/SOURCES create mode 100644 reactos/drivers/net/tcpip/network/icmp.c create mode 100644 reactos/drivers/net/tcpip/network/ip.c create mode 100644 reactos/drivers/net/tcpip/network/neighbor.c create mode 100644 reactos/drivers/net/tcpip/network/receive.c create mode 100644 reactos/drivers/net/tcpip/network/route.c create mode 100644 reactos/drivers/net/tcpip/network/router.c create mode 100644 reactos/drivers/net/tcpip/network/transmit.c create mode 100644 reactos/drivers/net/tcpip/readme.txt create mode 100644 reactos/drivers/net/tcpip/tcpip.def create mode 100644 reactos/drivers/net/tcpip/tcpip.edf create mode 100644 reactos/drivers/net/tcpip/tcpip.rc create mode 100644 reactos/drivers/net/tcpip/tcpip/Copy of info.c create mode 100644 reactos/drivers/net/tcpip/tcpip/Makefile create mode 100644 reactos/drivers/net/tcpip/tcpip/SOURCES create mode 100644 reactos/drivers/net/tcpip/tcpip/address.c create mode 100644 reactos/drivers/net/tcpip/tcpip/checksum.c create mode 100644 reactos/drivers/net/tcpip/tcpip/dispatch.c create mode 100644 reactos/drivers/net/tcpip/tcpip/fileobjs.c create mode 100644 reactos/drivers/net/tcpip/tcpip/info.c create mode 100644 reactos/drivers/net/tcpip/tcpip/main.c create mode 100644 reactos/drivers/net/tcpip/tcpip/pool.c create mode 100644 reactos/drivers/net/tcpip/tcpip/resource.rc create mode 100644 reactos/drivers/net/tcpip/tcpip/routines.c create mode 100644 reactos/drivers/net/tcpip/transport/DIRS create mode 100644 reactos/drivers/net/tcpip/transport/datagram/Makefile create mode 100644 reactos/drivers/net/tcpip/transport/datagram/SOURCES create mode 100644 reactos/drivers/net/tcpip/transport/datagram/datagram.c create mode 100644 reactos/drivers/net/tcpip/transport/rawip/Makefile create mode 100644 reactos/drivers/net/tcpip/transport/rawip/SOURCES create mode 100644 reactos/drivers/net/tcpip/transport/rawip/rawip.c create mode 100644 reactos/drivers/net/tcpip/transport/tcp/Makefile create mode 100644 reactos/drivers/net/tcpip/transport/tcp/SOURCES create mode 100644 reactos/drivers/net/tcpip/transport/tcp/tcp.c create mode 100644 reactos/drivers/net/tcpip/transport/udp/Makefile create mode 100644 reactos/drivers/net/tcpip/transport/udp/SOURCES create mode 100644 reactos/drivers/net/tcpip/transport/udp/udp.c diff --git a/reactos/drivers/net/tcpip/DIRS b/reactos/drivers/net/tcpip/DIRS new file mode 100644 index 00000000000..a49d00ed81c --- /dev/null +++ b/reactos/drivers/net/tcpip/DIRS @@ -0,0 +1,5 @@ +DIRS= datalink \ + network \ + transport \ + tcpip + diff --git a/reactos/drivers/net/tcpip/datalink/Makefile b/reactos/drivers/net/tcpip/datalink/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/datalink/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/datalink/SOURCES b/reactos/drivers/net/tcpip/datalink/SOURCES new file mode 100644 index 00000000000..b941923ac79 --- /dev/null +++ b/reactos/drivers/net/tcpip/datalink/SOURCES @@ -0,0 +1,13 @@ +TARGETNAME=datalink +TARGETPATH=..\objects +TARGETTYPE=LIBRARY + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib + +INCLUDES=..\include;$(BASEDIR)\INC;..\..\..\..\include\net +SOURCES= arp.c \ + lan.c \ + loopback.c + +MSC_WARNING_LEVEL=/W3 /WX diff --git a/reactos/drivers/net/tcpip/datalink/arp.c b/reactos/drivers/net/tcpip/datalink/arp.c new file mode 100644 index 00000000000..83d249096e9 --- /dev/null +++ b/reactos/drivers/net/tcpip/datalink/arp.c @@ -0,0 +1,277 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + + +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; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + PARP_HEADER Header; + PVOID DataBuffer; + ULONG Size; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + /* Prepare ARP packet */ + Size = MaxLLHeaderSize + sizeof(ARP_HEADER) + + 2 * LinkAddressLength + /* Hardware address length */ + 2 * ProtoAddressLength; /* Protocol address length */ + Size = MAX(Size, MinLLFrameSize); + + DataBuffer = ExAllocatePool(NonPagedPool, Size); + if (!DataBuffer) + return NULL; + + /* Allocate NDIS packet */ + NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool(DataBuffer); + return NULL; + } + + /* Allocate NDIS buffer for maximum link level header and ARP packet */ + NdisAllocateBuffer(&NdisStatus, &NdisBuffer, GlobalBufferPool, + DataBuffer, Size); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisFreePacket(NdisPacket); + ExFreePool(DataBuffer); + return NULL; + } + + /* Link NDIS buffer into packet */ + NdisChainBufferAtFront(NdisPacket, NdisBuffer); + 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); + (ULONG_PTR)DataBuffer += LinkAddressLength; + + /* Our protocol address */ + RtlCopyMemory(DataBuffer, SenderProtoAddress, ProtoAddressLength); + + if (TargetLinkAddress) { + (ULONG_PTR)DataBuffer += ProtoAddressLength; + /* Target hardware address */ + RtlCopyMemory(DataBuffer, TargetLinkAddress, LinkAddressLength); + (ULONG_PTR)DataBuffer += LinkAddressLength; + } else + /* Don't care about target hardware address */ + (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(MID_TRACE, ("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(MID_TRACE, ("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: + /* 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; + + (*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(MID_TRACE, ("Called.\n")); + + Header = (PARP_HEADER)Packet->Header; + + /* FIXME: Ethernet only */ + if (WN2H(Header->HWType) != 1) + return; + + /* Check protocol type */ + if (Header->ProtoType != ETYPE_IPv4) + 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(MIN_TRACE, ("Insufficient resources.\n")); + 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/net/tcpip/datalink/lan.c b/reactos/drivers/net/tcpip/datalink/lan.c new file mode 100644 index 00000000000..4476922b982 --- /dev/null +++ b/reactos/drivers/net/tcpip/datalink/lan.c @@ -0,0 +1,903 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: datalink/lan.c + * PURPOSE: Local Area Network media routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include +#include +#include +#include +#include + + +NDIS_HANDLE NdisProtocolHandle = (NDIS_HANDLE)NULL; +BOOLEAN ProtocolRegistered = FALSE; +PLAN_ADAPTER Adapters = NULL; + + +NDIS_STATUS NDISCall( + PLAN_ADAPTER Adapter, + NDIS_REQUEST_TYPE Type, + NDIS_OID OID, + PVOID Buffer, + UINT Length) +/* + * FUNCTION: Send a request to NDIS + * ARGUMENTS: + * Adapter = Pointer to a LAN_ADAPTER structure + * Type = Type of request (Set or Query) + * OID = Value to be set/queried for + * Buffer = Pointer to a buffer to use + * Length = Number of bytes in Buffer + * RETURNS: + * Status of operation + */ +{ + NDIS_REQUEST Request; + NDIS_STATUS NdisStatus; + + Request.RequestType = Type; + if (Type == NdisRequestSetInformation) { + Request.DATA.SET_INFORMATION.Oid = OID; + Request.DATA.SET_INFORMATION.InformationBuffer = Buffer; + Request.DATA.SET_INFORMATION.InformationBufferLength = Length; + } else { + Request.DATA.QUERY_INFORMATION.Oid = OID; + Request.DATA.QUERY_INFORMATION.InformationBuffer = Buffer; + Request.DATA.QUERY_INFORMATION.InformationBufferLength = Length; + } + + if (Adapter->State != LAN_STATE_RESETTING) { + NdisRequest(&NdisStatus, Adapter->NdisHandle, &Request); + } else + NdisStatus = NDIS_STATUS_NOT_ACCEPTED; + + /* Wait for NDIS to complete the request */ + if (NdisStatus == NDIS_STATUS_PENDING) { + KeWaitForSingleObject(&Adapter->Event, UserRequest, KernelMode, FALSE, NULL); + NdisStatus = Adapter->NdisStatus; + } + + return NdisStatus; +} + + +PNDIS_PACKET AllocateTDPacket( + PLAN_ADAPTER Adapter) +/* + * FUNCTION: Allocates an NDIS packet for NdisTransferData + * ARGUMENTS: + * Adapter = Pointer to LAN_ADAPTER structure + * RETURNS: + * Pointer to NDIS packet or NULL if there was not enough free + * non-paged memory + */ +{ + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER Buffer; + PVOID Data; + + NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool); + if (NdisStatus != NDIS_STATUS_SUCCESS) + return NULL; + + Data = ExAllocatePool(NonPagedPool, Adapter->MTU); + if (!Data) { + NdisFreePacket(NdisPacket); + return NULL; + } + + NdisAllocateBuffer(&NdisStatus, &Buffer, GlobalBufferPool, Data, Adapter->MTU); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisFreePacket(NdisPacket); + ExFreePool(Data); + return NULL; + } + + NdisChainBufferAtFront(NdisPacket, Buffer); + + PC(NdisPacket)->Context = NULL; /* End of list */ + + return NdisPacket; +} + + +VOID FreeTDPackets( + PLAN_ADAPTER Adapter) +/* + * FUNCTION: Frees transfer data packets + * ARGUMENTS: + * Adapter = Pointer to LAN_ADAPTER structure + */ +{ + PNDIS_PACKET NdisPacket, Next; + + /* Release transfer data packets */ + NdisPacket = Adapter->TDPackets; + while (NdisPacket) { + Next = PC(NdisPacket)->Context; + FreeNdisPacket(NdisPacket); + NdisPacket = Next; + } + Adapter->TDPackets = NULL; +} + + +VOID FreeAdapter( + PLAN_ADAPTER Adapter) +/* + * FUNCTION: Frees memory for a LAN_ADAPTER structure + * ARGUMENTS: + * Adapter = Pointer to LAN_ADAPTER structure to free + */ +{ + FreeTDPackets(Adapter); + ExFreePool(Adapter); +} + + +VOID ProtocolOpenAdapterComplete( + NDIS_HANDLE BindingContext, + NDIS_STATUS Status, + NDIS_STATUS OpenErrorStatus) +/* + * FUNCTION: Called by NDIS to complete opening of an adapter + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * Status = Status of the operation + * OpenErrorStatus = Additional status information + */ +{ + PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + KeSetEvent(&Adapter->Event, 0, FALSE); +} + + +VOID ProtocolCloseAdapterComplete( + NDIS_HANDLE BindingContext, + NDIS_STATUS Status) +/* + * FUNCTION: Called by NDIS to complete closing an adapter + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * Status = Status of the operation + */ +{ + PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + Adapter->NdisStatus = Status; + + KeSetEvent(&Adapter->Event, 0, FALSE); +} + + +VOID ProtocolResetComplete( + NDIS_HANDLE BindingContext, + NDIS_STATUS Status) +/* + * FUNCTION: Called by NDIS to complete resetting an adapter + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * Status = Status of the operation + */ +{ + TI_DbgPrint(MID_TRACE, ("Called.\n")); +} + + +VOID ProtocolRequestComplete( + NDIS_HANDLE BindingContext, + PNDIS_REQUEST NdisRequest, + NDIS_STATUS Status) +/* + * FUNCTION: Called by NDIS to complete a request + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * NdisRequest = Pointer to an object describing the request + * Status = Status of the operation + */ +{ + PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; + + /* Save status of request and signal an event */ + Adapter->NdisStatus = Status; + + KeSetEvent(&Adapter->Event, 0, FALSE); +} + + +VOID ProtocolSendComplete( + NDIS_HANDLE BindingContext, + PNDIS_PACKET Packet, + NDIS_STATUS Status) +/* + * FUNCTION: Called by NDIS to complete sending process + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * Packet = Pointer to a packet descriptor + * Status = Status of the operation + */ +{ + PLAN_ADAPTER Adapter = BindingContext; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + AdjustPacket(Packet, Adapter->HeaderSize, PC(Packet)->DLOffset); + + (*PC(Packet)->DLComplete)(Adapter->Context, Packet, Status); +} + + +VOID ProtocolTransferDataComplete( + NDIS_HANDLE BindingContext, + PNDIS_PACKET Packet, + NDIS_STATUS Status, + UINT BytesTransferred) +/* + * FUNCTION: Called by NDIS to complete reception of data + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * Packet = Pointer to a packet descriptor + * Status = Status of the operation + * BytesTransferred = Number of bytes transferred + * NOTES: + * If the packet was successfully received, determine the protocol + * type and pass it to the correct receive handler + */ +{ + UINT PacketType; + PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; + + if (Status == NDIS_STATUS_SUCCESS) { + PNDIS_BUFFER NdisBuffer; + IP_PACKET IPPacket; + + NdisGetFirstBufferFromPacket( + Packet, &NdisBuffer, &IPPacket.Header, + &IPPacket.ContigSize, &IPPacket.TotalSize); + + /* Determine which upper layer protocol that should receive + this packet and pass it to the correct receive handler */ + PacketType = ((PETH_HEADER)IPPacket.Header)->EType; + switch (PacketType) { + case ETYPE_IPv4: + case ETYPE_IPv6: + IPReceive(Adapter->Context, &IPPacket); + break; + case ETYPE_ARP: + ARPReceive(Adapter->Context, &IPPacket); + default: + break; + } + } + + /* Release the packet descriptor */ + KeAcquireSpinLockAtDpcLevel(&Adapter->Lock); + + PC(Packet)->Context = Adapter->TDPackets; + Adapter->TDPackets = Packet; + + KeReleaseSpinLockFromDpcLevel(&Adapter->Lock); +} + + +NDIS_STATUS ProtocolReceive( + NDIS_HANDLE BindingContext, + NDIS_HANDLE MacReceiveContext, + PVOID HeaderBuffer, + UINT HeaderBufferSize, + PVOID LookaheadBuffer, + UINT LookaheadBufferSize, + UINT PacketSize) +/* + * FUNCTION: Called by NDIS when a packet has been received on the physical link + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * MacReceiveContext = Handle used by underlying NIC driver + * HeaderBuffer = Pointer to a buffer containing the packet header + * HeaderBufferSize = Number of bytes in HeaderBuffer + * LookaheadBuffer = Pointer to a buffer containing buffered packet data + * LookaheadBufferSize = Size of LookaheadBuffer. May be less than asked for + * PacketSize = Overall size of the packet (not including header) + * RETURNS: + * Status of operation + */ +{ + USHORT EType; + UINT PacketType; + IP_PACKET IPPacket; + PLAN_ADAPTER Adapter = (PLAN_ADAPTER)BindingContext; + PETH_HEADER EHeader = (PETH_HEADER)HeaderBuffer; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + if ((Adapter->State != LAN_STATE_STARTED) || + HeaderBufferSize < Adapter->HeaderSize) + /* Adapter is not started or the header was too small */ + return NDIS_STATUS_NOT_ACCEPTED; + + if (Adapter->Media == NdisMedium802_3) { + /* Ethernet and IEEE 802.3 frames can be destinguished by + looking at the IEEE 802.3 length field. This field is + less than or equal to 1500 for a valid IEEE 802.3 frame + and larger than 1500 is it's a valid Ether-Type value. + See RFC 1122, section 2.3.3 for more information */ + if (((EType = EHeader->EType) != ETYPE_IPv4) && (EType != ETYPE_ARP)) + return NDIS_STATUS_NOT_ACCEPTED; + /* We use Ether-Type constants to destinguish packets */ + PacketType = EType; + } else + /* FIXME: Support other medias */ + return NDIS_STATUS_NOT_ACCEPTED; + + if (LookaheadBufferSize < PacketSize) { + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + UINT BytesTransferred; + + /* Get transfer data packet */ + + KeAcquireSpinLockAtDpcLevel(&Adapter->Lock); + + NdisPacket = Adapter->TDPackets; + if (NdisPacket == (PNDIS_PACKET)NULL) { + /* We don't have a free packet descriptor. Drop the packet */ + KeReleaseSpinLockFromDpcLevel(&Adapter->Lock); + return NDIS_STATUS_SUCCESS; + } + Adapter->TDPackets = PC(NdisPacket)->Context; + + KeReleaseSpinLockFromDpcLevel(&Adapter->Lock); + + /* Get the data */ + NdisTransferData(&NdisStatus, Adapter->NdisHandle, + MacReceiveContext, 0, PacketSize, + NdisPacket, &BytesTransferred); + if (NdisStatus != NDIS_STATUS_PENDING) + ProtocolTransferDataComplete(BindingContext, + NdisPacket, NdisStatus, BytesTransferred); + + return NDIS_STATUS_SUCCESS; + } + + /* We got all the data in the lookahead buffer */ + RtlZeroMemory(&IPPacket, sizeof(IPPacket)); + IPPacket.Header = LookaheadBuffer; + IPPacket.TotalSize = PacketSize; + + switch (PacketType) { + case ETYPE_IPv4: + case ETYPE_IPv6: + IPReceive(Adapter->Context, &IPPacket); + break; + case ETYPE_ARP: + ARPReceive(Adapter->Context, &IPPacket); + break; + default: + break; + } + + return NDIS_STATUS_SUCCESS; +} + + +VOID ProtocolReceiveComplete( + NDIS_HANDLE BindingContext) +/* + * FUNCTION: Called by NDIS when we're done receiving data + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + */ +{ + TI_DbgPrint(MID_TRACE, ("Called.\n")); +} + + +VOID ProtocolStatus( + NDIS_HANDLE BindingContext, + NDIS_STATUS GenerelStatus, + PVOID StatusBuffer, + UINT StatusBufferSize) +/* + * FUNCTION: Called by NDIS when the underlying driver has changed state + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + * GenerelStatus = A generel status code + * StatusBuffer = Pointer to a buffer with medium-specific data + * StatusBufferSize = Number of bytes in StatusBuffer + */ +{ + TI_DbgPrint(MID_TRACE, ("Called.\n")); +} + + +VOID ProtocolStatusComplete( + NDIS_HANDLE NdisBindingContext) +/* + * FUNCTION: Called by NDIS when a status-change has occurred + * ARGUMENTS: + * BindingContext = Pointer to a device context (LAN_ADAPTER) + */ +{ + TI_DbgPrint(MID_TRACE, ("Called.\n")); +} + + +VOID LANTransmit( + PVOID Context, + PNDIS_PACKET NdisPacket, + UINT Offset, + PVOID LinkAddress, + USHORT Type) +/* + * FUNCTION: Transmits a packet + * ARGUMENTS: + * Context = Pointer to context information (LAN_ADAPTER) + * NdisPacket = Pointer to NDIS packet to send + * Offset = Offset in packet where data starts + * LinkAddress = Pointer to link address of destination (NULL = broadcast) + * Type = LAN protocol type (LAN_PROTO_*) + */ +{ + NDIS_STATUS NdisStatus; + PETH_HEADER EHeader; + PVOID Data; + PLAN_ADAPTER Adapter = (PLAN_ADAPTER)Context; + + 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 */ + Data = AdjustPacket(NdisPacket, Offset, Adapter->HeaderSize); + PC(NdisPacket)->DLOffset = Offset; + + if (Adapter->State == LAN_STATE_STARTED) { + switch (Adapter->Media) { + case NdisMedium802_3: + EHeader = (PETH_HEADER)Data; + + if (LinkAddress) + /* Unicast address */ + RtlCopyMemory(EHeader->DstAddr, LinkAddress, IEEE_802_ADDR_LENGTH); + else + /* Broadcast address */ + RtlFillMemory(EHeader->DstAddr, IEEE_802_ADDR_LENGTH, 0xFF); + + RtlCopyMemory(EHeader->SrcAddr, Adapter->HWAddress, IEEE_802_ADDR_LENGTH); + + switch (Type) { + case LAN_PROTO_IPv4: + EHeader->EType = ETYPE_IPv4; + break; + case LAN_PROTO_ARP: + EHeader->EType = ETYPE_ARP; + break; + case LAN_PROTO_IPv6: + EHeader->EType = ETYPE_IPv6; + break; + default: +#if DBG + /* Should not happen */ + TI_DbgPrint(MIN_TRACE, ("Unknown LAN protocol.\n")); + + ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NDIS_STATUS_FAILURE); +#endif + return; + } + break; + + default: + /* FIXME: Support other medias */ + break; + } + + NdisSend(&NdisStatus, Adapter->NdisHandle, NdisPacket); + if (NdisStatus != NDIS_STATUS_PENDING) + ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NdisStatus); + } else + ProtocolSendComplete((NDIS_HANDLE)Context, NdisPacket, NDIS_STATUS_CLOSED); +} + + +VOID BindAdapter( + PLAN_ADAPTER Adapter) +/* + * FUNCTION: Binds a LAN adapter to IP layer + * ARGUMENTS: + * Adapter = Pointer to LAN_ADAPTER structure + * NOTES: + * We set the lookahead buffer size, set the packet filter and + * bind the adapter to IP layer + */ +{ + INT i; + PIP_INTERFACE IF; + PIP_ADDRESS Address; + PNDIS_PACKET Packet; + NDIS_STATUS NdisStatus; + LLIP_BIND_INFO BindInfo; + ULONG Lookahead = LOOKAHEAD_SIZE; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + Adapter->State = LAN_STATE_OPENING; + + NdisStatus = NDISCall(Adapter, NdisRequestSetInformation, + OID_GEN_CURRENT_LOOKAHEAD, &Lookahead, sizeof(ULONG)); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TI_DbgPrint(MID_TRACE, ("Could not set lookahead buffer size (0x%X).\n", NdisStatus)); + return; + } + + /* Allocate packets for NdisTransferData */ + /* FIXME: How many should we allocate? */ + Adapter->TDPackets = NULL; + for (i = 0; i < 2; i++) { + Packet = AllocateTDPacket(Adapter); + PC(Packet)->Context = Adapter->TDPackets; + Adapter->TDPackets = Packet; + if (!Packet) { + TI_DbgPrint(MID_TRACE, ("Could not allocate transfer data packet (out of resources).\n")); + FreeTDPackets(Adapter); + return; + } + } + + /* Bind the adapter to IP layer */ + BindInfo.Context = Adapter; + BindInfo.HeaderSize = Adapter->HeaderSize; + BindInfo.MinFrameSize = Adapter->MinFrameSize; + BindInfo.MTU = Adapter->MTU; + BindInfo.Address = (PUCHAR)&Adapter->HWAddress; + BindInfo.AddressLength = Adapter->HWAddressLength; + BindInfo.Transmit = LANTransmit; + + IF = IPCreateInterface(&BindInfo); + if (!IF) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + FreeTDPackets(Adapter); + return; + } + + /* FIXME: Get address from registry. + For now just use a private address, eg. 10.0.0.10 */ + Address = AddrBuildIPv4(0x0A00000A); + if (!Address) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + FreeTDPackets(Adapter); + IPDestroyInterface(Adapter->Context); + return; + } + /* Create a net table entry for this interface */ + if (!IPCreateNTE(IF, Address, 8)) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + FreeTDPackets(Adapter); + IPDestroyInterface(IF); + return; + } + + /* Reference the interface for the NTE. The reference for + the address is just passed on to the NTE */ + ReferenceObject(IF); + + /* Register interface with IP layer */ + IPRegisterInterface(IF); + + /* Set packet filter so we can send and receive packets */ + NdisStatus = NDISCall(Adapter, NdisRequestSetInformation, + OID_GEN_CURRENT_PACKET_FILTER, &Adapter->PacketFilter, sizeof(UINT)); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TI_DbgPrint(MID_TRACE, ("Could not set packet filter (0x%X).\n", NdisStatus)); + FreeTDPackets(Adapter); + IPDestroyInterface(IF); + return; + } + + Adapter->Context = IF; + + Adapter->State = LAN_STATE_STARTED; +} + + +VOID UnbindAdapter( + PLAN_ADAPTER Adapter) +/* + * FUNCTION: Unbinds a LAN adapter from IP layer + * ARGUMENTS: + * Adapter = Pointer to LAN_ADAPTER structure + */ +{ + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + if (Adapter->State == LAN_STATE_STARTED) { + PIP_INTERFACE IF = Adapter->Context; + + IPUnregisterInterface(IF); + + IPDestroyInterface(IF); + + /* Free transfer data packets */ + FreeTDPackets(Adapter); + } +} + + +NDIS_STATUS LANRegisterAdapter( + PNDIS_STRING AdapterName, + PLAN_ADAPTER *Adapter) +/* + * FUNCTION: Registers protocol with an NDIS adapter + * ARGUMENTS: + * AdapterName = Pointer to string with name of adapter to register + * Adapter = Address of pointer to a LAN_ADAPTER structure + * RETURNS: + * Status of operation + */ +{ + PLAN_ADAPTER IF; + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenStatus; + UINT MediaIndex; + NDIS_MEDIUM MediaArray[MAX_MEDIA]; + UINT AddressOID; + UINT Speed; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + IF = ExAllocatePool(NonPagedPool, sizeof(LAN_ADAPTER)); + if (!IF) + return NDIS_STATUS_RESOURCES; + + RtlZeroMemory(IF, sizeof(LAN_ADAPTER)); + + /* Put adapter in stopped state */ + IF->State = LAN_STATE_STOPPED; + + /* Initialize protecting spin lock */ + KeInitializeSpinLock(&IF->Lock); + + KeInitializeEvent(&IF->Event, SynchronizationEvent, FALSE); + + /* Initialize array with media IDs we support */ + MediaArray[MEDIA_ETH] = NdisMedium802_3; + + /* Open the adapter. */ + NdisOpenAdapter(&NdisStatus, &OpenStatus, &IF->NdisHandle, &MediaIndex, + MediaArray, MAX_MEDIA, NdisProtocolHandle, IF, AdapterName, 0, NULL); + + /* Wait until the adapter is opened */ + if (NdisStatus == NDIS_STATUS_PENDING) + KeWaitForSingleObject(&IF->Event, UserRequest, KernelMode, FALSE, NULL); + else if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool(IF); + return NdisStatus; + } + + IF->Media = MediaArray[MediaIndex]; + + /* Fill LAN_ADAPTER structure with some adapter specific information */ + switch (IF->Media) { + case NdisMedium802_3: + IF->HWAddressLength = IEEE_802_ADDR_LENGTH; + IF->BCastMask = BCAST_ETH_MASK; + IF->BCastCheck = BCAST_ETH_CHECK; + IF->BCastOffset = BCAST_ETH_OFFSET; + IF->HeaderSize = sizeof(ETH_HEADER); + IF->MinFrameSize = 60; + AddressOID = OID_802_3_CURRENT_ADDRESS; + IF->PacketFilter = + NDIS_PACKET_TYPE_BROADCAST | + NDIS_PACKET_TYPE_DIRECTED | + NDIS_PACKET_TYPE_MULTICAST; + break; + + default: + /* Unsupported media */ + TI_DbgPrint(MIN_TRACE, ("Unsupported media.\n")); + ExFreePool(IF); + return NDIS_STATUS_NOT_SUPPORTED; + } + + /* Get maximum frame size */ + NdisStatus = NDISCall(IF, NdisRequestQueryInformation, + OID_GEN_MAXIMUM_FRAME_SIZE, &IF->MTU, sizeof(UINT)); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool(IF); + return NdisStatus; + } + + /* Get maximum packet size */ + NdisStatus = NDISCall(IF, NdisRequestQueryInformation, + OID_GEN_MAXIMUM_TOTAL_SIZE, &IF->MaxPacketSize, sizeof(UINT)); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TI_DbgPrint(MIN_TRACE, ("Query for maximum packet size failed.\n")); + ExFreePool(IF); + return NdisStatus; + } + + /* Get maximum number of packets we can pass to NdisSend(Packets) at one time */ + NdisStatus = NDISCall(IF, NdisRequestQueryInformation, + OID_GEN_MAXIMUM_SEND_PACKETS, &IF->MaxSendPackets, sizeof(UINT)); + if (NdisStatus != NDIS_STATUS_SUCCESS) + /* Legacy NIC drivers may not support this query, if it fails we + assume it can send at least one packet per call to NdisSend(Packets) */ + IF->MaxSendPackets = 1; + + /* Get current hardware address */ + NdisStatus = NDISCall(IF, NdisRequestQueryInformation, AddressOID, + IF->HWAddress, IF->HWAddressLength); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TI_DbgPrint(MIN_TRACE, ("Query for current hardware address failed.\n")); + ExFreePool(IF); + return NdisStatus; + } + + /* Get maximum link speed */ + NdisStatus = NDISCall(IF, NdisRequestQueryInformation, + OID_GEN_LINK_SPEED, &Speed, sizeof(UINT)); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TI_DbgPrint(MIN_TRACE, ("Query for maximum link speed failed.\n")); + ExFreePool(IF); + return NdisStatus; + } + + /* Convert returned link speed to bps (it is in 100bps increments) */ + IF->Speed = Speed * 100L; + + *Adapter = IF; + + /* Add adapter to the adapter list */ + IF->Next = Adapters; + Adapters = IF; + + /* Bind adapter to IP layer */ + BindAdapter(IF); + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); + + return NDIS_STATUS_SUCCESS; +} + + +NDIS_STATUS LANUnregisterAdapter( + PLAN_ADAPTER Adapter) +/* + * FUNCTION: Unregisters protocol with NDIS adapter + * ARGUMENTS: + * Adapter = Pointer to a LAN_ADAPTER structure + * RETURNS: + * Status of operation + */ +{ + KIRQL OldIrql; + NDIS_HANDLE NdisHandle; + PLAN_ADAPTER IF, PrevIF; + BOOLEAN Found = FALSE; + NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + /* Search the adapter list for the specified adapter and remove it */ + IF = Adapters; + if (IF) { + if (Adapter != Adapters) { + PrevIF = IF; + while ((IF) && (!Found)) { + if (IF == Adapter) { + /* We've found the adapter, now remove it from the list */ + PrevIF->Next = IF->Next; + Found = TRUE; + } + PrevIF = IF; + IF = IF->Next; + } + } else { + Adapters = NULL; + Found = TRUE; + } + } + if (!Found) { + TI_DbgPrint(MIN_TRACE, ("Leaving (adapter was not in list).\n")); + return NDIS_STATUS_ADAPTER_NOT_FOUND; + } + + /* Unbind adapter from IP layer */ + UnbindAdapter(Adapter); + + KeAcquireSpinLock(&Adapter->Lock, &OldIrql); + NdisHandle = Adapter->NdisHandle; + if (NdisHandle) { + Adapter->NdisHandle = NULL; + KeReleaseSpinLock(&Adapter->Lock, OldIrql); + + NdisCloseAdapter(&NdisStatus, NdisHandle); + if (NdisStatus == NDIS_STATUS_PENDING) { + KeWaitForSingleObject(&Adapter->Event, + UserRequest, KernelMode, FALSE, NULL); + NdisStatus = Adapter->NdisStatus; + } + } else + KeReleaseSpinLock(&Adapter->Lock, OldIrql); + + FreeAdapter(Adapter); + + return NDIS_STATUS_SUCCESS; +} + + +NTSTATUS LANRegisterProtocol( + PSTRING Name) +/* + * FUNCTION: Registers this protocol driver with NDIS + * ARGUMENTS: + * Name = Name of this protocol driver + * RETURNS: + * Status of operation + */ +{ + NDIS_STATUS NdisStatus; + NDIS_PROTOCOL_CHARACTERISTICS ProtChars; + + /* Set up protocol characteristics */ + RtlZeroMemory(&ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); + ProtChars.MajorNdisVersion = NDIS_VERSION_MAJOR; + ProtChars.MinorNdisVersion = NDIS_VERSION_MINOR; + ProtChars.Name.Length = Name->Length; + ProtChars.Name.Buffer = (PVOID)Name->Buffer; + ProtChars.OpenAdapterCompleteHandler = ProtocolOpenAdapterComplete; + ProtChars.CloseAdapterCompleteHandler = ProtocolCloseAdapterComplete; + ProtChars.ResetCompleteHandler = ProtocolResetComplete; + ProtChars.RequestCompleteHandler = ProtocolRequestComplete; + ProtChars.SendCompleteHandler = ProtocolSendComplete; + ProtChars.TransferDataCompleteHandler = ProtocolTransferDataComplete; + ProtChars.ReceiveHandler = ProtocolReceive; + ProtChars.ReceiveCompleteHandler = ProtocolReceiveComplete; + ProtChars.StatusHandler = ProtocolStatus; + ProtChars.StatusCompleteHandler = ProtocolStatusComplete; + + /* Try to register protocol */ + NdisRegisterProtocol( + &NdisStatus, &NdisProtocolHandle, &ProtChars, + sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + Name->Length); + if (NdisStatus != NDIS_STATUS_SUCCESS) + return (NTSTATUS)NdisStatus; + + ProtocolRegistered = TRUE; + + return STATUS_SUCCESS; +} + + +VOID LANUnregisterProtocol( + VOID) +/* + * FUNCTION: Unregisters this protocol driver with NDIS + * NOTES: Does not care wether we are already registered + */ +{ + if (ProtocolRegistered) { + NDIS_STATUS NdisStatus; + + while (Adapters) + NdisStatus = LANUnregisterAdapter(Adapters); + + NdisDeregisterProtocol(&NdisStatus, NdisProtocolHandle); + ProtocolRegistered = FALSE; + } +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/datalink/loopback.c b/reactos/drivers/net/tcpip/datalink/loopback.c new file mode 100644 index 00000000000..d42e499fa56 --- /dev/null +++ b/reactos/drivers/net/tcpip/datalink/loopback.c @@ -0,0 +1,224 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + + +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 LoopLock; + + +VOID RealTransmit( + PVOID Context) +/* + * FUNCTION: Transmits one or more packet(s) in loopback queue to ourselves + * ARGUMENTS: + * Context = Pointer to context information (loopback interface) + */ +{ + KIRQL OldIrql; + PNDIS_PACKET NdisPacket; + IP_PACKET IPPacket; + PNDIS_BUFFER Buffer; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + KeAcquireSpinLockAtDpcLevel(&LoopLock); + + for (;;) { + /* Get the next packet from the queue (if any) */ + NdisPacket = LoopQueueHead; + if (!NdisPacket) + break; + + LoopQueueHead = *(PNDIS_PACKET*)NdisPacket->MacReserved; + + KeReleaseSpinLockFromDpcLevel(&LoopLock); + + IPPacket.NdisPacket = NdisPacket; + + NdisGetFirstBufferFromPacket(NdisPacket, + &Buffer, + &IPPacket.Header, + &IPPacket.ContigSize, + &IPPacket.TotalSize); + + IPReceive(Context, &IPPacket); + + AdjustPacket(NdisPacket, 0, PC(NdisPacket)->DLOffset); + + PC(NdisPacket)->DLComplete(Context, NdisPacket, NDIS_STATUS_SUCCESS); + + /* Lower IRQL for a moment to prevent starvation */ + KeLowerIrql(OldIrql); + + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + + KeAcquireSpinLockAtDpcLevel(&LoopLock); + } + + LoopBusy = FALSE; + + KeReleaseSpinLockFromDpcLevel(&LoopLock); + + KeLowerIrql(OldIrql); +} + + +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->MacReserved; + *pNdisPacket = NULL; + + KeAcquireSpinLock(&LoopLock, &OldIrql); + + /* Add packet to transmit queue */ + if (LoopQueueHead) { + /* Transmit queue is not empty */ + pNdisPacket = (PNDIS_PACKET*)LoopQueueTail->MacReserved; + *pNdisPacket = NdisPacket; + } else + /* Transmit queue is empty */ + LoopQueueHead = NdisPacket; + + LoopQueueTail = NdisPacket; + + /* If LoopTransmit is not running (or scheduled), schedule it to run */ + if (!LoopBusy) { + ExQueueWorkItem(&LoopWorkItem, CriticalWorkQueue); + LoopBusy = TRUE; + } + + KeReleaseSpinLock(&LoopLock, OldIrql); +} + + +NDIS_STATUS LoopRegisterAdapter( + PNDIS_STRING AdapterName, + PLAN_ADAPTER *Adapter) +/* + * FUNCTION: Registers loopback adapter + * ARGUMENTS: + * AdapterName = Unused + * Adapter = Unused + * RETURNS: + * Status of operation + */ +{ + PIP_ADDRESS Address; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + Address = AddrBuildIPv4(LOOPBACK_ADDRESS_IPv4); + if (Address) { + LLIP_BIND_INFO BindInfo; + + /* Bind the adapter to 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) && (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(&LoopLock); + 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 + * 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) { + IPUnregisterInterface(Loopback); + IPDestroyInterface(Loopback); + Loopback = NULL; + } + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); + + return NDIS_STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/address.h b/reactos/drivers/net/tcpip/include/address.h new file mode 100644 index 00000000000..aeb953d5c90 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/address.h @@ -0,0 +1,63 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/address.h + * PURPOSE: Address manipulation prototypes + */ +#ifndef __ADDRESS_H +#define __ADDRESS_H + + +/* + * Initialize an IPv4 style address + * VOID AddrInitIPv4( + * PIP_ADDRESS IPAddress, + * IPv4_RAW_ADDRESS RawAddress) + */ +#define AddrInitIPv4(IPAddress, RawAddress) \ +{ \ + (IPAddress)->RefCount = 1; \ + (IPAddress)->Type = IP_ADDRESS_V4; \ + (IPAddress)->Address.IPv4Address = (RawAddress); \ +} + + +BOOLEAN AddrIsUnspecified( + PIP_ADDRESS Address); + +NTSTATUS AddrGetAddress( + PTRANSPORT_ADDRESS AddrList, + PIP_ADDRESS *Address, + PUSHORT Port, + PIP_ADDRESS *Cache); + +BOOLEAN AddrIsEqual( + PIP_ADDRESS Address1, + PIP_ADDRESS Address2); + +INT AddrCompare( + PIP_ADDRESS Address1, + PIP_ADDRESS Address2); + +BOOLEAN AddrIsEqualIPv4( + PIP_ADDRESS Address1, + IPv4_RAW_ADDRESS Address2); + +PIP_ADDRESS AddrBuildIPv4( + IPv4_RAW_ADDRESS Address); + +PADDRESS_ENTRY AddrLocateADEv4( + IPv4_RAW_ADDRESS Address); + +PADDRESS_FILE AddrSearchFirst( + PIP_ADDRESS Address, + USHORT Port, + USHORT Protocol, + PAF_SEARCH SearchContext); + +PADDRESS_FILE AddrSearchNext( + PAF_SEARCH SearchContext); + +#endif /* __ADDRESS_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/arp.h b/reactos/drivers/net/tcpip/include/arp.h new file mode 100644 index 00000000000..b33ad147b38 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/arp.h @@ -0,0 +1,37 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/arp.h + * PURPOSE: Address Resolution Protocol definitions + */ +#ifndef __ARP_H +#define __ARP_H + +typedef struct ARP_HEADER { + USHORT HWType; /* Hardware Type */ + USHORT ProtoType; /* Protocol Type */ + UCHAR HWAddrLen; /* Hardware Address Length */ + UCHAR ProtoAddrLen; /* Protocol Address Length */ + USHORT Opcode; /* Opcode */ + /* Sender's Hardware Address */ + /* Sender's Protocol Address */ + /* Target's Hardware Address */ + /* Target's Protocol Address */ +} ARP_HEADER, *PARP_HEADER; + +/* We swap constants so we can compare values at runtime without swapping them */ +#define ARP_OPCODE_REQUEST WH2N(0x0001) /* ARP request */ +#define ARP_OPCODE_REPLY WH2N(0x0002) /* ARP reply */ + + +BOOLEAN ARPTransmit( + PIP_ADDRESS Address, + PNET_TABLE_ENTRY NTE); + +VOID ARPReceive( + PVOID Context, + PIP_PACKET Packet); + +#endif /* __ARP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/checksum.h b/reactos/drivers/net/tcpip/include/checksum.h new file mode 100644 index 00000000000..5805b514c0e --- /dev/null +++ b/reactos/drivers/net/tcpip/include/checksum.h @@ -0,0 +1,27 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/checksum.h + * PURPOSE: Checksum routine definitions + */ +#ifndef __CHECKSUM_H +#define __CHECKSUM_H + + +ULONG ChecksumCompute( + PVOID Data, + UINT Count, + ULONG Seed); + +#define IPv4Checksum(Data, Count, Seed)(ChecksumCompute(Data, Count, Seed)) + +/* + * Macro to check for a correct checksum + * BOOLEAN CorrectChecksum(PVOID Data, UINT Count) + */ +#define CorrectChecksum(Data, Count) \ + (BOOLEAN)(IPv4Checksum(Data, Count, 0) == DH2N(0x0000FFFF)) + +#endif /* __CHECKSUM_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/datagram.h b/reactos/drivers/net/tcpip/include/datagram.h new file mode 100644 index 00000000000..3dd3a7964df --- /dev/null +++ b/reactos/drivers/net/tcpip/include/datagram.h @@ -0,0 +1,49 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/datagram.h + * PURPOSE: Datagram types and constants + */ +#ifndef __DATAGRAM_H +#define __DATAGRAM_H + +#include + + +VOID DGSend( + PVOID Context, + PDATAGRAM_SEND_REQUEST SendRequest); + +VOID DGCancelSendRequest( + PADDRESS_FILE AddrFile, + PVOID Context); + +VOID DGCancelReceiveRequest( + PADDRESS_FILE AddrFile, + PVOID Context); + +NTSTATUS DGSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize, + DATAGRAM_BUILD_ROUTINE Build); + +NTSTATUS DGReceiveDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG ReceiveLength, + ULONG ReceiveFlags, + PTDI_CONNECTION_INFORMATION ReturnInfo, + PULONG BytesReceived); + +NTSTATUS DGStartup( + VOID); + +NTSTATUS DGShutdown( + VOID); + +#endif /* __DATAGRAM_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/debug.h b/reactos/drivers/net/tcpip/include/debug.h new file mode 100644 index 00000000000..a296d5ff820 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/debug.h @@ -0,0 +1,99 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/debug.h + * PURPOSE: Debugging support macros + * DEFINES: DBG - Enable debug output + * NASSERT - Disable assertions + */ +#ifndef __DEBUG_H +#define __DEBUG_H + +#define NORMAL_MASK 0x000000FF +#define SPECIAL_MASK 0xFFFFFF00 +#define MIN_TRACE 0x00000001 +#define MID_TRACE 0x00000002 +#define MAX_TRACE 0x00000003 + +#define DEBUG_MEMORY 0x00000100 +#define DEBUG_BUFFER 0x00000200 +#define DEBUG_IRP 0x00000400 +#define DEBUG_REFCOUNT 0x00000800 +#define DEBUG_ADDRFILE 0x00001000 +#define DEBUG_IP 0x00002000 +#define DEBUG_ROUTER 0x00004000 +#define DEBUG_RCACHE 0x00008000 +#define DEBUG_NCACHE 0x00010000 +#define DEBUG_ULTRA 0xFFFFFFFF + +#ifdef DBG + +extern DWORD DebugTraceLevel; + +#ifdef _MSC_VER + +#define TI_DbgPrint(_t_, _x_) \ + if (((DebugTraceLevel & NORMAL_MASK) >= _t_) || \ + ((DebugTraceLevel & _t_) > NORMAL_MASK)) { \ + DbgPrint("(%s:%d) ", __FILE__, __LINE__); \ + DbgPrint _x_ ; \ + } + +#else /* _MSC_VER */ + +#define TI_DbgPrint(_t_, _x_) \ + if (((DebugTraceLevel & NORMAL_MASK) >= _t_) || \ + ((DebugTraceLevel & _t_) > NORMAL_MASK)) { \ + DbgPrint("(%s:%d)(%s) ", __FILE__, __LINE__, __FUNCTION__); \ + DbgPrint _x_ ; \ + } + +#endif /* _MSC_VER */ + +#ifdef ASSERT +#undef ASSERT +#endif + +#ifdef NASSERT +#define ASSERT(x) +#else /* NASSERT */ +#define ASSERT(x) if (!(x)) { TI_DbgPrint(MIN_TRACE, ("Assertion "#x" failed at %s:%d\n", __FILE__, __LINE__)); KeBugCheck(0); } +#endif /* NASSERT */ + +#define ASSERT_IRQL(x) ASSERT(KeGetCurrentIrql() <= (x)) + +#else /* DBG */ + +#define TI_DbgPrint(_t_, _x_) + +#define ASSERT_IRQL(x) +#define ASSERT(x) + +#endif /* DBG */ + + +#define assert(x) ASSERT(x) +#define assert_irql(x) ASSERT_IRQL(x) + + +#ifdef _MSC_VER + +#define UNIMPLEMENTED \ + TI_DbgPrint(MIN_TRACE, ("The function at %s:%d is unimplemented, \ + but come back another day.\n", __FILE__, __LINE__)); + +#else /* _MSC_VER */ + +#define UNIMPLEMENTED \ + TI_DbgPrint(MIN_TRACE, ("(%s:%d)(%s) is unimplemented, \ + but come back another day.\n", __FILE__, __LINE__, __FUNCTION__)); + +#endif /* _MSC_VER */ + + +#define CHECKPOINT \ + do { TI_DbgPrint(MIN_TRACE, ("(%s:%d)\n", __FILE__, __LINE__)); } while(0); + +#endif /* __DEBUG_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/dispatch.h b/reactos/drivers/net/tcpip/include/dispatch.h new file mode 100644 index 00000000000..9f704859517 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/dispatch.h @@ -0,0 +1,61 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/dispatch.h + * PURPOSE: Dispatch routine prototypes + */ +#ifndef __DISPATCH_H +#define __DISPATCH_H + + +NTSTATUS DispTdiAccept( + PIRP Irp); + +NTSTATUS DispTdiAssociateAddress( + PIRP Irp); + +NTSTATUS DispTdiConnect( + PIRP Irp); + +NTSTATUS DispTdiDisassociateAddress( + PIRP Irp); + +NTSTATUS DispTdiDisconnect( + PIRP Irp); + +NTSTATUS DispTdiListen( + PIRP Irp); + +NTSTATUS DispTdiQueryInformation( + PDEVICE_OBJECT DeviceObject, + PIRP Irp); + +NTSTATUS DispTdiReceive( + PIRP Irp); + +NTSTATUS DispTdiReceiveDatagram( + PIRP Irp); + +NTSTATUS DispTdiSend( + PIRP Irp); + +NTSTATUS DispTdiSendDatagram( + PIRP Irp); + +NTSTATUS DispTdiSetEventHandler( + PIRP Irp); + +NTSTATUS DispTdiSetInformation( + PIRP Irp); + +NTSTATUS DispTdiQueryInformationEx( + PIRP Irp, + PIO_STACK_LOCATION IrpSp); + +NTSTATUS DispTdiSetInformationEx( + PIRP Irp, + PIO_STACK_LOCATION IrpSp); + +#endif /* __DISPATCH_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/fileobjs.h b/reactos/drivers/net/tcpip/include/fileobjs.h new file mode 100644 index 00000000000..e69e5fe1e53 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/fileobjs.h @@ -0,0 +1,32 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/fileobjs.h + * PURPOSE: File object routine prototypes + */ +#ifndef __FILEOBJS_H +#define __FILEOBJS_H + + +extern LIST_ENTRY AddressFileListHead; +extern KSPIN_LOCK AddressFileListLock; + + +NTSTATUS FileOpenAddress( + PTDI_REQUEST Request, + PTA_ADDRESS_IP AddrList, + USHORT Protocol, + PVOID Options); + +NTSTATUS FileCloseAddress( + PTDI_REQUEST Request); + +NTSTATUS FileCloseConnection( + PTDI_REQUEST Request); + +NTSTATUS FileCloseControlChannel( + PTDI_REQUEST Request); + +#endif /* __FILEOBJS_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/icmp.h b/reactos/drivers/net/tcpip/include/icmp.h new file mode 100644 index 00000000000..225e62de449 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/icmp.h @@ -0,0 +1,68 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/icmp.h + * PURPOSE: Internet Control Message Protocol definitions + */ +#ifndef __ICMP_H +#define __ICMP_H + +typedef struct ICMP_HEADER { + UCHAR Type; /* ICMP message type */ + UCHAR Code; /* ICMP message code */ + USHORT Checksum; /* ICMP message checksum */ + ULONG Unused; /* ICMP unused */ +} ICMP_HEADER, *PICMP_HEADER; + +/* ICMP message types */ +#define ICMP_TYPE_ECHO_REPLY 0 /* Echo reply */ +#define ICMP_TYPE_DEST_UNREACH 3 /* Destination unreachable */ +#define ICMP_TYPE_SOURCE_QUENCH 4 /* Source quench */ +#define ICMP_TYPE_REDIRECT 5 /* Redirect */ +#define ICMP_TYPE_ECHO_REQUEST 8 /* Echo request */ +#define ICMP_TYPE_TIME_EXCEEDED 11 /* Time exceeded */ +#define ICMP_TYPE_PARAMETER 12 /* Parameter problem */ +#define ICMP_TYPE_TIMESTAMP_REQUEST 13 /* Timestamp request */ +#define ICMP_TYPE_TIMESTAMP_REPLY 14 /* Timestamp reply */ +#define ICMP_TYPE_INFO_REQUEST 15 /* Information request */ +#define ICMP_TYPE_INFO_REPLY 16 /* Information reply */ + +/* ICMP codes for ICMP_TYPE_DEST_UNREACH */ +#define ICMP_CODE_DU_NET_UNREACH 0 /* Network unreachable */ +#define ICMP_CODE_DU_HOST_UNREACH 1 /* Host unreachable */ +#define ICMP_CODE_DU_PROTOCOL_UNREACH 2 /* Protocol unreachable */ +#define ICMP_CODE_DU_PORT_UNREACH 3 /* Port unreachable */ +#define ICMP_CODE_DU_FRAG_DF_SET 4 /* Fragmentation needed and DF set */ +#define ICMP_CODE_DU_SOURCE_ROUTE_FAILED 5 /* Source route failed */ + +/* ICMP codes for ICMP_TYPE_REDIRECT */ +#define ICMP_CODE_RD_NET 0 /* Redirect datagrams for the network */ +#define ICMP_CODE_RD_HOST 1 /* Redirect datagrams for the host */ +#define ICMP_CODE_RD_TOS_NET 2 /* Redirect datagrams for the Type of Service and network */ +#define ICMP_CODE_RD_TOS_HOST 3 /* Redirect datagrams for the Type of Service and host */ + +/* ICMP codes for ICMP_TYPE_TIME_EXCEEDED */ +#define ICMP_CODE_TE_TTL 0 /* Time to live exceeded in transit */ +#define ICMP_CODE_TE_REASSEMBLY 1 /* Fragment reassembly time exceeded */ + +/* ICMP codes for ICMP_TYPE_PARAMETER */ +#define ICMP_CODE_TP_POINTER 1 /* Pointer indicates the error */ + + +VOID ICMPReceive( + PNET_TABLE_ENTRY NTE, + PIP_PACKET IPPacket); + +VOID ICMPTransmit( + PNET_TABLE_ENTRY NTE, + PIP_PACKET IPPacket); + +VOID ICMPReply( + PNET_TABLE_ENTRY NTE, + PIP_PACKET IPPacket, + UCHAR Type, + UCHAR Code); + +#endif /* __ICMP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/info.h b/reactos/drivers/net/tcpip/include/info.h new file mode 100644 index 00000000000..1fd37d69225 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/info.h @@ -0,0 +1,93 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/info.h + * PURPOSE: TdiQueryInformation definitions + */ +#ifndef __INFO_H +#define __INFO_H + + +typedef struct IPSNMP_INFO { + ULONG Forwarding; + ULONG DefaultTTL; + ULONG InReceives; + ULONG InHdrErrors; + ULONG InAddrErrors; + ULONG ForwDatagrams; + ULONG InUnknownProtos; + ULONG InDiscards; + ULONG InDelivers; + ULONG OutRequests; + ULONG RoutingDiscards; + ULONG OutDiscards; + ULONG OutNoRoutes; + ULONG ReasmTimeout; + ULONG ReasmReqds; + ULONG ReasmOks; + ULONG ReasmFails; + ULONG FragOks; + ULONG FragFails; + ULONG FragCreates; + ULONG NumIf; + ULONG NumAddr; + ULONG NumRoutes; +} IPSNMP_INFO, *PIPSNMP_INFO; + +typedef struct IPADDR_ENTRY { + ULONG Addr; + ULONG Index; + ULONG Mask; + ULONG BcastAddr; + ULONG ReasmSize; + USHORT Context; + USHORT Pad; +} IPADDR_ENTRY, *PIPADDR_ENTRY; + +#define IP_MIB_STATS_ID 1 +#define IP_MIB_ADDRTABLE_ENTRY_ID 0x102 + +#define MAX_PHYSADDR_SIZE 8 + + +/* Only UDP is supported */ +#define TDI_SERVICE_FLAGS (TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_BROADCAST_SUPPORTED) + +#define TCP_MIB_STAT_ID 1 +#define UDP_MIB_STAT_ID 1 +#define TCP_MIB_TABLE_ID 0x101 +#define UDP_MIB_TABLE_ID 0x101 + +#define TL_INSTANCE 0 + + +typedef struct ADDRESS_INFO { + ULONG LocalAddress; + ULONG LocalPort; +} ADDRESS_INFO, *PADDRESS_INFO; + +typedef union TDI_INFO { + TDI_CONNECTION_INFO ConnInfo; + TDI_ADDRESS_INFO AddrInfo; + TDI_PROVIDER_INFO ProviderInfo; + TDI_PROVIDER_STATISTICS ProviderStats; +} TDI_INFO, *PTDI_INFO; + + +TDI_STATUS InfoTdiQueryInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PNDIS_BUFFER Buffer, + PUINT BufferSize, + PVOID Context); + +TDI_STATUS InfoTdiSetInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PVOID Buffer, + UINT BufferSize); + +#endif /* __INFO_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/ip.h b/reactos/drivers/net/tcpip/include/ip.h new file mode 100644 index 00000000000..4cbcd190b35 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/ip.h @@ -0,0 +1,245 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/ip.h + * PURPOSE: Internet Protocol related definitions + */ +#ifndef __IP_H +#define __IP_H + +/* Raw IPv4 style address */ +typedef ULONG IPv4_RAW_ADDRESS; +typedef IPv4_RAW_ADDRESS *PIPv4_RAW_ADDRESS; + +/* Raw IPv6 style address */ +typedef USHORT IPv6_RAW_ADDRESS[8]; +typedef IPv6_RAW_ADDRESS *PIPv6_RAW_ADDRESS; + +/* IP style address */ +typedef struct IP_ADDRESS { + ULONG RefCount; /* Number of references to this address */ + UCHAR Type; /* Type of IP address */ + union { + IPv4_RAW_ADDRESS IPv4Address; /* IPv4 address */ + PIPv6_RAW_ADDRESS IPv6Address; /* IPv6 address */ + } Address; +} IP_ADDRESS, *PIP_ADDRESS; + +/* IP type constants */ +#define IP_ADDRESS_V4 0x00 /* IPv4 style address */ +#define IP_ADDRESS_V6 0x01 /* IPv6 style address */ + + +/* IPv4 header format */ +typedef struct IPv4_HEADER { + UCHAR VerIHL; /* 4-bit version, 4-bit Internet Header Length */ + UCHAR Tos; /* Type of Service */ + USHORT TotalLength; /* Total Length */ + USHORT Id; /* Identification */ + USHORT FlagsFragOfs; /* 3-bit Flags, 13-bit Fragment Offset */ + UCHAR Ttl; /* Time to Live */ + UCHAR Protocol; /* Protocol */ + USHORT Checksum; /* Header Checksum */ + IPv4_RAW_ADDRESS SrcAddr; /* Source Address */ + IPv4_RAW_ADDRESS DstAddr; /* Destination Address */ +} IPv4_HEADER, *PIPv4_HEADER; + +#define IPv4_FRAGOFS_MASK 0x1FFF +#define IPv4_MF_MASK 0x2000 +#define IPv4_MAX_HEADER_SIZE 60 + +/* Packet completion handler prototype */ +typedef VOID (*PACKET_COMPLETION_ROUTINE)( + PVOID Context, + PNDIS_PACKET NdisPacket, + NDIS_STATUS NdisStatus); + +/* Structure for an IP packet */ +typedef struct IP_PACKET { + ULONG RefCount; /* Reference count for this object */ + UCHAR Type; /* Type of IP packet (see IP_ADDRESS_xx above) */ + PVOID Header; /* Pointer to IP header for this packet */ + UINT HeaderSize; /* Size of IP header */ + PVOID Data; /* Current pointer into packet data */ + UINT TotalSize; /* Total amount of data in packet (IP header and data) */ + UINT ContigSize; /* Number of contiguous bytes left in current buffer */ + UINT Position; /* Current logical offset into packet */ + PNDIS_PACKET NdisPacket; /* Pointer to NDIS packet */ + IP_ADDRESS SrcAddr; /* Source address */ + IP_ADDRESS DstAddr; /* Destination address */ +} IP_PACKET, *PIP_PACKET; + +/* Packet context */ +typedef struct PACKET_CONTEXT { + PACKET_COMPLETION_ROUTINE Complete; /* Transport level completion handler */ + PVOID Context; /* Context information for handler */ + PACKET_COMPLETION_ROUTINE DLComplete; /* Data link level completion handler. Also + used to link to next packet in a queue */ + UINT DLOffset; /* Offset where data (IP header) starts */ +} PACKET_CONTEXT, *PPACKET_CONTEXT; + +/* The ProtocolReserved field is structured as a PACKET_CONTEXT */ +#define PC(Packet) ((PPACKET_CONTEXT)(&Packet->ProtocolReserved)) + + +/* Address information a.k.a ADE */ +typedef struct _ADDRESS_ENTRY { + LIST_ENTRY ListEntry; /* Entry on list */ + ULONG RefCount; /* Reference count */ + struct _NET_TABLE_ENTRY *NTE; /* NTE associated with this address */ + UCHAR Type; /* Address type */ + PIP_ADDRESS Address; /* Pointer to address identifying this entry */ +} ADDRESS_ENTRY, *PADDRESS_ENTRY; + +/* Values for address type */ +#define ADE_UNICAST 0x01 +#define ADE_MULTICAST 0x02 +#define ADE_ADDRMASK 0x03 + +/* There is one NTE for each source (unicast) address assigned to an interface */ +typedef struct _NET_TABLE_ENTRY { + LIST_ENTRY IFListEntry; /* Entry on interface list */ + LIST_ENTRY NTListEntry; /* Entry on net table list */ + struct _IP_INTERFACE *Interface; /* Pointer to interface on this net */ + struct _PREFIX_LIST_ENTRY *PLE; /* Pointer to prefix list entry for this net */ + ULONG RefCount; /* Reference count */ + PIP_ADDRESS Address; /* Pointer to unicast address for this net */ +} NET_TABLE_ENTRY, *PNET_TABLE_ENTRY; + + +/* Link layer transmit prototype */ +typedef VOID (*LL_TRANSMIT_ROUTINE)( + PVOID Context, + PNDIS_PACKET NdisPacket, + UINT Offset, + PVOID LinkAddress, + USHORT Type); + +/* Link layer to IP binding information */ +typedef struct _LLIP_BIND_INFO { + PVOID Context; /* Pointer to link layer context information */ + UINT HeaderSize; /* Size of link level header */ + UINT MinFrameSize; /* Minimum frame size in bytes */ + UINT MTU; /* Maximum transmission unit */ + PUCHAR Address; /* Pointer to interface address */ + UINT AddressLength; /* Length of address in bytes */ + LL_TRANSMIT_ROUTINE Transmit; /* Transmit function for this interface */ +} LLIP_BIND_INFO, *PLLIP_BIND_INFO; + + +/* Information about an IP interface */ +typedef struct _IP_INTERFACE { + LIST_ENTRY ListEntry; /* Entry on list */ + ULONG RefCount; /* Reference count */ + KSPIN_LOCK Lock; /* Spin lock for this object */ + LIST_ENTRY NTEListHead; /* List of NTEs on this interface */ + LIST_ENTRY ADEListHead; /* List of ADEs on this interface */ + PVOID Context; /* Pointer to link layer context information */ + UINT HeaderSize; /* Size of link level header */ + UINT MinFrameSize; /* Minimum frame size in bytes */ + UINT MTU; /* Maximum transmission unit */ + PUCHAR Address; /* Pointer to interface address */ + UINT AddressLength; /* Length of address in bytes */ + LL_TRANSMIT_ROUTINE Transmit; /* Pointer to transmit function */ +} IP_INTERFACE, *PIP_INTERFACE; + + +/* Prefix List Entry */ +typedef struct _PREFIX_LIST_ENTRY { + LIST_ENTRY ListEntry; /* Entry on list */ + ULONG RefCount; /* Reference count */ + PIP_INTERFACE Interface; /* Pointer to interface */ + PIP_ADDRESS Prefix; /* Pointer to prefix */ + UINT PrefixLength; /* Length of prefix */ +} PREFIX_LIST_ENTRY, *PPREFIX_LIST_ENTRY; + + +#define IP_PROTOCOL_TABLE_SIZE 0x100 + +typedef VOID (*IP_PROTOCOL_HANDLER)( + PNET_TABLE_ENTRY NTE, + PIP_PACKET IPPacket); + +/* Loopback adapter address information (network byte order) */ +#define LOOPBACK_ADDRESS_IPv4 ((IPv4_RAW_ADDRESS)DH2N(0x7F000001)) +#define LOOPBACK_BCASTADDR_IPv4 ((IPv4_RAW_ADDRESS)DH2N(0x7F0000FF)) +#define LOOPBACK_ADDRMASK_IPv4 ((IPv4_RAW_ADDRESS)DH2N(0xFFFFFF00)) + +/* Protocol definitions */ +#define IPPROTO_ICMP 1 /* Internet Control Message Protocol */ +#define IPPROTO_IGMP 2 /* Internet Group Management Protocol */ +#define IPPROTO_TCP 6 /* Transmission Control Protocol */ +#define IPPROTO_UDP 17 /* User Datagram Protocol */ + +/* Timeout timer constants */ +#define IP_TICKS_SECOND 2 /* Two ticks per second */ +#define IP_TIMEOUT (1000 / IP_TICKS_SECOND) /* Timeout in milliseconds */ + + +extern LIST_ENTRY InterfaceListHead; +extern KSPIN_LOCK InterfaceListLock; +extern LIST_ENTRY NetTableListHead; +extern KSPIN_LOCK NetTableListLock; +extern LIST_ENTRY PrefixListHead; +extern KSPIN_LOCK PrefixListLock; +extern UINT MaxLLHeaderSize; +extern UINT MinLLFrameSize; + + +PNET_TABLE_ENTRY IPCreateNTE( + PIP_INTERFACE IF, + PIP_ADDRESS Address, + UINT PrefixLength); + +PIP_INTERFACE IPCreateInterface( + PLLIP_BIND_INFO BindInfo); + +VOID IPDestroyInterface( + PIP_INTERFACE IF); + +BOOLEAN IPRegisterInterface( + PIP_INTERFACE IF); + +VOID IPUnregisterInterface( + PIP_INTERFACE IF); + +PNET_TABLE_ENTRY IPLocateNTEOnInterface( + PIP_INTERFACE IF, + PIP_ADDRESS Address, + PUINT AddressType); + +PNET_TABLE_ENTRY IPLocateNTE( + PIP_ADDRESS Address, + PUINT AddressType); + +PADDRESS_ENTRY IPLocateADE( + PIP_ADDRESS Address, + UINT AddressType); + +PADDRESS_ENTRY IPGetDefaultADE( + UINT AddressType); + +VOID IPTimeout( + PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +VOID IPDispatchProtocol( + PNET_TABLE_ENTRY NTE, + PIP_PACKET IPPacket); + +VOID IPRegisterProtocol( + UINT ProtocolNumber, + IP_PROTOCOL_HANDLER Handler); + +NTSTATUS IPStartup( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath); + +NTSTATUS IPShutdown( + VOID); + +#endif /* __IP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/lan.h b/reactos/drivers/net/tcpip/include/lan.h new file mode 100644 index 00000000000..20799d7b88a --- /dev/null +++ b/reactos/drivers/net/tcpip/include/lan.h @@ -0,0 +1,100 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/lan.h + * PURPOSE: LAN adapter definitions + */ +#ifndef __LAN_H +#define __LAN_H + + +/* Medias we support */ +#define MEDIA_ETH 0 + +#define MAX_MEDIA 1 + +#define IEEE_802_ADDR_LENGTH 6 + +/* Ethernet header layout */ +typedef struct ETH_HEADER { + UCHAR DstAddr[IEEE_802_ADDR_LENGTH]; /* Destination MAC address */ + UCHAR SrcAddr[IEEE_802_ADDR_LENGTH]; /* Source MAC address */ + USHORT EType; /* Ethernet protocol type */ +} ETH_HEADER, *PETH_HEADER; + +#define MAX_MEDIA_ETH sizeof(ETH_HEADER) + +/* Broadcast masks */ +#define BCAST_ETH_MASK 0x01 + +/* Broadcast values to check against */ +#define BCAST_ETH_CHECK 0x01 + +/* Offset of broadcast address */ +#define BCAST_ETH_OFFSET 0x00 + +/* Per adapter information */ +typedef struct LAN_ADAPTER { + struct LAN_ADAPTER *Next; /* Pointer to next adapter */ + KSPIN_LOCK Lock; /* Lock for this structure */ + UCHAR State; /* State of the adapter */ + KEVENT Event; /* Opening event */ + PVOID Context; /* Upper layer context information */ + NDIS_HANDLE NdisHandle; /* NDIS binding handle */ + NDIS_STATUS NdisStatus; /* NDIS status of last request */ + NDIS_MEDIUM Media; /* Media type */ + UCHAR HWAddress[IEEE_802_ADDR_LENGTH]; /* Local HW address */ + UINT HWAddressLength; /* Length of HW address */ + UCHAR BCastMask; /* Mask for checking broadcast */ + UCHAR BCastCheck; /* Value to check against */ + UCHAR BCastOffset; /* Offset in frame to check against */ + UCHAR HeaderSize; /* Size of link-level header */ + USHORT MTU; /* Maximum Transfer Unit */ + UINT MinFrameSize; /* Minimum frame size in bytes */ + UINT MaxPacketSize; /* Maximum packet size when sending */ + UINT MaxSendPackets; /* Maximum number of packets per send */ + UINT MacOptions; /* MAC options for NIC driver/adapter */ + UINT Speed; /* Link speed */ + UINT PacketFilter; /* Packet filter for this adapter */ + PNDIS_PACKET TDPackets; /* Transfer Data packets */ +} LAN_ADAPTER, *PLAN_ADAPTER; + +/* LAN adapter state constants */ +#define LAN_STATE_OPENING 0 +#define LAN_STATE_RESETTING 1 +#define LAN_STATE_STARTED 2 +#define LAN_STATE_STOPPED 3 + +/* Size of out lookahead buffer */ +#define LOOKAHEAD_SIZE 128 + +/* Ethernet types. We swap constants so we can compare values at runtime + without swapping them */ +#define ETYPE_IPv4 WH2N(0x0800) +#define ETYPE_IPv6 WH2N(0x0000) /* FIXME */ +#define ETYPE_ARP WH2N(0x0806) + +/* Protocols */ +#define LAN_PROTO_IPv4 0x0000 /* Internet Protocol version 4 */ +#define LAN_PROTO_IPv6 0x0001 /* Internet Protocol version 6 */ +#define LAN_PROTO_ARP 0x0002 /* Address Resolution Protocol */ + +extern PLAN_ADAPTER Adapters; + + +NDIS_STATUS LANRegisterAdapter( + PNDIS_STRING AdapterName, + PLAN_ADAPTER *Adapter); + +NDIS_STATUS LANUnregisterAdapter( + PLAN_ADAPTER Adapter); + +NTSTATUS LANRegisterProtocol( + STRING *Name); + +VOID LANUnregisterProtocol( + VOID); + +#endif /* __LAN_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/loopback.h b/reactos/drivers/net/tcpip/include/loopback.h new file mode 100644 index 00000000000..abf3e1db72b --- /dev/null +++ b/reactos/drivers/net/tcpip/include/loopback.h @@ -0,0 +1,25 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/loopback.h + * PURPOSE: Loopback adapter definitions + */ +#ifndef __LOOPBACK_H +#define __LOOPBACK_H + +#include + + +extern PIP_INTERFACE Loopback; + + +NDIS_STATUS LoopRegisterAdapter( + PNDIS_STRING AdapterName, + PLAN_ADAPTER *Adapter); + +NDIS_STATUS LoopUnregisterAdapter( + PLAN_ADAPTER Adapter); + +#endif /* __LOOPBACK_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/neighbor.h b/reactos/drivers/net/tcpip/include/neighbor.h new file mode 100644 index 00000000000..a2c50feb76b --- /dev/null +++ b/reactos/drivers/net/tcpip/include/neighbor.h @@ -0,0 +1,101 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/neighbor.h + * PURPOSE: Neighbor definitions + */ +#ifndef __NEIGHBOR_H +#define __NEIGHBOR_H + + +#define NB_HASHMASK 0xF /* Hash mask for neighbor cache */ + +typedef struct NEIGHBOR_CACHE_TABLE { + struct NEIGHBOR_CACHE_ENTRY *Cache; /* Pointer to cache */ + KSPIN_LOCK Lock; /* Protecting lock */ +} NEIGHBOR_CACHE_TABLE, *PNEIGHBOR_CACHE_TABLE; + +/* Information about a neighbor */ +typedef struct NEIGHBOR_CACHE_ENTRY { + struct NEIGHBOR_CACHE_ENTRY *Next; /* Pointer to next entry */ + struct NEIGHBOR_CACHE_TABLE *Table; /* Pointer to table */ + ULONG RefCount; /* Number of references */ + UCHAR State; /* State of NCE */ + UINT EventTimer; /* Ticks since last event */ + UINT EventCount; /* Number of events */ + PIP_INTERFACE Interface; /* Pointer to interface */ + PIP_ADDRESS Address; /* IP address of neighbor */ + UINT LinkAddressLength; /* Length of link address */ + PVOID LinkAddress; /* Pointer to link address */ + PNDIS_PACKET WaitQueue; /* Pointer to NDIS packets + waiting to be sent */ +} NEIGHBOR_CACHE_ENTRY, *PNEIGHBOR_CACHE_ENTRY; + +/* NCE states */ +#define NUD_NONE 0x00 +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 + +#define NUD_IN_TIMER (NUD_INCOMPLETE | NUD_DELAY | NUD_PROBE) +#define NUD_VALID (NUD_REACHABLE | NUD_NOARP | NUD_STALE | NUD_DELAY | \ + NUD_PROBE | NUD_PERMANENT) +#define NUD_CONNECTED (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE) + + +/* Maximum number of retransmissions of multicast solicits */ +#define MAX_MULTICAST_SOLICIT 3 /* 3 transmissions */ + +/* Number of ticks between address resolution messages */ +#define RETRANS_TIMER IP_TICKS_SECOND /* One second */ + + +extern NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1]; + + +VOID NBTimeout( + VOID); + +VOID NBStartup( + VOID); + +VOID NBShutdown( + VOID); + +VOID NBSendSolicit( + PNEIGHBOR_CACHE_ENTRY NCE); + +PNEIGHBOR_CACHE_ENTRY NBAddNeighbor( + PIP_INTERFACE Interface, + PIP_ADDRESS Address, + PVOID LinkAddress, + UINT LinkAddressLength, + UCHAR Type); + +VOID NBUpdateNeighbor( + PNEIGHBOR_CACHE_ENTRY NCE, + PVOID LinkAddress, + UCHAR State); + +PNEIGHBOR_CACHE_ENTRY NBLocateNeighbor( + PIP_ADDRESS Address); + +PNEIGHBOR_CACHE_ENTRY NBFindOrCreateNeighbor( + PIP_INTERFACE Interface, + PIP_ADDRESS Address); + +BOOLEAN NBQueuePacket( + PNEIGHBOR_CACHE_ENTRY NCE, + PNDIS_PACKET NdisPacket); + +VOID NBRemoveNeighbor( + PNEIGHBOR_CACHE_ENTRY NCE); + +#endif /* __NEIGHBOR_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/pool.h b/reactos/drivers/net/tcpip/include/pool.h new file mode 100644 index 00000000000..653b0a4fa8d --- /dev/null +++ b/reactos/drivers/net/tcpip/include/pool.h @@ -0,0 +1,19 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/pool.h + * PURPOSE: Prototypes for memory pooling + */ +#ifndef __POOL_H +#define __POOL_H + + +PVOID PoolAllocateBuffer( + ULONG Size); + +VOID PoolFreeBuffer( + PVOID Buffer); + +#endif /* __POOL_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/rawip.h b/reactos/drivers/net/tcpip/include/rawip.h new file mode 100644 index 00000000000..b1322293438 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/rawip.h @@ -0,0 +1,24 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/rawip.h + * PURPOSE: Raw IP types and constants + */ +#ifndef __RAWIP_H +#define __RAWIP_H + +NTSTATUS RawIPSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize); + +NTSTATUS RawIPStartup( + VOID); + +NTSTATUS RawIPShutdown( + VOID); + +#endif /* __RAWIP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/receive.h b/reactos/drivers/net/tcpip/include/receive.h new file mode 100644 index 00000000000..2dab3d4f0f7 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/receive.h @@ -0,0 +1,61 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/receive.h + * PURPOSE: Internet Protocol receive prototypes + */ +#ifndef __RECEIVE_H +#define __RECEIVE_H + +#include + + +/* IP datagram fragment descriptor. Used to store IP datagram fragments */ +typedef struct IP_FRAGMENT { + LIST_ENTRY ListEntry; /* Entry on list */ + PVOID Data; /* Pointer to fragment data */ + UINT Offset; /* Offset into datagram where this fragment is */ + UINT Size; /* Size of this fragment */ +} IP_FRAGMENT, *PIP_FRAGMENT; + +/* IP datagram hole descriptor. Used to reassemble IP datagrams */ +typedef struct IPDATAGRAM_HOLE { + LIST_ENTRY ListEntry; /* Entry on list */ + UINT First; /* Offset of first octet of the hole */ + UINT Last; /* Offset of last octet of the hole */ +} IPDATAGRAM_HOLE, *PIPDATAGRAM_HOLE; + +/* IP datagram reassembly information */ +typedef struct IPDATAGRAM_REASSEMBLY { + LIST_ENTRY ListEntry; /* Entry on list */ + KSPIN_LOCK Lock; /* Protecting spin lock */ + ULONG RefCount; /* Reference count for this object */ + UINT DataSize; /* Size of datagram data area */ + IP_ADDRESS SrcAddr; /* Source address */ + IP_ADDRESS DstAddr; /* Destination address */ + UCHAR Protocol; /* Internet Protocol number */ + USHORT Id; /* Identification number */ + PIPv4_HEADER IPv4Header; /* Pointer to IP header */ + UINT HeaderSize; /* Length of IP header */ + LIST_ENTRY FragmentListHead; /* IP fragment list */ + LIST_ENTRY HoleListHead; /* IP datagram hole list */ +} IPDATAGRAM_REASSEMBLY, *PIPDATAGRAM_REASSEMBLY; + + +extern LIST_ENTRY ReassemblyListHead; +extern KSPIN_LOCK ReassemblyListLock; + + +VOID IPFreeReassemblyList( + VOID); + +VOID IPDatagramReassemblyTimeout( + VOID); + +VOID IPReceive( + PVOID Context, + PIP_PACKET IPPacket); + +#endif /* __RECEIVE_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/route.h b/reactos/drivers/net/tcpip/include/route.h new file mode 100644 index 00000000000..d2c827c8d76 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/route.h @@ -0,0 +1,75 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/route.h + * PURPOSE: Routing cache definitions + */ +#ifndef __ROUTE_H +#define __ROUTE_H + +#include +#include +#include +#include +#include + + +/* Route Cache Node structure. + * The primary purpose of the RCN is to cache selected source and + * next-hop addresses. The routing cache is implemented as a binary + * search tree to provide fast lookups when many RCNs are in the cache. + */ +typedef struct ROUTE_CACHE_NODE { + struct ROUTE_CACHE_NODE *Parent; /* Pointer to parent */ + struct ROUTE_CACHE_NODE *Left; /* Pointer to left child */ + struct ROUTE_CACHE_NODE *Right; /* Pointer to right child */ + /* Memebers above this line must not be moved */ + ULONG RefCount; /* Reference count */ + UCHAR State; /* RCN state (RCN_STATE_*) */ + IP_ADDRESS Destination; /* Destination address */ + PNET_TABLE_ENTRY NTE; /* Preferred NTE */ + PNEIGHBOR_CACHE_ENTRY NCE; /* Pointer to NCE for first hop (NULL if none) */ + UINT PathMTU; /* Path MTU to destination */ +} ROUTE_CACHE_NODE, *PROUTE_CACHE_NODE; + +/* RCN states */ +#define RCN_STATE_PERMANENT 0x00 /* RCN is permanent (properly local) */ +#define RCN_STATE_COMPUTED 0x01 /* RCN is computed */ + + +#define IsExternalRCN(RCN) \ + (RCN == ExternalRCN) + +#define IsInternalRCN(RCN) \ + (RCN != ExternalRCN) + + +NTSTATUS RouteStartup( + VOID); + +NTSTATUS RouteShutdown( + VOID); + +UINT RouteGetRouteToDestination( + PIP_ADDRESS Destination, + PNET_TABLE_ENTRY NTE, + PROUTE_CACHE_NODE *RCN); + +PROUTE_CACHE_NODE RouteAddRouteToDestination( + PIP_ADDRESS Destination, + PNET_TABLE_ENTRY NTE, + PIP_INTERFACE IF, + PNEIGHBOR_CACHE_ENTRY NCE); + +VOID RouteRemoveRouteToDestination( + PROUTE_CACHE_NODE RCN); + +VOID RouteInvalidateNTE( + PNET_TABLE_ENTRY NTE); + +VOID RouteInvalidateNCE( + PNEIGHBOR_CACHE_ENTRY NCE); + +#endif /* __ROUTE_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/router.h b/reactos/drivers/net/tcpip/include/router.h new file mode 100644 index 00000000000..e8d52ef1601 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/router.h @@ -0,0 +1,62 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/router.h + * PURPOSE: IP routing definitions + */ +#ifndef __ROUTER_H +#define __ROUTER_H + +#include + + +/* Forward Information Base Entry */ +typedef struct _FIB_ENTRY { + LIST_ENTRY ListEntry; /* Entry on list */ + ULONG RefCount; /* Reference count */ + PIP_ADDRESS NetworkAddress; /* Address of network */ + PIP_ADDRESS Netmask; /* Netmask of network */ + PNET_TABLE_ENTRY NTE; /* Pointer to NTE to use */ + PNEIGHBOR_CACHE_ENTRY Router; /* Pointer to NCE of router to use */ + UINT Metric; /* Cost of this route */ +} FIB_ENTRY, *PFIB_ENTRY; + + +PNET_TABLE_ENTRY RouterFindBestNTE( + PIP_INTERFACE Interface, + PIP_ADDRESS Destination); + +PIP_INTERFACE RouterFindOnLinkInterface( + PIP_ADDRESS Address, + PNET_TABLE_ENTRY NTE); + +PFIB_ENTRY RouterAddRoute( + PIP_ADDRESS NetworkAddress, + PIP_ADDRESS Netmask, + PNET_TABLE_ENTRY NTE, + PNEIGHBOR_CACHE_ENTRY Router, + UINT Metric); + +PNEIGHBOR_CACHE_ENTRY RouterGetRoute( + PIP_ADDRESS Destination, + PNET_TABLE_ENTRY NTE); + +VOID RouterRemoveRoute( + PFIB_ENTRY FIBE); + +PFIB_ENTRY RouterCreateRouteIPv4( + IPv4_RAW_ADDRESS NetworkAddress, + IPv4_RAW_ADDRESS Netmask, + IPv4_RAW_ADDRESS RouterAddress, + PNET_TABLE_ENTRY NTE, + UINT Metric); + +NTSTATUS RouterStartup( + VOID); + +NTSTATUS RouterShutdown( + VOID); + +#endif /* __ROUTER_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/routines.h b/reactos/drivers/net/tcpip/include/routines.h new file mode 100644 index 00000000000..dcd0347b555 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/routines.h @@ -0,0 +1,53 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/routines.h + * PURPOSE: Common routine prototypes + */ +#ifndef __ROUTINES_H +#define __ROUTINES_H + + +UINT Random( + VOID); + +UINT CopyBufferToBufferChain( + PNDIS_BUFFER DstBuffer, + UINT DstOffset, + PUCHAR SrcData, + UINT Length); + +UINT CopyBufferChainToBuffer( + PUCHAR DstData, + PNDIS_BUFFER SrcBuffer, + UINT SrcOffset, + UINT Length); + +UINT CopyPacketToBuffer( + PUCHAR DstData, + PNDIS_PACKET SrcPacket, + UINT SrcOffset, + UINT Length); + +UINT CopyPacketToBufferChain( + PNDIS_BUFFER DstBuffer, + UINT DstOffset, + PNDIS_PACKET SrcPacket, + UINT SrcOffset, + UINT Length); + +VOID FreeNdisPacket( + PNDIS_PACKET Packet); + +PVOID AdjustPacket( + PNDIS_PACKET Packet, + UINT Available, + UINT Needed); + +UINT ResizePacket( + PNDIS_PACKET Packet, + UINT Size); + +#endif /* __ROUTINES_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/tcp.h b/reactos/drivers/net/tcpip/include/tcp.h new file mode 100644 index 00000000000..d58654a1f30 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/tcp.h @@ -0,0 +1,18 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/tcp.h + * PURPOSE: Transmission Control Protocol definitions + */ +#ifndef __TCP_H +#define __TCP_H + +NTSTATUS TCPStartup( + VOID); + +NTSTATUS TCPShutdown( + VOID); + +#endif /* __TCP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/tcpip.h b/reactos/drivers/net/tcpip/include/tcpip.h new file mode 100644 index 00000000000..7a56e2a847f --- /dev/null +++ b/reactos/drivers/net/tcpip/include/tcpip.h @@ -0,0 +1,131 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/tcpip.h + * PURPOSE: TCP/IP protocol driver definitions + * NOTES: Spin lock acquire order: + * - Net table list lock + * - Interface lock + * - Interface list lock + * - Prefix list lock + * - Neighbor cache lock + * - Route cache lock + */ +#ifndef __TCPIP_H +#define __TCPIP_H + +#ifdef _MSC_VER +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#include + + +/* Define _NTTEST_ to make test version. Device names are prefixed with + 'NT' to allow the driver to run side by side with MS TCP/IP driver */ +#define _NTTEST_ + +/* FIXME: The following should be moved to ntddk.h or tdi headers */ +#ifndef _MSC_VER + +#ifndef IO_NETWORK_INCREMENT +#define IO_NETWORK_INCREMENT 2 +#endif + +#endif + +#ifdef _MSC_VER +/* EXPORTED is already defined ddk/defines.h */ +#define EXPORTED __declspec(dllexport) + +#endif + +#include +#include +#include + + +/* Macros */ + +#define MIN(value1, value2) \ + ((value1 < value2)? value1 : value2) + +#define MAX(value1, value2) \ + ((value1 > value2)? value1 : value2) + + +#ifdef i386 + +/* DWORD network to host byte order conversion for i386 */ +#define DN2H(dw) \ + ((((dw) & 0xFF000000L) >> 24) | \ + (((dw) & 0x00FF0000L) >> 8) | \ + (((dw) & 0x0000FF00L) << 8) | \ + (((dw) & 0x000000FFL) << 24)) + +/* DWORD host to network byte order conversion for i386 */ +#define DH2N(dw) \ + ((((dw) & 0xFF000000L) >> 24) | \ + (((dw) & 0x00FF0000L) >> 8) | \ + (((dw) & 0x0000FF00L) << 8) | \ + (((dw) & 0x000000FFL) << 24)) + +/* WORD network to host order conversion for i386 */ +#define WN2H(w) \ + ((((w) & 0xFF00) >> 8) | \ + (((w) & 0x00FF) << 8)) + +/* WORD host to network byte order conversion for i386 */ +#define WH2N(w) \ + ((((w) & 0xFF00) >> 8) | \ + (((w) & 0x00FF) << 8)) + +#else /* i386 */ + +/* DWORD network to host byte order conversion for other architectures */ +#define DN2H(dw) \ + (dw) + +/* DWORD host to network byte order conversion for other architectures */ +#define DH2N(dw) \ + (dw) + +/* WORD network to host order conversion for other architectures */ +#define WN2H(w) \ + (w) + +/* WORD host to network byte order conversion for other architectures */ +#define WH2N(w) \ + (w) + +#endif /* i386 */ + + +/* Global variable */ +extern PDEVICE_OBJECT TCPDeviceObject; +extern PDEVICE_OBJECT UDPDeviceObject; +extern PDEVICE_OBJECT IPDeviceObject; +extern PDEVICE_OBJECT RawIPDeviceObject; +extern LIST_ENTRY InterfaceListHead; +extern KSPIN_LOCK InterfaceListLock; +extern LIST_ENTRY AddressFileListHead; +extern KSPIN_LOCK AddressFileListLock; +extern NDIS_HANDLE GlobalPacketPool; +extern NDIS_HANDLE GlobalBufferPool; +extern TDIEntityID *EntityList; +extern ULONG EntityCount; +extern UDP_STATISTICS UDPStats; + +#endif /* __TCPIP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/ticonsts.h b/reactos/drivers/net/tcpip/include/ticonsts.h new file mode 100644 index 00000000000..2ed72467286 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/ticonsts.h @@ -0,0 +1,56 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/ticonsts.h + * PURPOSE: TCP/IP protocol driver constants + */ +#ifndef __TICONSTS_H +#define __TICONSTS_H + +/* NDIS version this driver supports */ +#define NDIS_VERSION_MAJOR 3 +#define NDIS_VERSION_MINOR 0 + +#ifdef _NTTEST_ +/* Name of devices */ +#define DD_TCP_DEVICE_NAME L"\\Device\\NTTcp" +#define DD_UDP_DEVICE_NAME L"\\Device\\NTUdp" +#define DD_IP_DEVICE_NAME L"\\Device\\NTIp" +#define DD_RAWIP_DEVICE_NAME L"\\Device\\NTRawIp" + +/* For NDIS protocol registration */ +#define IP_DEVICE_NAME "\\Device\\NTIp" +#else +#define DD_TCP_DEVICE_NAME L"\\Device\\Tcp" +#define DD_UDP_DEVICE_NAME L"\\Device\\Udp" +#define DD_IP_DEVICE_NAME L"\\Device\\Ip" +#define DD_RAWIP_DEVICE_NAME L"\\Device\\RawIp" + +/* For NDIS protocol registration */ +#define IP_DEVICE_NAME "\\Device\\Ip" +#endif /* _NTTEST_ */ + +/* TCP/UDP/RawIP IOCTL code definitions */ + +#define FSCTL_TCP_BASE FILE_DEVICE_NETWORK + +#define _TCP_CTL_CODE(Function, Method, Access) \ + CTL_CODE(FSCTL_TCP_BASE, Function, Method, Access) + +#define IOCTL_TCP_QUERY_INFORMATION_EX \ + _TCP_CTL_CODE(0, METHOD_NEITHER, FILE_ANY_ACCESS) + +#define IOCTL_TCP_SET_INFORMATION_EX \ + _TCP_CTL_CODE(1, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +/* Unique error values for log entries */ +#define TI_ERROR_DRIVERENTRY 0 + +/* Internal status codes */ +#define IP_SUCCESS 0x0000 /* Successful */ +#define IP_NO_RESOURCES 0x0001 /* Not enough free resources */ +#define IP_NO_ROUTE_TO_DESTINATION 0x0002 /* No route to destination */ + +#endif /* __TICONSTS_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/titypes.h b/reactos/drivers/net/tcpip/include/titypes.h new file mode 100644 index 00000000000..833d671dbf1 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/titypes.h @@ -0,0 +1,215 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/titypes.h + * PURPOSE: TCP/IP protocol driver types + */ +#ifndef __TITYPES_H +#define __TITYPES_H + +#include + + +#ifdef DBG + +#define DEBUG_REFCHECK(Object) { \ + if ((Object)->RefCount <= 0) { \ + TI_DbgPrint(MIN_TRACE, ("Object at (0x%X) has invalid reference count (%d).\n", \ + (Object), (Object)->RefCount)); \ + } \ +} + +#else + +#define DEBUG_REFCHECK(Object) + +#endif + + +/* + * VOID ReferenceObject( + * PVOID Object) + */ +#define ReferenceObject(Object) \ +{ \ + DEBUG_REFCHECK(Object); \ + TI_DbgPrint(DEBUG_REFCOUNT, ("Referencing object at (0x%X). RefCount (%d).\n", \ + (Object), (Object)->RefCount)); \ + \ + InterlockedIncrement(&((Object)->RefCount)); \ +} + +/* + * VOID DereferenceObject( + * PVOID Object) + */ +#define DereferenceObject(Object) \ +{ \ + DEBUG_REFCHECK(Object); \ + TI_DbgPrint(DEBUG_REFCOUNT, ("Dereferencing object at (0x%X). RefCount (%d).\n", \ + (Object), (Object)->RefCount)); \ + \ + if (InterlockedDecrement(&((Object)->RefCount)) == 0) \ + PoolFreeBuffer(Object); \ +} + + +typedef NTSTATUS (*DATAGRAM_SEND_ROUTINE)( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize); + +/* Datagram completion handler prototype */ +typedef VOID (*DATAGRAM_COMPLETION_ROUTINE)( + PVOID Context, + NDIS_STATUS Status, + ULONG Count); + +typedef struct _DATAGRAM_RECEIVE_REQUEST { + LIST_ENTRY ListEntry; /* Entry on list */ + PIP_ADDRESS RemoteAddress; /* Remote address we receive from (NULL means any) */ + USHORT RemotePort; /* Remote port we receive from (0 means any) */ + PTDI_CONNECTION_INFORMATION ReturnInfo; /* Return information */ + PNDIS_BUFFER Buffer; /* Pointer to receive buffer */ + ULONG BufferSize; /* Size of Buffer */ + DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine */ + PVOID Context; /* Pointer to context information */ +} DATAGRAM_RECEIVE_REQUEST, *PDATAGRAM_RECEIVE_REQUEST; + +/* Datagram build routine prototype */ +typedef NTSTATUS (*DATAGRAM_BUILD_ROUTINE)( + PVOID Context, + PIP_ADDRESS LocalAddress, + USHORT LocalPort, + PIP_PACKET *IPPacket); + +typedef struct _DATAGRAM_SEND_REQUEST { + LIST_ENTRY ListEntry; /* Entry on list */ + PIP_ADDRESS RemoteAddress; /* Pointer to remote IP address */ + USHORT RemotePort; /* Remote port number */ + PNDIS_BUFFER Buffer; /* Pointer to NDIS buffer to send */ + DWORD BufferSize; /* Size of Buffer */ + DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine */ + PVOID Context; /* Pointer to context information */ + DATAGRAM_BUILD_ROUTINE Build; /* Datagram build routine */ +} DATAGRAM_SEND_REQUEST, *PDATAGRAM_SEND_REQUEST; + + +/* Transport (TCP/UDP) endpoint context structure. The FileObject->FsContext + field holds a pointer to this structure */ +typedef struct _TRANSPORT_CONTEXT { + union { + HANDLE AddressHandle; + CONNECTION_CONTEXT ConnectionContext; + HANDLE ControlChannel; + } Handle; + ULONG RefCount; + BOOL CancelIrps; + KEVENT CleanupEvent; +} TRANSPORT_CONTEXT, *PTRANSPORT_CONTEXT; + + +typedef struct _ADDRESS_FILE { + LIST_ENTRY ListEntry; /* Entry on list */ + KSPIN_LOCK Lock; /* Spin lock to manipulate this structure */ + ULONG RefCount; /* Number of references to this object */ + USHORT Flags; /* Flags for address file (see below) */ + PADDRESS_ENTRY ADE; /* Associated address entry */ + USHORT Protocol; /* Protocol number */ + USHORT Port; /* Network port (network byte order) */ + WORK_QUEUE_ITEM WorkItem; /* Work queue item handle */ + DATAGRAM_COMPLETION_ROUTINE Complete; /* Completion routine for delete request */ + PVOID Context; /* Delete request context */ + DATAGRAM_SEND_ROUTINE Send; /* Routine to send a datagram */ + LIST_ENTRY ReceiveQueue; /* List of outstanding receive requests */ + LIST_ENTRY TransmitQueue; /* List of outstanding transmit requests */ + PIP_ADDRESS AddrCache; /* One entry address cache (destination + address of last packet transmitted) */ + + /* The following members are used to control event notification */ + + /* Connection indication handler */ + PTDI_IND_CONNECT ConnectionHandler; + PVOID ConnectionHandlerContext; + BOOL RegisteredConnectionHandler; + /* Disconnect indication handler */ + PTDI_IND_DISCONNECT DisconnectHandler; + PVOID DisconnectHandlerContext; + BOOL RegisteredDisconnectHandler; + /* Receive indication handler */ + PTDI_IND_RECEIVE ReceiveHandler; + PVOID ReceiveHandlerContext; + BOOL RegisteredReceiveHandler; + /* Expedited receive indication handler */ + PTDI_IND_RECEIVE_EXPEDITED ExpeditedReceiveHandler; + PVOID ExpeditedReceiveHandlerContext; + BOOL RegisteredExpeditedReceiveHandler; + /* Receive datagram indication handler */ + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + BOOL RegisteredReceiveDatagramHandler; + /* Error indication handler */ + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + PVOID ErrorHandlerOwner; + BOOL RegisteredErrorHandler; +} ADDRESS_FILE, *PADDRESS_FILE; + +/* Address File Flag constants */ +#define AFF_VALID 0x0001 /* Address file object is valid for use */ +#define AFF_BUSY 0x0002 /* Address file object is exclusive to someone */ +#define AFF_DELETE 0x0004 /* Address file object is sheduled to be deleted */ +#define AFF_SEND 0x0008 /* A send request is pending */ +#define AFF_RECEIVE 0x0010 /* A receive request is pending */ +#define AFF_PENDING 0x001C /* A request is pending */ + +/* Macros for manipulating address file object flags */ + +#define AF_IS_VALID(ADF) ((ADF)->Flags & AFF_VALID) +#define AF_SET_VALID(ADF) ((ADF)->Flags |= AFF_VALID) +#define AF_CLR_VALID(ADF) ((ADF)->Flags &= ~AFF_VALID) + +#define AF_IS_BUSY(ADF) ((ADF)->Flags & AFF_BUSY) +#define AF_SET_BUSY(ADF) ((ADF)->Flags |= AFF_BUSY) +#define AF_CLR_BUSY(ADF) ((ADF)->Flags &= ~AFF_BUSY) + +#define AF_IS_PENDING(ADF, X) (ADF->Flags & X) +#define AF_SET_PENDING(ADF, X) (ADF->Flags |= X) +#define AF_CLR_PENDING(ADF, X) (ADF->Flags &= ~X) + + +/* Structure used to search through Address Files */ +typedef struct _AF_SEARCH { + PLIST_ENTRY Next; /* Next address file to check */ + PIP_ADDRESS Address; /* Pointer to address to be found */ + USHORT Port; /* Network port */ + USHORT Protocol; /* Protocol number */ +} AF_SEARCH, *PAF_SEARCH; + +/* Transport connection context structure. The FileObject->FsContext2 + field holds a pointer to this structure */ +typedef struct _CONNECTION_ENDPOINT { + LIST_ENTRY ListEntry; /* Entry on list */ + KSPIN_LOCK Lock; /* Spin lock to protect this structure */ + ULONG RefCount; /* Number of references to this object */ +} CONNECTION_ENDPOINT, *PCONNECTION_ENDPOINT; + +/* Transport control channel context structure. The FileObject->FsContext2 + field holds a pointer to this structure */ +typedef struct _CONTROL_CHANNEL { + LIST_ENTRY ListEntry; /* Entry on list */ + KSPIN_LOCK Lock; /* Spin lock to protect this structure */ + ULONG RefCount; /* Number of references to this object */ +} CONTROL_CHANNEL, *PCONTROL_CHANNEL; + +typedef struct _TI_QUERY_CONTEXT { + PIRP Irp; + PMDL InputMdl; + PMDL OutputMdl; + TCP_REQUEST_QUERY_INFORMATION_EX QueryInfo; +} TI_QUERY_CONTEXT, *PTI_QUERY_CONTEXT; + +#endif /* __TITYPES_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/transmit.h b/reactos/drivers/net/tcpip/include/transmit.h new file mode 100644 index 00000000000..c0f816787f2 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/transmit.h @@ -0,0 +1,47 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/transmit.h + * PURPOSE: Internet Protocol transmit prototypes + */ +#ifndef __TRANSMIT_H +#define __TRANSMIT_H + +#include +#include +#include + + +/* IP fragment context information */ +typedef struct IPFRAGMENT_CONTEXT { + struct IPFRAGMENT_CONTEXT *Next; /* Pointer to next in list */ + PNDIS_PACKET Datagram; /* Pointer to original NDIS packet */ + PVOID DatagramData; /* Pointer to datagram data */ + UINT HeaderSize; /* IP datagram header size */ + PNDIS_PACKET NdisPacket; /* Pointer to NDIS packet */ + PNDIS_BUFFER NdisBuffer; /* Pointer to NDIS buffer */ + PVOID Header; /* Pointer to IP header in fragment buffer */ + PVOID Data; /* Pointer to fragment data */ + UINT Position; /* Current fragment offset */ + UINT BytesLeft; /* Number of bytes left to send */ + UINT PathMTU; /* Path Maximum Transmission Unit */ + PNEIGHBOR_CACHE_ENTRY NCE; /* Pointer to NCE to use */ +} IPFRAGMENT_CONTEXT, *PIPFRAGMENT_CONTEXT; + + +VOID IPSendComplete( + PVOID Context, + PNDIS_PACKET NdisPacket, + NDIS_STATUS NdisStatus); + +NTSTATUS IPSendFragment( + PNDIS_PACKET NdisPacket, + PNEIGHBOR_CACHE_ENTRY NCE); + +NTSTATUS IPSendDatagram( + PIP_PACKET IPPacket, + PROUTE_CACHE_NODE RCN); + +#endif /* __TRANSMIT_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/include/udp.h b/reactos/drivers/net/tcpip/include/udp.h new file mode 100644 index 00000000000..919a86b5341 --- /dev/null +++ b/reactos/drivers/net/tcpip/include/udp.h @@ -0,0 +1,68 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: include/udp.h + * PURPOSE: User Datagram Protocol definitions + */ +#ifndef __UDP_H +#define __UDP_H + + +/* UDPv4 header structure */ +typedef struct UDP_HEADER { + USHORT SourcePort; /* Source port */ + USHORT DestPort; /* Destination port */ + USHORT Length; /* Size of header and data */ + USHORT Checksum; /* Checksum of datagram */ +} UDP_HEADER, *PUDP_HEADER; + +/* UDPv4 pseudo header */ +typedef struct UDP_PSEUDO_HEADER { + ULONG SourceAddress; /* Source address */ + ULONG DestAddress; /* Destination address */ + UCHAR Zero; /* Reserved */ + UCHAR Protocol; /* Protocol */ + USHORT UDPLength; /* Size of UDP datagram */ +} UDP_PSEUDO_HEADER, *PUDP_PSEUDO_HEADER; + + +typedef struct UDP_STATISTICS { + ULONG InputDatagrams; + ULONG NumPorts; + ULONG InputErrors; + ULONG OutputDatagrams; + ULONG NumAddresses; +} UDP_STATISTICS, *PUDP_STATISTICS; + +VOID UDPSend( + PVOID Context, + PDATAGRAM_SEND_REQUEST SendRequest); + +NTSTATUS UDPSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize); + +NTSTATUS UDPReceiveDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG ReceiveLength, + ULONG ReceiveFlags, + PTDI_CONNECTION_INFORMATION ReturnInfo, + PULONG BytesReceived); + +VOID UDPReceive( + PNET_TABLE_ENTRY NTE, + PIP_PACKET IPPacket); + +NTSTATUS UDPStartup( + VOID); + +NTSTATUS UDPShutdown( + VOID); + +#endif /* __UDP_H */ + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/makefile b/reactos/drivers/net/tcpip/makefile new file mode 100644 index 00000000000..db914fae34c --- /dev/null +++ b/reactos/drivers/net/tcpip/makefile @@ -0,0 +1,132 @@ +# TCPIP.SYS - TCP/IP protocol driver + +TARGETNAME=tcpip + +BASE_CFLAGS = -I./include -I../../../include + +RESOURCE_OBJECT = $(TARGETNAME).coff +TCPIP_OBJECTS = tcpip/main.o tcpip/address.o tcpip/checksum.o \ + tcpip/dispatch.o tcpip/fileobjs.o tcpip/info.o \ + tcpip/pool.o tcpip/routines.o +DATALINK_OBJECTS = datalink/arp.o datalink/lan.o datalink/loopback.o +NETWORK_OBJECTS = network/icmp.o network/ip.o network/neighbor.o \ + network/receive.o network/route.o network/router.o \ + network/transmit.o +DATAGRAM_OBJECTS = transport/datagram/datagram.o +RAWIP_OBJECTS = transport/rawip/rawip.o +TCP_OBJECTS = transport/tcp/tcp.o +UDP_OBJECTS = transport/udp/udp.o + +all: objects $(TARGETNAME).sys + +objects: + mkdir objects + +objects/tcpip.o: $(TCPIP_OBJECTS) + $(LD) -r -o objects/tcpip.o $(TCPIP_OBJECTS) + +objects/datalink.o: $(DATALINK_OBJECTS) + $(LD) -r -o objects/datalink.o $(DATALINK_OBJECTS) + +objects/network.o: $(NETWORK_OBJECTS) + $(LD) -r -o objects/network.o $(NETWORK_OBJECTS) + +objects/datagram.o: $(DATAGRAM_OBJECTS) + $(LD) -r -o objects/datagram.o $(DATAGRAM_OBJECTS) + +objects/rawip.o: $(RAWIP_OBJECTS) + $(LD) -r -o objects/rawip.o $(RAWIP_OBJECTS) + +objects/tcp.o: $(TCP_OBJECTS) + $(LD) -r -o objects/tcp.o $(TCP_OBJECTS) + +objects/udp.o: $(UDP_OBJECTS) + $(LD) -r -o objects/udp.o $(UDP_OBJECTS) + +OBJECTS = objects/tcpip.o objects/datalink.o objects/network.o \ + objects/datagram.o objects/rawip.o objects/tcp.o objects/udp.o \ + $(RESOURCE_OBJECT) \ + ../../../ntoskrnl/ntoskrnl.a ../ndis/ndis.a + +$(TARGETNAME).coff: $(TARGETNAME).rc ../../../include/reactos/resource.h + +ifeq ($(DOSCLI),yes) +CLEAN_FILES = \ + *.o objects\*.o tcpip\*.o datalink\*.o network\*.o \ + transport\datagram\*.o transport\rawip\*.o \ + transport\tcp\*.o transport\udp\*.o $(TARGETNAME).coff \ + $(TARGETNAME).a junk.tmp base.tmp temp.exp \ + $(TARGETNAME).sys $(TARGETNAME).sym +else +CLEAN_FILES = \ + *.o objects/*.o tcpip/*.o datalink/*.o network/*.o \ + transport/datagram/*.o transport/rawip/*.o \ + transport/tcp/*.o transport/udp/*.o $(TARGETNAME).coff \ + $(TARGETNAME).a junk.tmp base.tmp temp.exp \ + $(TARGETNAME).sys $(TARGETNAME).sym +endif + + +$(TARGETNAME).sys: $(OBJECTS) + $(DLLTOOL) \ + --dllname $(TARGETNAME).sys \ + --def $(TARGETNAME).def \ + --kill-at \ + --output-lib $(TARGETNAME).a + $(CC) \ + -mdll \ + -specs=../../svc_specs \ + -Wl,-e,_DriverEntry@8 \ + -Wl,--base-file,base.tmp \ + -Wl,--defsym,_end=end \ + -Wl,--defsym,_edata=__data_end__ \ + -Wl,--defsym,_etext=etext \ + $(OBJECTS) \ + -o junk.tmp + - $(RM) junk.tmp + $(DLLTOOL) \ + --dllname $(TARGETNAME).sys \ + --base-file base.tmp \ + --output-exp temp.exp \ + --def $(TARGETNAME).edf + - $(RM) base.tmp + $(CC) \ + -mdll \ + -specs=../../svc_specs \ + -Wl,--image-base,0x10000 \ + -Wl,-e,_DriverEntry@8 \ + -Wl,temp.exp \ + $(OBJECTS) \ + -o $(TARGETNAME).sys + - $(RM) temp.exp + $(NM) --numeric-sort $(TARGETNAME).sys > $(TARGETNAME).sym + +clean: $(CLEAN_FILES:%=%_clean) + +$(CLEAN_FILES:%=%_clean): %_clean: + - $(RM) $* + +.PHONY: clean $(CLEAN_FILES:%=%_clean) + +floppy: $(FLOPPY_DIR)/drivers/$(TARGETNAME).sys + +$(FLOPPY_DIR)/drivers/$(TARGETNAME).sys: $(TARGETNAME).sys +ifeq ($(DOSCLI),yes) + $(CP) $(TARGETNAME).sys $(FLOPPY_DIR)\drivers\$(TARGETNAME).sys +else + $(CP) $(TARGETNAME).sys $(FLOPPY_DIR)/drivers/$(TARGETNAME).sys +endif + +dist: $(DIST_DIR)/drivers/$(TARGETNAME).sys + +$(DIST_DIR)/drivers/$(TARGETNAME).sys: $(TARGETNAME).sys +ifeq ($(DOSCLI),yes) + $(CP) $(TARGETNAME).sys ..\..\$(DIST_DIR)\drivers\$(TARGETNAME).sys +else + $(CP) $(TARGETNAME).sys ../../$(DIST_DIR)/drivers/$(TARGETNAME).sys +endif + +#WITH_DEBUGGING = yes +#WIN32_LEAN_AND_MEAN = yes +#WARNINGS_ARE_ERRORS = yes +include ../../../rules.mak diff --git a/reactos/drivers/net/tcpip/network/Makefile b/reactos/drivers/net/tcpip/network/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/network/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/network/SOURCES b/reactos/drivers/net/tcpip/network/SOURCES new file mode 100644 index 00000000000..076841668fe --- /dev/null +++ b/reactos/drivers/net/tcpip/network/SOURCES @@ -0,0 +1,20 @@ +TARGETNAME=network +TARGETPATH=..\objects +TARGETTYPE=LIBRARY + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib + +INCLUDES=..\include;$(BASEDIR)\INC;..\..\..\..\include\net + + +SOURCES= icmp.c \ + ip.c \ + neighbor.c \ + receive.c \ + route.c \ + router.c \ + transmit.c + +MSC_WARNING_LEVEL=/W3 /WX + diff --git a/reactos/drivers/net/tcpip/network/icmp.c b/reactos/drivers/net/tcpip/network/icmp.c new file mode 100644 index 00000000000..f75e6b094fc --- /dev/null +++ b/reactos/drivers/net/tcpip/network/icmp.c @@ -0,0 +1,299 @@ +/* + * 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 +#include +#include +#include +#include +#include + + +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 = (PIP_PACKET)Context; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + TI_DbgPrint(MAX_TRACE, ("Freeing NDIS packet (%X).\n", Packet)); + + /* Free packet */ + FreeNdisPacket(Packet); + + TI_DbgPrint(MAX_TRACE, ("Freeing IP packet at %X.\n", IPPacket)); + + PoolFreeBuffer(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(MAX_TRACE, ("Called. DataSize = %d.\n", DataSize)); + + /* Prepare ICMP packet */ + IPPacket = PoolAllocateBuffer(sizeof(IP_PACKET)); + if (!IPPacket) + return NULL; + + TI_DbgPrint(MAX_TRACE, ("IPPacket at %X.\n", IPPacket)); + + Size = MaxLLHeaderSize + sizeof(IPv4_HEADER) + + sizeof(ICMP_HEADER) + DataSize; + DataBuffer = ExAllocatePool(NonPagedPool, Size); + if (!DataBuffer) { + PoolFreeBuffer(IPPacket); + return NULL; + } + + TI_DbgPrint(MAX_TRACE, ("Size = %d, Data at %X.\n", Size, DataBuffer)); + + /* Allocate NDIS packet */ + NdisAllocatePacket(&NdisStatus, &NdisPacket, GlobalPacketPool); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PoolFreeBuffer(IPPacket); + ExFreePool(DataBuffer); + return NULL; + } + + TI_DbgPrint(MAX_TRACE, ("NdisPacket at %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) { + PoolFreeBuffer(IPPacket); + NdisFreePacket(NdisPacket); + ExFreePool(DataBuffer); + return NULL; + } + + TI_DbgPrint(MAX_TRACE, ("NdisBuffer at %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; + ULONG Checksum; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + ICMPHeader = (PICMP_HEADER)IPPacket->Data; + + TI_DbgPrint(MID_TRACE, ("Size = %d.\n", IPPacket->TotalSize)); + + TI_DbgPrint(MID_TRACE, ("HeaderSize = %d.\n", IPPacket->HeaderSize)); + + TI_DbgPrint(MID_TRACE, ("Type = %d.\n", ICMPHeader->Type)); + + TI_DbgPrint(MID_TRACE, ("Code = %d.\n", ICMPHeader->Code)); + + TI_DbgPrint(MID_TRACE, ("Checksum = %X.\n", ICMPHeader->Checksum)); + + /* Checksum ICMP header and data and compare */ + Checksum = DN2H(IPv4Checksum(IPPacket->Data, IPPacket->TotalSize - IPPacket->HeaderSize, 0)); + if (Checksum != 0xFFFF) { + TI_DbgPrint(MIN_TRACE, ("Bad ICMP checksum (0x%X).\n", Checksum)); + /* 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; + + ICMPTransmit(NTE, NewPacket); + + TI_DbgPrint(MID_TRACE, ("Echo reply sent.\n")); + + return; + default: + TI_DbgPrint(MID_TRACE, ("Discarded ICMP datagram of unknown type.\n")); + /* Discard packet */ + break; + } +} + + +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(MID_TRACE, ("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, NTE, &RCN) == IP_SUCCESS) { + /* Send the packet */ + if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS) { + FreeNdisPacket(IPPacket->NdisPacket); + PoolFreeBuffer(IPPacket); + } + /* 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(MIN_TRACE, ("No route to destination address 0x%X.\n", + IPPacket->DstAddr.Address.IPv4Address)); + /* Discard packet */ + FreeNdisPacket(IPPacket->NdisPacket); + PoolFreeBuffer(IPPacket); + } +} + + +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(MID_TRACE, ("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/net/tcpip/network/ip.c b/reactos/drivers/net/tcpip/network/ip.c new file mode 100644 index 00000000000..f15449c2165 --- /dev/null +++ b/reactos/drivers/net/tcpip/network/ip.c @@ -0,0 +1,979 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + + +KTIMER IPTimer; +KDPC IPTimeoutDpc; +LIST_ENTRY InterfaceListHead; +KSPIN_LOCK InterfaceListLock; +LIST_ENTRY NetTableListHead; +KSPIN_LOCK NetTableListLock; +LIST_ENTRY PrefixListHead; +KSPIN_LOCK PrefixListLock; +UINT MaxLLHeaderSize; /* Largest maximum header size */ +UINT MinLLFrameSize; /* Largest minimum frame size */ +BOOLEAN IPInitialized = FALSE; + +IP_PROTOCOL_HANDLER ProtocolTable[IP_PROTOCOL_TABLE_SIZE]; + + +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)); + + /* Allocate space for an ADE and set it up */ + ADE = PoolAllocateBuffer(sizeof(ADDRESS_ENTRY)); + if (!ADE) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + 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)); + + /* 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 */ + PoolFreeBuffer(ADE); + TI_DbgPrint(MIN_TRACE, ("Check.\n")); +} + + +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; + } +} + + +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)); + + /* Allocate space for an PLE and set it up */ + PLE = PoolAllocateBuffer(sizeof(PREFIX_LIST_ENTRY)); + if (!PLE) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + 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)); + + /* 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 */ + PoolFreeBuffer(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); +} + + +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)); + + /* Allocate room for an NTE */ + NTE = PoolAllocateBuffer(sizeof(NET_TABLE_ENTRY)); + if (!NTE) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + 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")); + PoolFreeBuffer(NTE); + return NULL; + } + + /* Create a prefix list entry for unicast address */ + NTE->PLE = CreatePLE(IF, NTE->Address, PrefixLength); + if (!NTE->PLE) { + DestroyADE(IF, ADE); + PoolFreeBuffer(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)); + + /* 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 */ + 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 */ + PoolFreeBuffer(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 (0x%X) AddressType (0x%X).\n", + IF, Address, AddressType)); + + KeAcquireSpinLock(&IF->Lock, &OldIrql); + + /* Search the list and return the NTE if found */ + CurrentEntry = IF->ADEListHead.Flink; + 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)); + + 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; + PLIST_ENTRY CurrentIFEntry; + PLIST_ENTRY CurrentADEEntry; + PIP_INTERFACE CurrentIF; + PADDRESS_ENTRY CurrentADE; + + TI_DbgPrint(DEBUG_IP, ("Called. Address (0x%X) AddressType (0x%X).\n", + Address, AddressType)); + + KeAcquireSpinLock(&InterfaceListLock, &OldIrql); + + /* Search the interface list */ + CurrentIFEntry = InterfaceListHead.Flink; + while (CurrentIFEntry != &InterfaceListHead) { + CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry); + + /* Search the address entry list and return the ADE if found */ + CurrentADEEntry = CurrentIF->ADEListHead.Flink; + while (CurrentADEEntry != &CurrentIF->ADEListHead) { + CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry); + if ((AddrIsEqual(Address, CurrentADE->Address)) && + (CurrentADE->Type == AddressType)) { + ReferenceObject(CurrentADE); + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + return CurrentADE; + } + CurrentADEEntry = CurrentADEEntry->Flink; + } + CurrentIFEntry = CurrentIFEntry->Flink; + } + + 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; + PLIST_ENTRY CurrentIFEntry; + PLIST_ENTRY CurrentADEEntry; + PIP_INTERFACE CurrentIF; + PADDRESS_ENTRY CurrentADE; +#if 0 + BOOLEAN LoopbackIsRegistered = FALSE; +#endif + TI_DbgPrint(DEBUG_IP, ("Called. AddressType (0x%X).\n", AddressType)); + + KeAcquireSpinLock(&InterfaceListLock, &OldIrql); + + /* Search the interface list */ + CurrentIFEntry = InterfaceListHead.Flink; + while (CurrentIFEntry != &InterfaceListHead) { + CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry); +#if 0 + if (CurrentIF != Loopback) { +#endif + /* Search the address entry list and return the first appropriate ADE found */ + CurrentADEEntry = CurrentIF->ADEListHead.Flink; + while (CurrentADEEntry != &CurrentIF->ADEListHead) { + CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry); + if (CurrentADE->Type == AddressType) + ReferenceObject(CurrentADE); + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + return CurrentADE; + } + CurrentADEEntry = CurrentADEEntry->Flink; +#if 0 + } else + LoopbackIsRegistered = TRUE; +#endif + CurrentIFEntry = CurrentIFEntry->Flink; + } +#if 0 + /* No address was found. Use loopback interface if available */ + if (LoopbackIsRegistered) { + CurrentADEEntry = Loopback->ADEListHead.Flink; + while (CurrentADEEntry != &Loopback->ADEListHead) { + CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry); + if (CurrentADE->Type == AddressType) { + ReferenceObject(CurrentADE); + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + return CurrentADE; + } + CurrentADEEntry = CurrentADEEntry->Flink; + } + } +#endif + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + + return NULL; +} + + +VOID 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(); +} + + +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)); + + IF = PoolAllocateBuffer(sizeof(IP_INTERFACE)); + if (!IF) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + 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); + + 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)); + + 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 + PoolFreeBuffer(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(DEBUG_IP, ("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; + } + 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); + return FALSE; + } + /* 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 */ + ExInterlockedInsertTailList(&InterfaceListHead, &IF->ListEntry, &InterfaceListLock); + + 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; + + /* 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); + + /* Initialize the prefix list and protecting lock */ + InitializeListHead(&PrefixListHead); + KeInitializeSpinLock(&PrefixListLock); + + /* 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(); + + IPInitialized = FALSE; + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/network/neighbor.c b/reactos/drivers/net/tcpip/network/neighbor.c new file mode 100644 index 00000000000..2e17cb4225c --- /dev/null +++ b/reactos/drivers/net/tcpip/network/neighbor.c @@ -0,0 +1,506 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +NEIGHBOR_CACHE_TABLE NeighborCache[NB_HASHMASK + 1]; + + +VOID NCETimeout( + PNEIGHBOR_CACHE_ENTRY NCE) +/* + * FUNCTION: Neighbor cache entry timeout handler + * NOTES: + * The neighbor cache lock must be held + */ +{ + PNDIS_PACKET NdisPacket, Next; + + 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) { + Next = (PNDIS_PACKET)PC(NdisPacket)->DLComplete; + IPSendComplete((PVOID)NCE->Interface, NdisPacket, + NDIS_STATUS_REQUEST_ABORTED); + NdisPacket = Next; + } + NCE->WaitQueue = NULL; + + NCE->EventCount = 0; + + /* Remove route cache entries with references to this NCE. + Remember that neighbor cache lock is taken before + 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 other states don't use the event timer */ + 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) { + 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 + */ +{ + UINT i; + KIRQL OldIrql; + PNDIS_PACKET NdisPacket, Next; + PNEIGHBOR_CACHE_ENTRY CurNCE, NextNCE; + + 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) { + Next = (PNDIS_PACKET)PC(NdisPacket)->DLComplete; + FreeNdisPacket(NdisPacket); + NdisPacket = Next; + } + +#if 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 */ + CurrentEntry = NCE->Interface->NTEListHead.Flink; + if (!IsListEmpty(CurrentEntry)) { + 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 + */ +{ + ULONG HashValue; + KIRQL OldIrql; + PNEIGHBOR_CACHE_ENTRY NCE; + + 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)); + + NCE = PoolAllocateBuffer(sizeof(NEIGHBOR_CACHE_ENTRY) + LinkAddressLength); + if (!NCE) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + /* 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)((ULONG_PTR)NCE + sizeof(NEIGHBOR_CACHE_ENTRY)); + if (LinkAddress) + 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 + */ +{ + KIRQL OldIrql; + PNDIS_PACKET Current; + PNDIS_PACKET Next; + + 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); +#if 1 + /* Send any waiting packets */ + while (Current) { + /* 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; + } +#endif +} + + +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 + */ +{ + UINT HashValue; + KIRQL OldIrql; + PNEIGHBOR_CACHE_ENTRY NCE; + + 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 (if 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) { + 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 + */ +{ + KIRQL OldIrql; + PKSPIN_LOCK Lock; + + 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 + */ +{ + ULONG HashValue; + KIRQL OldIrql; + PNEIGHBOR_CACHE_ENTRY *PrevNCE; + PNEIGHBOR_CACHE_ENTRY CurNCE; + PNDIS_PACKET NdisPacket, Next; + + 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) { + Next = (PNDIS_PACKET)PC(NdisPacket)->DLComplete; + FreeNdisPacket(NdisPacket); + NdisPacket = Next; + } + + /* Remove all references from route cache */ + RouteInvalidateNCE(CurNCE); + + /* Remove reference to the address */ + DereferenceObject(CurNCE->Address); + +#if 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 + PoolFreeBuffer(CurNCE); + + KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql); + + return; + } + } + + KeReleaseSpinLock(&NeighborCache[HashValue].Lock, OldIrql); +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/network/receive.c b/reactos/drivers/net/tcpip/network/receive.c new file mode 100644 index 00000000000..679a899915a --- /dev/null +++ b/reactos/drivers/net/tcpip/network/receive.c @@ -0,0 +1,638 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + + +LIST_ENTRY ReassemblyListHead; +KSPIN_LOCK ReassemblyListLock; + + +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 = PoolAllocateBuffer(sizeof(IPDATAGRAM_HOLE)); + 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 */ + PoolFreeBuffer(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 */ + PoolFreeBuffer(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)); + + PoolFreeBuffer(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 + */ +{ + PIP_PACKET IPPacket; + PLIST_ENTRY CurrentEntry; + PIP_FRAGMENT Current; + PVOID Data; + + TI_DbgPrint(DEBUG_IP, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR)); + + IPPacket = PoolAllocateBuffer(sizeof(IP_PACKET)); + if (!IPPacket) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + /* FIXME: Assume IPv4 */ + + IPPacket->Type = IP_ADDRESS_V4; + IPPacket->RefCount = 1; + IPPacket->TotalSize = IPDR->HeaderSize + IPDR->DataSize; + 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")); + PoolFreeBuffer(IPPacket); + return NULL; + } + + /* Copy the header into the buffer */ + RtlCopyMemory(IPPacket->Header, IPDR->IPv4Header, IPDR->HeaderSize); + + Data = (PVOID)((ULONG_PTR)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); + + 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) + PoolFreeBuffer(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 = PoolAllocateBuffer(sizeof(IPDATAGRAM_REASSEMBLY)); + if (!IPDR) { + /* We don't have the resources to process this packet, discard it */ + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + 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 */ + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + PoolFreeBuffer(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 + PoolFreeBuffer(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 = PoolAllocateBuffer(sizeof(IP_FRAGMENT)); + 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; + } + + TI_DbgPrint(DEBUG_IP, ("Fragment data buffer allocated at (0x%X) Size (%d).\n", + Fragment->Data, Fragment->Size)); + + /* 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 */ + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return; + } + + /* Give the packet to the protocol dispatcher */ + IPDispatchProtocol(NTE, Datagram); + + /* We're done with this datagram */ + ExFreePool(Datagram->Header); + PoolFreeBuffer(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 are being reassembled to form a + * complete IP datagram + */ +{ +} + + +VOID IPv4Receive( + PVOID Context, + 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(MAX_TRACE, ("Received IPv4 datagram.\n")); + + IPPacket->HeaderSize = (((PIPv4_HEADER)IPPacket->Header)->VerIHL & 0x0F) << 2; + + 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 (!CorrectChecksum(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; + } + + TI_DbgPrint(MAX_TRACE, ("TotalSize (datalink) is (%d).\n", IPPacket->TotalSize)); + + IPPacket->TotalSize = WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength); + + TI_DbgPrint(MAX_TRACE, ("TotalSize (IPv4) is (%d).\n", IPPacket->TotalSize)); + + 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); + + /* FIXME: Possibly forward packets with multicast addresses */ + + /* FIXME: Should we allow packets to be received on the wrong interface? */ +#if 0 + NTE = IPLocateNTE(&IPPacket->DstAddr, &AddressType); +#else + NTE = IPLocateNTEOnInterface((PIP_INTERFACE)Context, &IPPacket->DstAddr, &AddressType); +#endif + if (NTE) { + /* This packet is destined for us */ + ProcessFragment((PIP_INTERFACE)Context, 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 */ +#if 0 + NCE = RouteFindRouter(&IPPacket->DstAddr, NULL); + if (NCE) { + /* FIXME: Possibly fragment datagram */ + /* Forward the packet */ + IPSendFragment(IPPacket, NCE); + } else { + TI_DbgPrint(MIN_TRACE, ("No route to destination (0x%X).\n", + IPPacket->DstAddr.Address.IPv4Address)); + + /* FIXME: Send ICMP error code */ + } +#endif + } +} + + +VOID IPReceive( + PVOID Context, + PIP_PACKET IPPacket) +/* + * FUNCTION: Receives an IP datagram (or fragment) + * ARGUMENTS: + * Context = Pointer to context information (IP_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(Context, IPPacket); + break; + case 6: + IPPacket->Type = IP_ADDRESS_V6; + TI_DbgPrint(MIN_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/net/tcpip/network/route.c b/reactos/drivers/net/tcpip/network/route.c new file mode 100644 index 00000000000..f113b0b8917 --- /dev/null +++ b/reactos/drivers/net/tcpip/network/route.c @@ -0,0 +1,665 @@ +/* + * 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 + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include + + +/* 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; + + +#if 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 + + +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; + + TI_DbgPrint(DEBUG_RCACHE, ("Called.\n")); + + RCN = PoolAllocateBuffer(sizeof(ROUTE_CACHE_NODE)); + if (!RCN) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + 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; + + 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 */ +#if 0 + /* 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 */ +#endif + 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); + +#if 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")); + + /* Initialize the pseudo external route cache node */ + ExternalRCN = PoolAllocateBuffer(sizeof(ROUTE_CACHE_NODE)); + if (!ExternalRCN) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + 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); + + PoolFreeBuffer(ExternalRCN); + + KeReleaseSpinLock(&RouteCacheLock, OldIrql); + + 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)); + + 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)); + + 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 */ + + /* 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); +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/network/router.c b/reactos/drivers/net/tcpip/network/router.c new file mode 100644 index 00000000000..1791dc40e9c --- /dev/null +++ b/reactos/drivers/net/tcpip/network/router.c @@ -0,0 +1,500 @@ +/* + * 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) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include +#include + + +LIST_ENTRY FIBListHead; +KSPIN_LOCK FIBLock; + + +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 + */ +{ + /* Unlink the FIB entry from the list */ + RemoveEntryList(&FIBE->ListEntry); + + /* Dereference the referenced objects */ + DereferenceObject(FIBE->NetworkAddress); + DereferenceObject(FIBE->Netmask); + DereferenceObject(FIBE->Router); + DereferenceObject(FIBE->NTE); + +#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 */ + PoolFreeBuffer(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 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_RCACHE, ("Called. Address1 (0x%X) Address2 (0x%X).\n", Address1, Address2)); + + if (Address1->Type == IP_ADDRESS_V4) + Size = sizeof(IPv4_RAW_ADDRESS); + else + Size = sizeof(IPv6_RAW_ADDRESS); + + Addr1 = (PUCHAR)&Address1->Address; + Addr2 = (PUCHAR)&Address2->Address; + + /* Find first non-matching byte */ + for (i = 0; ; i++) { + if (i == Size) + return 8 * i; /* The two addresses are equal */ + + if (Addr1[i] != Addr2[i]) + break; + } + + /* Find first non-matching bit */ + Bitmask = 0x80; + for (j = 0; ; j++) { + if ((Addr1[i] & Bitmask) != (Addr2[i] & Bitmask)) + break; + 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_RCACHE, ("Called. Address (0x%X) Prefix (0x%X) Length (%d).\n", Address, Prefix, Length)); + + /* 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_RCACHE, ("Called. Interface (0x%X) Destination (0x%X).\n", Interface, Destination)); + + KeAcquireSpinLock(&Interface->Lock, &OldIrql); + + CurrentEntry = Interface->NTEListHead.Flink; + while (CurrentEntry != &Interface->NTEListHead) { + Current = CONTAINING_RECORD(CurrentEntry, NET_TABLE_ENTRY, IFListEntry); + + 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_RCACHE, ("Called. Address (0x%X) NTE (0x%X).\n", Address, NTE)); + + 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, + PNET_TABLE_ENTRY NTE, + 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 + * NTE = Pointer to NTE to use + * 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, NTE 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) NTE (0x%X) " + "Router (0x%X) Metric (%d).\n", NetworkAddress, Netmask, NTE, Router, Metric)); + + FIBE = PoolAllocateBuffer(sizeof(FIB_ENTRY)); + if (!FIBE) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + FIBE->NetworkAddress = NetworkAddress; + FIBE->Netmask = Netmask; + FIBE->NTE = NTE; + 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)); + + 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)); + + 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 (!NetworkAddress) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return NULL; + } + + pNetmask = AddrBuildIPv4(Netmask); + if (!Netmask) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + DereferenceObject(pNetworkAddress); + return NULL; + } + + pRouterAddress = AddrBuildIPv4(RouterAddress); + if (!RouterAddress) { + 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, NTE, NCE, 1); + if (!FIBE) { + /* Not enough free resources */ + NBRemoveNeighbor(NCE); + PoolFreeBuffer(pNetworkAddress); + PoolFreeBuffer(pNetmask); + PoolFreeBuffer(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/net/tcpip/network/transmit.c b/reactos/drivers/net/tcpip/network/transmit.c new file mode 100644 index 00000000000..cb3c4f5f5fc --- /dev/null +++ b/reactos/drivers/net/tcpip/network/transmit.c @@ -0,0 +1,328 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + + +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.\n")); + + 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); + + /* Update pointers */ + (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.\n")); + + IFC = PoolAllocateBuffer(sizeof(IPFRAGMENT_CONTEXT)); + if (!IFC) + 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 (!IFC->Header) { + PoolFreeBuffer(IFC); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Allocate NDIS packet */ + NdisAllocatePacket(&NdisStatus, &IFC->NdisPacket, GlobalPacketPool); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool(Data); + PoolFreeBuffer(IFC); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Allocate NDIS buffer */ + NdisAllocateBuffer(&NdisStatus, &IFC->NdisBuffer, + GlobalBufferPool, Data, MaxLLHeaderSize + PathMTU); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NdisFreePacket(IFC->NdisPacket); + ExFreePool(Data); + PoolFreeBuffer(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.\n")); + + /* FIXME: Stop sending fragments and cleanup datagram buffers if + there was an error */ + + if (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); + PoolFreeBuffer(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.\n")); + + TI_DbgPrint(MAX_TRACE, ("NCE->State = %d.\n", NCE->State)); + + 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; + } + + PC(NdisPacket)->DLComplete = IPSendComplete; + (*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.\n")); + + NCE = RCN->NCE; + +#if 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; + if (IPPacket->TotalSize > PathMTU) { + return SendFragments(IPPacket, NCE, PathMTU); + } else { + /* Calculate checksum of IP header */ + ((PIPv4_HEADER)IPPacket->Header)->Checksum = 0; + + ((PIPv4_HEADER)IPPacket->Header)->Checksum = (USHORT) + IPv4Checksum(IPPacket->Header, IPPacket->HeaderSize, 0); + + TI_DbgPrint(MAX_TRACE, ("Sending packet (length is %d).\n", WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength))); + + return IPSendFragment(IPPacket->NdisPacket, NCE); + } +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/readme.txt b/reactos/drivers/net/tcpip/readme.txt new file mode 100644 index 00000000000..a4f061ea43e --- /dev/null +++ b/reactos/drivers/net/tcpip/readme.txt @@ -0,0 +1,19 @@ +Build instructions for TCP/IP protocol driver +--------------------------------------------- + +Building with Visual C++ and Windows NT DDK: + +Variables: +%BASEDIR% = path to NT4 DDK (e.g. c:\ntddk) +%DDKBUILDENV% = DDK build environment (free or checked) + +DDK environment variables must be set! (run setenv.bat) + + - Create the directory objects/i386/%DDKBUILDENV% + - Run "build" to build the driver + + +Building with Mingw32 and ReactOS include files: + + - Build NDIS.SYS (i.e. "make ndis") + - Run "make tcpip" FROM THE ReactOS ROOT DIRECTORY to build the driver diff --git a/reactos/drivers/net/tcpip/tcpip.def b/reactos/drivers/net/tcpip/tcpip.def new file mode 100644 index 00000000000..43b9506306d --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip.def @@ -0,0 +1,32 @@ +; TCPIP.SYS - TCP/IP protocol driver + +LIBRARY tcpip.sys + +EXPORTS +;FreeIprBuff +;GetIFAndLink +IPAddInterface@20 +;IPAllocBuff +IPDelInterface@4 +;IPDelayedNdisReEnumerateBindings +;IPDeregisterARP +;IPDisableSniffer +;IPEnableSniffer +;IPFreeBuff +;IPGetAddrType +;IPGetBestInterface +;IPGetInfo +;IPInjectPkt +;IPProxyNdisRequest +;IPRegisterARP +;IPRegisterProtocol +;IPSetIPSecStatus +;IPTransmit +LookupRoute@8 +;LookupRouteInformation +;SendICMPErr +;SetIPSecPtr +;UnSetIPSecPtr +;UnSetIPSecSendPtr + +; EOF diff --git a/reactos/drivers/net/tcpip/tcpip.edf b/reactos/drivers/net/tcpip/tcpip.edf new file mode 100644 index 00000000000..08f8b9b3587 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip.edf @@ -0,0 +1,32 @@ +; TCPIP.SYS - TCP/IP protocol driver + +LIBRARY tcpip.sys + +EXPORTS +;FreeIprBuff +;GetIFAndLink +IPAddInterface=IPAddInterface@20 +;IPAllocBuff +IPDelInterface=IPDelInterface@4 +;IPDelayedNdisReEnumerateBindings +;IPDeregisterARP +;IPDisableSniffer +;IPEnableSniffer +;IPFreeBuff +;IPGetAddrType +;IPGetBestInterface +;IPGetInfo +;IPInjectPkt +;IPProxyNdisRequest +;IPRegisterARP +;IPRegisterProtocol +;IPSetIPSecStatus +;IPTransmit +LookupRoute=LookupRoute@8 +;LookupRouteInformation +;SendICMPErr +;SetIPSecPtr +;UnSetIPSecPtr +;UnSetIPSecSendPtr + +; EOF diff --git a/reactos/drivers/net/tcpip/tcpip.rc b/reactos/drivers/net/tcpip/tcpip.rc new file mode 100644 index 00000000000..484b00dc098 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip.rc @@ -0,0 +1,38 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "TCP/IP protocol driver\0" + VALUE "FileVersion", "0.0.0\0" + VALUE "InternalName", "tcpip\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalFilename", "tcpip.sys\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/drivers/net/tcpip/tcpip/Copy of info.c b/reactos/drivers/net/tcpip/tcpip/Copy of info.c new file mode 100644 index 00000000000..6241e539a5a --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/Copy of info.c @@ -0,0 +1,335 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: tcpip/info.c + * PURPOSE: TDI query and set information routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/07-2000 Created + */ +#include +#include +#include + + +TDI_STATUS IPTdiQueryInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PNDIS_BUFFER Buffer, + PUINT BufferSize, + PVOID Context) +/* + * FUNCTION: Returns extended information about network layer + * ARGUMENTS: + * Request = Pointer to TDI request structure for the request + * ID = TDI object ID + * Buffer = Pointer to buffer with data to use. + * BufferSize = Pointer to buffer with size of Buffer. On return + * this is filled with number of bytes returned + * Context = Pointer to context buffer + * RETURNS: + * Status of operation + */ +{ + IPADDR_ENTRY IpAddress; + IPSNMP_INFO SnmpInfo; + PADDRESS_ENTRY ADE; + PIP_INTERFACE IF; + ULONG Temp; + UINT Count; + ULONG Entity; + KIRQL OldIrql; + UINT BufSize = *BufferSize; + + /* Make return parameters consistent every time */ + *BufferSize = 0; + + Entity = ID->toi_entity.tei_entity; + if (Entity != CL_NL_ENTITY) { + /* We can't handle this entity */ + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_entity.tei_instance != TL_INSTANCE) + /* We only support a single instance */ + return TDI_INVALID_REQUEST; + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_type == INFO_TYPE_PROVIDER && + ID->toi_id == ENTITY_TYPE_ID) { + + if (BufSize < sizeof(ULONG)) + return TDI_BUFFER_TOO_SMALL; + Temp = CL_NL_IP; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&Temp, sizeof(ULONG)); + + return TDI_SUCCESS; + } + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_class == INFO_CLASS_PROTOCOL) { + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + switch (ID->toi_id) { + case IP_MIB_ADDRTABLE_ENTRY_ID: + Temp = 0; + + KeAcquireSpinLock(&InterfaceLock, &OldIrql); +/* + /* Search the interface list */ + CurrentIFEntry = InterfaceListHead.Flink; + while (CurrentIFEntry != &InterfaceListHead) { + CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry); + if (CurrentIF != Loopback) { + /* Search the address entry list and return the first appropriate ADE found */ + CurrentADEEntry = CurrentIF->ADEListHead.Flink; + while (CurrentADEEntry != &CurrentIF->ADEListHead) { + CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry); + if (CurrentADE->Type == AddressType) + ReferenceAddress(CurrentADE->Address); + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + return CurrentADE; + } + CurrentADEEntry = CurrentADEEntry->Flink; + } else + LoopbackIsRegistered = TRUE; + CurrentIFEntry = CurrentIFEntry->Flink; + } + + + */ + for (IF = InterfaceList; IF != NULL; IF = IF->Next) { + if (Temp + sizeof(IPADDR_ENTRY) > BufSize) { + KeReleaseSpinLock(&InterfaceLock, OldIrql); + return TDI_BUFFER_TOO_SMALL; + } + + IpAddress.Addr = 0; + IpAddress.BcastAddr = 0; + IpAddress.Mask = 0; + /* Locate the diffrent addresses and put them the right place */ + for (ADE = IF->ADE; ADE != NULL; ADE = ADE->Next) { + switch (ADE->Type) { + case ADE_UNICAST : IpAddress.Addr = ADE->Address->Address.IPv4Address; + case ADE_MULTICAST: IpAddress.BcastAddr = ADE->Address->Address.IPv4Address; + case ADE_ADDRMASK : IpAddress.Mask = ADE->Address->Address.IPv4Address; + } + } + /* Pack the address information into IPADDR_ENTRY structure */ + IpAddress.Index = 0; + IpAddress.ReasmSize = 0; + IpAddress.Context = 0; + IpAddress.Pad = 0; + + Count = CopyBufferToBufferChain(Buffer, Temp, (PUCHAR)&IpAddress, sizeof(IPADDR_ENTRY)); + + Temp += sizeof(IPADDR_ENTRY); + } + KeReleaseSpinLock(&InterfaceLock, OldIrql); + return TDI_SUCCESS; + + case IP_MIB_STATS_ID: + if (BufSize < sizeof(IPSNMP_INFO)) + return TDI_BUFFER_TOO_SMALL; + + RtlZeroMemory(&SnmpInfo, sizeof(IPSNMP_INFO)); + + /* Count number of addresses */ + Count = 0; + KeAcquireSpinLock(&InterfaceLock, &OldIrql); + for (IF = InterfaceList; IF != NULL; IF = IF->Next) + Count++; + KeReleaseSpinLock(&InterfaceLock, OldIrql); + + SnmpInfo.NumAddr = Count; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&SnmpInfo, sizeof(IPSNMP_INFO)); + + return TDI_SUCCESS; + + default: + /* We can't handle this ID */ + return TDI_INVALID_PARAMETER; + } + } + + return TDI_INVALID_PARAMETER; +} + + +TDI_STATUS InfoTdiQueryInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PNDIS_BUFFER Buffer, + PUINT BufferSize, + PVOID Context) +/* + * FUNCTION: Returns extended information + * ARGUMENTS: + * Request = Pointer to TDI request structure for the request + * ID = TDI object ID + * Buffer = Pointer to buffer with data to use + * BufferSize = Pointer to buffer with size of Buffer. On return + * this is filled with number of bytes returned + * Context = Pointer to context buffer + * RETURNS: + * Status of operation + */ +{ + PADDRESS_FILE AddrFile; + PADDRESS_ENTRY ADE; + ADDRESS_INFO Info; + KIRQL OldIrql; + UINT Entity; + UINT Count; + UINT Size; + ULONG Temp; + UINT Offset = 0; + UINT BufSize = *BufferSize; + + /* Check wether it is a query for a list of entities */ + Entity = ID->toi_entity.tei_entity; + if (Entity == GENERIC_ENTITY) { + if (ID->toi_class != INFO_CLASS_GENERIC || + ID->toi_type != INFO_TYPE_PROVIDER || + ID->toi_id != ENTITY_LIST_ID) + return TDI_INVALID_PARAMETER; + + *BufferSize = 0; + + Size = EntityCount * sizeof(TDIEntityID); + if (BufSize < Size) + /* The buffer is too small to contain requested data */ + return TDI_BUFFER_TOO_SMALL; + + /* Return entity list */ + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)EntityList, Size); + + *BufferSize = Size; + + return TDI_SUCCESS; + } + + if ((Entity != CL_TL_ENTITY) && (Entity != CO_TL_ENTITY)) { + /* We can't handle this entity, pass it on */ + return IPTdiQueryInformationEx( + Request, ID, Buffer, BufferSize, Context); + } + + /* Make return parameters consistent every time */ + *BufferSize = 0; + + if (ID->toi_entity.tei_instance != TL_INSTANCE) + /* We only support a single instance */ + return TDI_INVALID_REQUEST; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + + if (ID->toi_type != INFO_TYPE_PROVIDER || + ID->toi_id != ENTITY_TYPE_ID) + return TDI_INVALID_PARAMETER; + + if (BufSize < sizeof(ULONG)) + return TDI_BUFFER_TOO_SMALL; + + if (Entity == CL_TL_ENTITY) + Temp = CL_TL_UDP; + else if (Entity == CO_TL_ENTITY) + Temp = CO_TL_TCP; + else + return TDI_INVALID_PARAMETER; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&Temp, sizeof(ULONG)); + + return TDI_SUCCESS; + } + + if (ID->toi_class == INFO_CLASS_PROTOCOL) { + + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + switch (ID->toi_id) { + case UDP_MIB_STAT_ID: + if (Entity != CL_TL_ENTITY) + return TDI_INVALID_PARAMETER; + + if (BufSize < sizeof(UDPStats)) + return TDI_BUFFER_TOO_SMALL; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&UDPStats, sizeof(UDP_STATISTICS)); + + return TDI_SUCCESS; + + case UDP_MIB_TABLE_ID: + if (Entity != CL_TL_ENTITY) + return TDI_INVALID_PARAMETER; + + Offset = 0; + + KeAcquireSpinLock(&AddressFileLock, &OldIrql); + + for (AddrFile = AddressFileList; + AddrFile->Next != NULL; + AddrFile = AddrFile->Next) { + + if (Offset + sizeof(ADDRESS_INFO) > BufSize) { + KeReleaseSpinLock(&AddressFileLock, OldIrql); + *BufferSize = Offset; + return TDI_BUFFER_OVERFLOW; + } + + for (ADE = AddrFile->ADE; ADE != NULL; ADE = ADE->Next) { + /* We only care about IPv4 unicast address */ + if ((ADE->Type == ADE_UNICAST) && + (ADE->Address->Type == IP_ADDRESS_V4)) + Info.LocalAddress = ADE->Address->Address.IPv4Address; + } + + Info.LocalPort = AddrFile->Port; + + Count = CopyBufferToBufferChain(Buffer, Offset, (PUCHAR)&Info, sizeof(ADDRESS_INFO)); + + Offset += Count; + } + + KeReleaseSpinLock(&AddressFileLock, OldIrql); + + *BufferSize = Offset; + + return STATUS_SUCCESS; + + default: + /* We can't handle this ID */ + return TDI_INVALID_PARAMETER; + } + } + + return TDI_INVALID_PARAMETER; +} + + +TDI_STATUS InfoTdiSetInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PVOID Buffer, + UINT BufferSize) +/* + * FUNCTION: Sets extended information + * ARGUMENTS: + * Request = Pointer to TDI request structure for the request + * ID = Pointer to TDI object ID + * Buffer = Pointer to buffer with data to use + * BufferSize = Size of Buffer + * RETURNS: + * Status of operation + */ +{ + /* FIXME: Set extended information */ + + return TDI_INVALID_REQUEST; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/Makefile b/reactos/drivers/net/tcpip/tcpip/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/tcpip/SOURCES b/reactos/drivers/net/tcpip/tcpip/SOURCES new file mode 100644 index 00000000000..e39f49ad634 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/SOURCES @@ -0,0 +1,28 @@ +TARGETNAME=tcpip +TARGETPATH=..\objects +TARGETTYPE=EXPORT_DRIVER + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib \ + ..\objects\*\free\datagram.lib \ + ..\objects\*\free\datalink.lib \ + ..\objects\*\free\network.lib \ + ..\objects\*\free\rawip.lib \ + ..\objects\*\free\tcp.lib \ + ..\objects\*\free\udp.lib + +INCLUDES=..\include;$(BASEDIR)\INC;..\..\..\..\include\net + + +SOURCES= address.c \ + checksum.c \ + dispatch.c \ + fileobjs.c \ + info.c \ + main.c \ + pool.c \ + routines.c \ + RESOURCE.RC + +MSC_WARNING_LEVEL=/W3 /WX + diff --git a/reactos/drivers/net/tcpip/tcpip/address.c b/reactos/drivers/net/tcpip/tcpip/address.c new file mode 100644 index 00000000000..eba0e2c064b --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/address.c @@ -0,0 +1,312 @@ +/* + * 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 +#include +#include +#include + + +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, + PIP_ADDRESS *Cache) +{ + 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 */ + PIP_ADDRESS IPAddress; + PTDI_ADDRESS_IP ValidAddr = (PTDI_ADDRESS_IP)CurAddr->Address; + + *Port = ValidAddr->sin_port; + + if (*Cache) { + if (((*Cache)->Type == IP_ADDRESS_V4) && + ((*Cache)->Address.IPv4Address == ValidAddr->in_addr)) { + *Address = *Cache; + return STATUS_SUCCESS; + } else { + /* Release the cached address as we cannot use it this time */ + DereferenceObject(*Cache); + *Cache = NULL; + } + } + + IPAddress = PoolAllocateBuffer(sizeof(IP_ADDRESS)); + if (IPAddress) { + AddrInitIPv4(IPAddress, ValidAddr->in_addr); + *Address = IPAddress; + + /* Update address cache */ + *Cache = IPAddress; + ReferenceObject(*Cache); + return STATUS_SUCCESS; + } else + return STATUS_INSUFFICIENT_RESOURCES; + } else + return STATUS_INVALID_ADDRESS; + default: + /* This is an unsupported address type. + Skip it and go to the next in the list */ + CurAddr = (PTA_ADDRESS)((ULONG_PTR)CurAddr->Address + CurAddr->AddressLength); + } + } + + return STATUS_INVALID_ADDRESS; +} + + +/* + * 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 + * 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 = PoolAllocateBuffer(sizeof(IP_ADDRESS)); + if (IPAddress) { + IPAddress->RefCount = 1; + IPAddress->Type = IP_ADDRESS_V4; + IPAddress->Address.IPv4Address = Address; + } + + return IPAddress; +} + + +/* + * 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); +} + + +/* + * FUNCTION: Searches through address file entries to find the first match + * ARGUMENTS: + * Address = IP address + * Port = Port number + * Protocol = Protocol number + * SearchContext = Pointer to search context + * RETURNS: + * Pointer to address file, NULL if none was found + */ +PADDRESS_FILE AddrSearchFirst( + PIP_ADDRESS Address, + USHORT Port, + USHORT Protocol, + PAF_SEARCH SearchContext) +{ + SearchContext->Address = Address; + SearchContext->Port = Port; + SearchContext->Next = AddressFileListHead.Flink; + SearchContext->Protocol = Protocol; + + return AddrSearchNext(SearchContext); +} + + +/* + * FUNCTION: Searches through address file entries to find next match + * ARGUMENTS: + * SearchContext = Pointer to search context + * RETURNS: + * Pointer to address file, NULL if none was found + */ +PADDRESS_FILE AddrSearchNext( + PAF_SEARCH SearchContext) +{ + PLIST_ENTRY CurrentEntry; + PIP_ADDRESS IPAddress; + KIRQL OldIrql; + PADDRESS_FILE Current = NULL; + BOOLEAN Found = FALSE; + + if (IsListEmpty(SearchContext->Next)) + return NULL; + + CurrentEntry = SearchContext->Next; + + KeAcquireSpinLock(&AddressFileListLock, &OldIrql); + + while (CurrentEntry != &AddressFileListHead) { + Current = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry); + + /* See if this address matches the search criteria */ + IPAddress = Current->ADE->Address; + if (((Current->Port == SearchContext->Port) && + (Current->Protocol == SearchContext->Protocol) && + (AddrIsEqual(IPAddress, SearchContext->Address))) || + (AddrIsUnspecified(IPAddress))) { + /* We've found a match */ + Found = TRUE; + break; + } + CurrentEntry = CurrentEntry->Flink; + } + + KeReleaseSpinLock(&AddressFileListLock, OldIrql); + + if (Found) { + SearchContext->Next = CurrentEntry->Flink; + return Current; + } else + return NULL; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/checksum.c b/reactos/drivers/net/tcpip/tcpip/checksum.c new file mode 100644 index 00000000000..b43b29a6f80 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/checksum.c @@ -0,0 +1,50 @@ +/* + * 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 +#include + + +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 + */ +{ + /* FIXME: This should be done in assembler */ + + register ULONG Sum = Seed; + + while (Count > 1) { + Sum += *(PUSHORT)Data; + Count -= 2; + (ULONG_PTR)Data += 2; + } + + /* Add left-over byte, if any */ + if (Count > 0) + Sum += *(PUCHAR)Data; + + /* Fold 32-bit sum to 16 bits */ + while (Sum >> 16) + Sum = (Sum & 0xFFFF) + (Sum >> 16); + + return ~Sum; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/dispatch.c b/reactos/drivers/net/tcpip/tcpip/dispatch.c new file mode 100644 index 00000000000..5d91ff3a4e4 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/dispatch.c @@ -0,0 +1,871 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: tcpip/dispatch.h + * PURPOSE: TDI dispatch routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include +#include +#include + + +NTSTATUS DispPrepareIrpForCancel( + PTRANSPORT_CONTEXT Context, + PIRP Irp, + PDRIVER_CANCEL CancelRoutine) +/* + * FUNCTION: Prepare an IRP for cancellation + * ARGUMENTS: + * Context = Pointer to context information + * Irp = Pointer to an I/O request packet + * CancelRoutine = Routine to be called when I/O request is cancelled + * RETURNS: + * Status of operation + */ +{ + KIRQL OldIrql; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + IoAcquireCancelSpinLock(&OldIrql); + + if (!Irp->Cancel) { + IoMarkIrpPending(Irp); + IoSetCancelRoutine(Irp, CancelRoutine); + Context->RefCount++; + IoReleaseCancelSpinLock(OldIrql); + + TI_DbgPrint(DEBUG_IRP, ("Leaving (IRP at 0x%X can now be cancelled).\n", Irp)); + + return STATUS_SUCCESS; + } + + /* IRP has already been cancelled */ + + IoReleaseCancelSpinLock(OldIrql); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + + TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp)); + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + TI_DbgPrint(DEBUG_IRP, ("Leaving (IRP was already cancelled).\n")); + + return STATUS_CANCELLED; +} + + +VOID DispCancelComplete( + PVOID Context) +/* + * FUNCTION: Completes a cancel request + * ARGUMENTS: + * Context = Pointer to context information (FILE_OBJECT) + */ +{ + KIRQL OldIrql; + PFILE_OBJECT FileObject; + PTRANSPORT_CONTEXT TranContext; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + FileObject = (PFILE_OBJECT)Context; + TranContext = (PTRANSPORT_CONTEXT)FileObject->FsContext; + + IoAcquireCancelSpinLock(&OldIrql); + + /* Remove the reference placed on the endpoint by the cancel routine. + The cancelled IRP will be completed by the completion routine for + the request */ + TranContext->RefCount--; + + if (TranContext->RefCount == 0) { + TI_DbgPrint(DEBUG_IRP, ("Setting TranContext->CleanupEvent to signaled.\n")); + /* Set the cleanup event */ + KeSetEvent(&TranContext->CleanupEvent, 0, FALSE); + } + + TI_DbgPrint(DEBUG_REFCOUNT, ("TranContext->RefCount (%d).\n", TranContext->RefCount)); + + IoReleaseCancelSpinLock(OldIrql); + + TI_DbgPrint(DEBUG_IRP, ("Leaving.\n")); +} + + +VOID DispCancelRequest( + PDEVICE_OBJECT Device, + PIRP Irp) +/* + * FUNCTION: Cancels an IRP + * ARGUMENTS: + * Device = Pointer to device object + * Irp = Pointer to an I/O request packet + */ +{ + PIO_STACK_LOCATION IrpSp; + PTRANSPORT_CONTEXT TranContext; + PFILE_OBJECT FileObject; + UCHAR MinorFunction; + NTSTATUS Status = STATUS_SUCCESS; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + FileObject = IrpSp->FileObject; + TranContext = (PTRANSPORT_CONTEXT)FileObject->FsContext; + MinorFunction = IrpSp->MinorFunction; + + TI_DbgPrint(DEBUG_IRP, ("IRP at (0x%X) MinorFunction (0x%X) IrpSp (0x%X).\n", Irp, MinorFunction, IrpSp)); + +#ifdef DBG + if (!Irp->Cancel) + TI_DbgPrint(MIN_TRACE, ("Irp->Cancel is FALSE, should be TRUE.\n")); +#endif + + /* Increase reference count to prevent accidential closure + of the object while inside the cancel routine */ + TranContext->RefCount++; + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + /* Try canceling the request */ + switch(MinorFunction) { + case TDI_SEND: + + case TDI_RECEIVE: + /* FIXME: Close connection */ + break; + + case TDI_SEND_DATAGRAM: + if (FileObject->FsContext2 != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + TI_DbgPrint(MIN_TRACE, ("TDI_SEND_DATAGRAM, but no address file.\n")); + break; + } + + DGCancelSendRequest(TranContext->Handle.AddressHandle, Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + if (FileObject->FsContext2 != (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + TI_DbgPrint(MIN_TRACE, ("TDI_RECEIVE_DATAGRAM, but no address file.\n")); + break; + } + + DGCancelReceiveRequest(TranContext->Handle.AddressHandle, Irp); + break; + + default: + TI_DbgPrint(MIN_TRACE, ("Unknown IRP. MinorFunction (0x%X).\n", MinorFunction)); + break; + } + + if (Status != STATUS_PENDING) + DispCancelComplete(FileObject); + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); +} + + +VOID DispDataRequestComplete( + PVOID Context, + NTSTATUS Status, + ULONG Count) +/* + * FUNCTION: Completes a send/receive IRP + * ARGUMENTS: + * Context = Pointer to context information (IRP) + * Status = Status of the request + * Count = Number of bytes sent or received + */ +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTRANSPORT_CONTEXT TranContext; + KIRQL OldIrql; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + Irp = (PIRP)Context; + IrpSp = IoGetCurrentIrpStackLocation(Irp); + TranContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext; + + IoAcquireCancelSpinLock(&OldIrql); + + IoSetCancelRoutine(Irp, NULL); + TranContext->RefCount--; + TI_DbgPrint(DEBUG_REFCOUNT, ("TranContext->RefCount (%d).\n", TranContext->RefCount)); + if (TranContext->RefCount == 0) { + TI_DbgPrint(DEBUG_IRP, ("Setting TranContext->CleanupEvent to signaled.\n")); + + KeSetEvent(&TranContext->CleanupEvent, 0, FALSE); + } + + if (Irp->Cancel || TranContext->CancelIrps) { + /* The IRP has been cancelled */ + + TI_DbgPrint(DEBUG_IRP, ("IRP is cancelled.\n")); + + Status = STATUS_CANCELLED; + Count = 0; + } + + IoReleaseCancelSpinLock(OldIrql); + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Count; + + TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp)); + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); +} + + +NTSTATUS DispTdiAccept( + PIRP Irp) +/* + * FUNCTION: TDI_ACCEPT handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiAssociateAddress( + PIRP Irp) +/* + * FUNCTION: TDI_ASSOCIATE_ADDRESS handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiConnect( + PIRP Irp) +/* + * FUNCTION: TDI_CONNECT handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiDisassociateAddress( + PIRP Irp) +/* + * FUNCTION: TDI_DISASSOCIATE_ADDRESS handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiDisconnect( + PIRP Irp) +/* + * FUNCTION: TDI_DISCONNECT handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiListen( + PIRP Irp) +/* + * FUNCTION: TDI_LISTEN handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiQueryInformation( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: TDI_QUERY_INFORMATION handler + * ARGUMENTS: + * DeviceObject = Pointer to device object structure + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiReceive( + PIRP Irp) +/* + * FUNCTION: TDI_RECEIVE handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiReceiveDatagram( + PIRP Irp) +/* + * FUNCTION: TDI_RECEIVE_DATAGRAM handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_RECEIVEDG DgramInfo; + PTRANSPORT_CONTEXT TranContext; + TDI_REQUEST Request; + NTSTATUS Status; + ULONG BytesReceived; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + DgramInfo = (PTDI_REQUEST_KERNEL_RECEIVEDG)&(IrpSp->Parameters); + + TranContext = IrpSp->FileObject->FsContext; + /* Initialize a receive request */ + Request.Handle.AddressHandle = TranContext->Handle.AddressHandle; + Request.RequestNotifyObject = DispDataRequestComplete; + Request.RequestContext = Irp; + Status = DispPrepareIrpForCancel(IrpSp->FileObject->FsContext, Irp, (PDRIVER_CANCEL)DispCancelRequest); + if (NT_SUCCESS(Status)) { + Status = UDPReceiveDatagram(&Request, + DgramInfo->ReceiveDatagramInformation, + (PNDIS_BUFFER)Irp->MdlAddress, + DgramInfo->ReceiveLength, + DgramInfo->ReceiveFlags, + DgramInfo->ReturnDatagramInformation, + &BytesReceived); + if (Status != STATUS_PENDING) { + DispDataRequestComplete(Irp, Status, BytesReceived); + /* Return STATUS_PENDING because DispPrepareIrpForCancel marks Irp as pending */ + Status = STATUS_PENDING; + } + } + + TI_DbgPrint(DEBUG_IRP, ("Leaving. Status is (0x%X)\n", Status)); + + return Status; +} + + +NTSTATUS DispTdiSend( + PIRP Irp) +/* + * FUNCTION: TDI_SEND handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS DispTdiSendDatagram( + PIRP Irp) +/* + * FUNCTION: TDI_SEND_DATAGRAM handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + PIO_STACK_LOCATION IrpSp; + TDI_REQUEST Request; + PTDI_REQUEST_KERNEL_SENDDG DgramInfo; + PTRANSPORT_CONTEXT TranContext; + NTSTATUS Status; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + DgramInfo = (PTDI_REQUEST_KERNEL_SENDDG)&(IrpSp->Parameters); + + TranContext = IrpSp->FileObject->FsContext; + /* Initialize a send request */ + Request.Handle.AddressHandle = TranContext->Handle.AddressHandle; + Request.RequestNotifyObject = DispDataRequestComplete; + Request.RequestContext = Irp; + + Status = DispPrepareIrpForCancel(IrpSp->FileObject->FsContext, Irp, (PDRIVER_CANCEL)DispCancelRequest); + if (NT_SUCCESS(Status)) { + + /* FIXME: DgramInfo->SendDatagramInformation->RemoteAddress + must be of type PTDI_ADDRESS_IP */ + + Status = (*((PADDRESS_FILE)Request.Handle.AddressHandle)->Send)( + &Request, DgramInfo->SendDatagramInformation, + (PNDIS_BUFFER)Irp->MdlAddress, DgramInfo->SendLength); + if (Status != STATUS_PENDING) { + DispDataRequestComplete(Irp, Status, 0); + /* Return STATUS_PENDING because DispPrepareIrpForCancel marks Irp as pending */ + Status = STATUS_PENDING; + } + } + + TI_DbgPrint(DEBUG_IRP, ("Leaving.\n")); + + return Status; +} + + +NTSTATUS DispTdiSetEventHandler( + PIRP Irp) +/* + * FUNCTION: TDI_SET_EVENT_HANDER handler + * ARGUMENTS: + * Irp = Pointer to a I/O request packet + * RETURNS: + * Status of operation + */ +{ +#ifdef _MSC_VER + PTDI_REQUEST_KERNEL_SET_EVENT Parameters; + PTRANSPORT_CONTEXT TranContext; + PIO_STACK_LOCATION IrpSp; + PADDRESS_FILE AddrFile; + NTSTATUS Status; + KIRQL OldIrql; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + /* Get associated address file object. Quit if none exists */ + TranContext = IrpSp->FileObject->FsContext; + if (!TranContext) { + TI_DbgPrint(MIN_TRACE, ("Bad transport context.\n")); + return STATUS_INVALID_PARAMETER; + } + + AddrFile = (PADDRESS_FILE)TranContext->Handle.AddressHandle; + if (!AddrFile) { + TI_DbgPrint(MIN_TRACE, ("No address file object.\n")); + return STATUS_INVALID_PARAMETER; + } + + Parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)&IrpSp->Parameters; + Status = STATUS_SUCCESS; + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + + /* Set the event handler. if an event handler is associated with + a specific event, it's flag (RegisteredXxxHandler) is TRUE. + If an event handler is not used it's flag is FALSE */ + switch (Parameters->EventType) { + case TDI_EVENT_CONNECT: + if (!Parameters->EventHandler) { + AddrFile->ConnectionHandler = + (PTDI_IND_CONNECT)TdiDefaultConnectHandler; + AddrFile->ConnectionHandlerContext = NULL; + AddrFile->RegisteredConnectionHandler = FALSE; + } else { + AddrFile->ConnectionHandler = + (PTDI_IND_CONNECT)Parameters->EventHandler; + AddrFile->ConnectionHandlerContext = Parameters->EventContext; + AddrFile->RegisteredConnectionHandler = TRUE; + } + break; + + case TDI_EVENT_DISCONNECT: + if (!Parameters->EventHandler) { + AddrFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)TdiDefaultDisconnectHandler; + AddrFile->DisconnectHandlerContext = NULL; + AddrFile->RegisteredDisconnectHandler = FALSE; + } else { + AddrFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)Parameters->EventHandler; + AddrFile->DisconnectHandlerContext = Parameters->EventContext; + AddrFile->RegisteredDisconnectHandler = TRUE; + } + break; + + case TDI_EVENT_RECEIVE: + if (Parameters->EventHandler == NULL) { + AddrFile->ReceiveHandler = + (PTDI_IND_RECEIVE)TdiDefaultReceiveHandler; + AddrFile->ReceiveHandlerContext = NULL; + AddrFile->RegisteredReceiveHandler = FALSE; + } else { + AddrFile->ReceiveHandler = + (PTDI_IND_RECEIVE)Parameters->EventHandler; + AddrFile->ReceiveHandlerContext = Parameters->EventContext; + AddrFile->RegisteredReceiveHandler = TRUE; + } + break; + + case TDI_EVENT_RECEIVE_EXPEDITED: + if (Parameters->EventHandler == NULL) { + AddrFile->ExpeditedReceiveHandler = + (PTDI_IND_RECEIVE_EXPEDITED)TdiDefaultRcvExpeditedHandler; + AddrFile->ExpeditedReceiveHandlerContext = NULL; + AddrFile->RegisteredExpeditedReceiveHandler = FALSE; + } else { + AddrFile->ExpeditedReceiveHandler = + (PTDI_IND_RECEIVE_EXPEDITED)Parameters->EventHandler; + AddrFile->ExpeditedReceiveHandlerContext = Parameters->EventContext; + AddrFile->RegisteredExpeditedReceiveHandler = TRUE; + } + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + if (Parameters->EventHandler == NULL) { + AddrFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + AddrFile->ReceiveDatagramHandlerContext = NULL; + AddrFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + AddrFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)Parameters->EventHandler; + AddrFile->ReceiveDatagramHandlerContext = Parameters->EventContext; + AddrFile->RegisteredReceiveDatagramHandler = TRUE; + } + break; + + case TDI_EVENT_ERROR: + if (Parameters->EventHandler == NULL) { + AddrFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + AddrFile->ErrorHandlerContext = NULL; + AddrFile->RegisteredErrorHandler = FALSE; + } else { + AddrFile->ErrorHandler = + (PTDI_IND_ERROR)Parameters->EventHandler; + AddrFile->ErrorHandlerContext = Parameters->EventContext; + AddrFile->RegisteredErrorHandler = TRUE; + } + break; + + default: + Status = STATUS_INVALID_PARAMETER; + } + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + return Status; +#else + return STATUS_NOT_IMPLEMENTED; +#endif +} + + +NTSTATUS DispTdiSetInformation( + PIRP Irp) +/* + * FUNCTION: TDI_SET_INFORMATION handler + * ARGUMENTS: + * Irp = Pointer to an I/O request packet + * RETURNS: + * Status of operation + */ +{ + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + return STATUS_NOT_IMPLEMENTED; +} + + +VOID DispTdiQueryInformationExComplete( + PVOID Context, + ULONG Status, + UINT ByteCount) +/* + * FUNCTION: Completes a TDI QueryInformationEx request + * ARGUMENTS: + * Context = Pointer to the IRP for the request + * Status = TDI status of the request + * ByteCount = Number of bytes returned in output buffer + */ +{ + PTI_QUERY_CONTEXT QueryContext; + UINT Count = 0; + + QueryContext = (PTI_QUERY_CONTEXT)Context; + if (NT_SUCCESS(Status)) { + Count = CopyBufferToBufferChain( + QueryContext->InputMdl, + FIELD_OFFSET(TCP_REQUEST_QUERY_INFORMATION_EX, Context), + (PUCHAR)&QueryContext->QueryInfo.Context, + CONTEXT_SIZE); + } + + MmUnlockPages(QueryContext->InputMdl); + IoFreeMdl(QueryContext->InputMdl); + MmUnlockPages(QueryContext->OutputMdl); + IoFreeMdl(QueryContext->OutputMdl); + + QueryContext->Irp->IoStatus.Information = Count; + QueryContext->Irp->IoStatus.Status = Status; + + ExFreePool(QueryContext); +} + + +NTSTATUS DispTdiQueryInformationEx( + PIRP Irp, + PIO_STACK_LOCATION IrpSp) +/* + * FUNCTION: TDI QueryInformationEx handler + * ARGUMENTS: + * Irp = Pointer to I/O request packet + * IrpSp = Pointer to current stack location of Irp + * RETURNS: + * Status of operation + */ +{ + PTCP_REQUEST_QUERY_INFORMATION_EX InputBuffer; + PTRANSPORT_CONTEXT TranContext; + PTI_QUERY_CONTEXT QueryContext; + PVOID OutputBuffer; + TDI_REQUEST Request; + UINT Size; + UINT InputBufferLength; + UINT OutputBufferLength; + BOOLEAN InputMdlLocked = FALSE; + BOOLEAN OutputMdlLocked = FALSE; + PMDL InputMdl = NULL; + PMDL OutputMdl = NULL; + NTSTATUS Status = STATUS_SUCCESS; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + TranContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext; + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + Request.Handle.AddressHandle = TranContext->Handle.AddressHandle; + break; + + case TDI_CONNECTION_FILE: + Request.Handle.ConnectionContext = TranContext->Handle.ConnectionContext; + break; + + case TDI_CONTROL_CHANNEL_FILE: + Request.Handle.ControlChannel = TranContext->Handle.ControlChannel; + break; + + default: + TI_DbgPrint(MIN_TRACE, ("Invalid transport context\n")); + return STATUS_INVALID_PARAMETER; + } + + InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + /* Validate parameters */ + if ((InputBufferLength == sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)) && + (OutputBufferLength != 0)) { + + InputBuffer = (PTCP_REQUEST_QUERY_INFORMATION_EX) + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + OutputBuffer = Irp->UserBuffer; + + QueryContext = ExAllocatePool(NonPagedPool, sizeof(TI_QUERY_CONTEXT)); + if (QueryContext) { +#ifdef _MSC_VER + try { +#endif + InputMdl = IoAllocateMdl(InputBuffer, + sizeof(TCP_REQUEST_QUERY_INFORMATION_EX), + FALSE, TRUE, NULL); + + OutputMdl = IoAllocateMdl(OutputBuffer, + OutputBufferLength, FALSE, TRUE, NULL); + + if (InputMdl && OutputMdl) { + + MmProbeAndLockPages(InputMdl, Irp->RequestorMode, + IoModifyAccess); + + InputMdlLocked = TRUE; + + MmProbeAndLockPages(OutputMdl, Irp->RequestorMode, + IoWriteAccess); + + OutputMdlLocked = TRUE; + + RtlCopyMemory(&QueryContext->QueryInfo, + InputBuffer, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX)); + + } else + Status = STATUS_INSUFFICIENT_RESOURCES; +#ifdef _MSC_VER + } except(EXCEPTION_EXECUTE_HANDLER) { + Status = GetExceptionCode(); + } +#endif + if (NT_SUCCESS(Status)) { + Size = MmGetMdlByteCount(OutputMdl); + + QueryContext->Irp = Irp; + QueryContext->InputMdl = InputMdl; + QueryContext->OutputMdl = OutputMdl; + + Request.RequestNotifyObject = DispTdiQueryInformationExComplete; + Request.RequestContext = QueryContext; + Status = InfoTdiQueryInformationEx(&Request, + &QueryContext->QueryInfo.ID, OutputMdl, + &Size, &QueryContext->QueryInfo.Context); + DispTdiQueryInformationExComplete(QueryContext, Status, Size); + + TI_DbgPrint(MAX_TRACE, ("Leaving. Status = (0x%X)\n", Status)); + + return Status; + } + + /* An error occurred if we get here */ + + if (InputMdl) { + if (InputMdlLocked) + MmUnlockPages(InputMdl); + IoFreeMdl(InputMdl); + } + + if (OutputMdl) { + if (OutputMdlLocked) + MmUnlockPages(OutputMdl); + IoFreeMdl(OutputMdl); + } + + ExFreePool(QueryContext); + } else + Status = STATUS_INSUFFICIENT_RESOURCES; + } else + Status = STATUS_INVALID_PARAMETER; + + TI_DbgPrint(MIN_TRACE, ("Leaving. Status = (0x%X)\n", Status)); + + return Status; +} + + +NTSTATUS DispTdiSetInformationEx( + PIRP Irp, + PIO_STACK_LOCATION IrpSp) +/* + * FUNCTION: TDI SetInformationEx handler + * ARGUMENTS: + * Irp = Pointer to I/O request packet + * IrpSp = Pointer to current stack location of Irp + * RETURNS: + * Status of operation + */ +{ + PTRANSPORT_CONTEXT TranContext; + PTCP_REQUEST_SET_INFORMATION_EX Info; + TDI_REQUEST Request; + TDI_STATUS Status; + KIRQL OldIrql; + + TI_DbgPrint(DEBUG_IRP, ("Called.\n")); + + TranContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext; + Info = (PTCP_REQUEST_SET_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer; + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + Request.Handle.AddressHandle = TranContext->Handle.AddressHandle; + break; + + case TDI_CONNECTION_FILE: + Request.Handle.ConnectionContext = TranContext->Handle.ConnectionContext; + break; + + case TDI_CONTROL_CHANNEL_FILE: + Request.Handle.ControlChannel = TranContext->Handle.ControlChannel; + break; + + default: + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + + TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp)); + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + + return STATUS_INVALID_PARAMETER; + } + + Status = DispPrepareIrpForCancel(TranContext, Irp, NULL); + if (NT_SUCCESS(Status)) { + Request.RequestNotifyObject = DispDataRequestComplete; + Request.RequestContext = Irp; + + Status = InfoTdiSetInformationEx(&Request, &Info->ID, + &Info->Buffer, Info->BufferSize); + + if (Status != STATUS_PENDING) { + IoAcquireCancelSpinLock(&OldIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(OldIrql); + } + } + + return Status; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/fileobjs.c b/reactos/drivers/net/tcpip/tcpip/fileobjs.c new file mode 100644 index 00000000000..29bb42f0aec --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/fileobjs.c @@ -0,0 +1,396 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: tcpip/fileobjs.c + * PURPOSE: Routines for handling file objects + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include +#include +#include +#include +#include +#include + +LIST_ENTRY AddressFileListHead; +KSPIN_LOCK AddressFileListLock; + + +/* + * FUNCTION: Deletes an address file object + * ARGUMENTS: + * AddrFile = Pointer to address file object to delete + */ +VOID DeleteAddress( + PADDRESS_FILE AddrFile) +{ + KIRQL OldIrql; + PLIST_ENTRY CurrentEntry; + PLIST_ENTRY NextEntry; + PDATAGRAM_SEND_REQUEST SendRequest; + PDATAGRAM_RECEIVE_REQUEST ReceiveRequest; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + /* Remove address file from the global list */ + KeAcquireSpinLock(&AddressFileListLock, &OldIrql); + RemoveEntryList(&AddrFile->ListEntry); + KeReleaseSpinLock(&AddressFileListLock, OldIrql); + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + + /* FIXME: Kill TCP connections on this address file object */ + + /* Return pending requests with error */ + + TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting receive requests on AddrFile at (0x%X).\n", AddrFile)); + + /* Go through pending receive request list and cancel them all */ + CurrentEntry = AddrFile->ReceiveQueue.Flink; + while (CurrentEntry != &AddrFile->ReceiveQueue) { + NextEntry = CurrentEntry->Flink; + ReceiveRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_RECEIVE_REQUEST, ListEntry); + /* Abort the request and free its resources */ + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + (*ReceiveRequest->Complete)(ReceiveRequest->Context, STATUS_ADDRESS_CLOSED, 0); + PoolFreeBuffer(ReceiveRequest); + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + CurrentEntry = NextEntry; + } + + TI_DbgPrint(DEBUG_ADDRFILE, ("Aborting send requests on address file at (0x%X).\n", AddrFile)); + + /* Go through pending send request list and cancel them all */ + CurrentEntry = AddrFile->TransmitQueue.Flink; + while (CurrentEntry != &AddrFile->TransmitQueue) { + NextEntry = CurrentEntry->Flink; + SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry); + /* Abort the request and free its resources */ + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + (*SendRequest->Complete)(SendRequest->Context, STATUS_ADDRESS_CLOSED, 0); + PoolFreeBuffer(SendRequest); + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + CurrentEntry = NextEntry; + } + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + /* Dereference address entry */ + DereferenceObject(AddrFile->ADE); + + /* Dereference address cache */ + if (AddrFile->AddrCache) + DereferenceObject(AddrFile->AddrCache); + +#ifdef DBG + /* Remove reference provided at creation time */ + AddrFile->RefCount--; + + if (AddrFile->RefCount != 0) + TI_DbgPrint(DEBUG_REFCOUNT, ("AddrFile->RefCount is (%d) (should be 0).\n", AddrFile->RefCount)); +#endif + + PoolFreeBuffer(AddrFile); + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); +} + + +VOID RequestWorker( + PVOID Context) +/* + * FUNCTION: Worker routine for processing address file object requests + * ARGUMENTS: + * Context = Pointer to context information (ADDRESS_FILE) + */ +{ + KIRQL OldIrql; + PLIST_ENTRY CurrentEntry; + PADDRESS_FILE AddrFile = Context; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + + /* Check it the address file should be deleted */ + if (AF_IS_PENDING(AddrFile, AFF_DELETE)) { + DATAGRAM_COMPLETION_ROUTINE RtnComplete; + PVOID RtnContext; + + RtnComplete = AddrFile->Complete; + RtnContext = AddrFile->Context; + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + DeleteAddress(AddrFile); + + (*RtnComplete)(RtnContext, TDI_SUCCESS, 0); + + TI_DbgPrint(MAX_TRACE, ("Leaving (delete).\n")); + + return; + } + + /* Check if there is a pending send request */ + if (AF_IS_PENDING(AddrFile, AFF_SEND)) { + if (!IsListEmpty(&AddrFile->TransmitQueue)) { + PDATAGRAM_SEND_REQUEST SendRequest; + + CurrentEntry = RemoveHeadList(&AddrFile->TransmitQueue); + SendRequest = CONTAINING_RECORD(CurrentEntry, DATAGRAM_SEND_REQUEST, ListEntry); + + AF_CLR_BUSY(AddrFile); + + ReferenceObject(AddrFile); + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + /* The send routine processes the send requests in + the transmit queue on the address file. When the + queue is empty the pending send flag is cleared. + The routine may return with the pending send flag + set. This can happen if there was not enough free + resources available to complete all send requests */ + DGSend(AddrFile, SendRequest); + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + DereferenceObject(AddrFile); + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + TI_DbgPrint(MAX_TRACE, ("Leaving (send request).\n")); + + return; + } else + /* There was a pending send, but no send request. + Print a debug message and continue */ + TI_DbgPrint(MIN_TRACE, ("Pending send, but no send request.\n")); + } + + AF_CLR_BUSY(AddrFile); + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); +} + + +/* + * FUNCTION: Open an address file object + * ARGUMENTS: + * Request = Pointer to TDI request structure for this request + * Address = Pointer to address to be opened + * Protocol = Protocol on which to open the address + * Options = Pointer to option buffer + * RETURNS: + * Status of operation + */ +NTSTATUS FileOpenAddress( + PTDI_REQUEST Request, + PTA_ADDRESS_IP Address, + USHORT Protocol, + PVOID Options) +{ + PADDRESS_FILE AddrFile; + IPv4_RAW_ADDRESS IPv4Address; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + AddrFile = PoolAllocateBuffer(sizeof(ADDRESS_FILE)); + if (!AddrFile) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + TI_DbgPrint(DEBUG_ADDRFILE, ("Address file object allocated at (0x%X).\n", AddrFile)); + + RtlZeroMemory(AddrFile, sizeof(ADDRESS_FILE)); + + /* Make sure address is a local unicast address or 0 */ + + /* Locate address entry. If specified address is 0, a random address is chosen */ + + IPv4Address = Address->Address[0].Address[0].in_addr; + if (IPv4Address == 0) + AddrFile->ADE = IPGetDefaultADE(ADE_UNICAST); + else + AddrFile->ADE = AddrLocateADEv4(IPv4Address); + + if (!AddrFile->ADE) { + PoolFreeBuffer(AddrFile); + TI_DbgPrint(MIN_TRACE, ("Non-local address given (0x%X).\n", DN2H(IPv4Address))); + return STATUS_INVALID_PARAMETER; + } + + TI_DbgPrint(DEBUG_ADDRFILE, ("Opening address (0x%X) for communication.\n", + DN2H(AddrFile->ADE->Address->Address.IPv4Address))); + + /* Protocol specific handling */ + switch (Protocol) { + case IPPROTO_TCP: + /* FIXME: TCP */ + TI_DbgPrint(MIN_TRACE, ("TCP is not supported.\n")); + DereferenceObject(AddrFile->ADE); + PoolFreeBuffer(AddrFile); + return STATUS_INVALID_PARAMETER; + case IPPROTO_UDP: + /* FIXME: If specified port is 0, a port is chosen dynamically */ + AddrFile->Port = Address->Address[0].Address[0].sin_port; + AddrFile->Send = UDPSendDatagram; + break; + default: + /* Use raw IP for all other protocols */ + AddrFile->Send = RawIPSendDatagram; + break; + } + + /* Set protocol */ + AddrFile->Protocol = Protocol; + + /* Initialize receive and transmit queues */ + InitializeListHead(&AddrFile->ReceiveQueue); + InitializeListHead(&AddrFile->TransmitQueue); + + /* Initialize work queue item. We use this for pending requests */ + ExInitializeWorkItem(&AddrFile->WorkItem, RequestWorker, AddrFile); + + /* Initialize spin lock that protects the address file object */ + KeInitializeSpinLock(&AddrFile->Lock); + + /* Reference the object */ + AddrFile->RefCount = 1; + + /* Set valid flag so the address can be used */ + AF_SET_VALID(AddrFile); + + /* Return address file object */ + Request->Handle.AddressHandle = AddrFile; + + /* Add address file to global list */ + ExInterlockedInsertTailList(&AddressFileListHead, &AddrFile->ListEntry, &AddressFileListLock); + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); + + return STATUS_SUCCESS; +} + + +/* + * FUNCTION: Closes an address file object + * ARGUMENTS: + * Request = Pointer to TDI request structure for this request + * RETURNS: + * Status of operation + */ +NTSTATUS FileCloseAddress( + PTDI_REQUEST Request) +{ + KIRQL OldIrql; + PADDRESS_FILE AddrFile; + NTSTATUS Status = STATUS_SUCCESS; + + TI_DbgPrint(MID_TRACE, ("Called.\n")); + + AddrFile = Request->Handle.AddressHandle; + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + + if ((!AF_IS_BUSY(AddrFile)) && (AddrFile->RefCount == 1)) { + /* Set address file object exclusive to us */ + AF_SET_BUSY(AddrFile); + AF_CLR_VALID(AddrFile); + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + DeleteAddress(AddrFile); + } else { + if (!AF_IS_PENDING(AddrFile, AFF_DELETE)) { + AddrFile->Complete = Request->RequestNotifyObject; + AddrFile->Context = Request->RequestContext; + + /* Shedule address file for deletion */ + AF_SET_PENDING(AddrFile, AFF_DELETE); + AF_CLR_VALID(AddrFile); + + if (!AF_IS_BUSY(AddrFile)) { + /* Worker function is not running, so shedule it to run */ + AF_SET_BUSY(AddrFile); + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + ExQueueWorkItem(&AddrFile->WorkItem, CriticalWorkQueue); + } else + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n")); + + return STATUS_PENDING; + } else + Status = STATUS_ADDRESS_CLOSED; + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + } + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); + + return Status; +} + + +/* + * FUNCTION: Opens a connection file object + * ARGUMENTS: + * Request = Pointer to TDI request structure for this request + * RETURNS: + * Status of operation + */ +NTSTATUS FileOpenConnection( + PTDI_REQUEST Request) +{ + return STATUS_NOT_IMPLEMENTED; +} + + +/* + * FUNCTION: Closes an connection file object + * ARGUMENTS: + * Request = Pointer to TDI request structure for this request + * RETURNS: + * Status of operation + */ +NTSTATUS FileCloseConnection( + PTDI_REQUEST Request) +{ + return STATUS_NOT_IMPLEMENTED; +} + + +/* + * FUNCTION: Opens a control channel file object + * ARGUMENTS: + * Request = Pointer to TDI request structure for this request + * RETURNS: + * Status of operation + */ +NTSTATUS FileOpenControlChannel( + PTDI_REQUEST Request) +{ + return STATUS_NOT_IMPLEMENTED; +} + + +/* + * FUNCTION: Closes a control channel file object + * ARGUMENTS: + * Request = Pointer to TDI request structure for this request + * RETURNS: + * Status of operation + */ +NTSTATUS FileCloseControlChannel( + PTDI_REQUEST Request) +{ + return STATUS_NOT_IMPLEMENTED; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/info.c b/reactos/drivers/net/tcpip/tcpip/info.c new file mode 100644 index 00000000000..fc8f07d9505 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/info.c @@ -0,0 +1,339 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: tcpip/info.c + * PURPOSE: TDI query and set information routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include + + +TDI_STATUS IPTdiQueryInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PNDIS_BUFFER Buffer, + PUINT BufferSize, + PVOID Context) +/* + * FUNCTION: Returns extended information about network layer + * ARGUMENTS: + * Request = Pointer to TDI request structure for the request + * ID = TDI object ID + * Buffer = Pointer to buffer with data to use. + * BufferSize = Pointer to buffer with size of Buffer. On return + * this is filled with number of bytes returned + * Context = Pointer to context buffer + * RETURNS: + * Status of operation + */ +{ + PLIST_ENTRY CurrentIFEntry; + PLIST_ENTRY CurrentADEEntry; + PIP_INTERFACE CurrentIF; + PADDRESS_ENTRY CurrentADE; + IPADDR_ENTRY IpAddress; + IPSNMP_INFO SnmpInfo; + ULONG Temp; + UINT Count; + ULONG Entity; + KIRQL OldIrql; + UINT BufSize = *BufferSize; + + /* Make return parameters consistent every time */ + *BufferSize = 0; + + Entity = ID->toi_entity.tei_entity; + if (Entity != CL_NL_ENTITY) { + /* We can't handle this entity */ + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_entity.tei_instance != TL_INSTANCE) + /* We only support a single instance */ + return TDI_INVALID_REQUEST; + if (ID->toi_class == INFO_CLASS_GENERIC) { + if (ID->toi_type == INFO_TYPE_PROVIDER && + ID->toi_id == ENTITY_TYPE_ID) { + + if (BufSize < sizeof(ULONG)) + return TDI_BUFFER_TOO_SMALL; + Temp = CL_NL_IP; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&Temp, sizeof(ULONG)); + + return TDI_SUCCESS; + } + return TDI_INVALID_PARAMETER; + } + + if (ID->toi_class == INFO_CLASS_PROTOCOL) { + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + switch (ID->toi_id) { + case IP_MIB_ADDRTABLE_ENTRY_ID: + Temp = 0; + + KeAcquireSpinLock(&InterfaceListLock, &OldIrql); + + CurrentIFEntry = InterfaceListHead.Flink; + while (CurrentIFEntry != &InterfaceListHead) { + CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry); + + if (Temp + sizeof(IPADDR_ENTRY) > BufSize) { + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + return TDI_BUFFER_TOO_SMALL; + } + + IpAddress.Addr = 0; + IpAddress.BcastAddr = 0; + IpAddress.Mask = 0; + + /* Locate the diffrent addresses and put them the right place */ + CurrentADEEntry = CurrentIF->ADEListHead.Flink; + while (CurrentADEEntry != &CurrentIF->ADEListHead) { + CurrentADE = CONTAINING_RECORD(CurrentADEEntry, ADDRESS_ENTRY, ListEntry); + + switch (CurrentADE->Type) { + case ADE_UNICAST: + IpAddress.Addr = CurrentADE->Address->Address.IPv4Address; + break; + case ADE_MULTICAST: + IpAddress.BcastAddr = CurrentADE->Address->Address.IPv4Address; + break; + case ADE_ADDRMASK: + IpAddress.Mask = CurrentADE->Address->Address.IPv4Address; + break; + default: + /* Should not happen */ + TI_DbgPrint(MIN_TRACE, ("Unknown address entry type (0x%X)\n", CurrentADE->Type)); + break; + } + CurrentADEEntry = CurrentADEEntry->Flink; + } + /* Pack the address information into IPADDR_ENTRY structure */ + IpAddress.Index = 0; + IpAddress.ReasmSize = 0; + IpAddress.Context = 0; + IpAddress.Pad = 0; + + Count = CopyBufferToBufferChain(Buffer, Temp, (PUCHAR)&IpAddress, sizeof(IPADDR_ENTRY)); + + Temp += sizeof(IPADDR_ENTRY); + + CurrentIFEntry = CurrentIFEntry->Flink; + } + + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + + return TDI_SUCCESS; + + case IP_MIB_STATS_ID: + if (BufSize < sizeof(IPSNMP_INFO)) + return TDI_BUFFER_TOO_SMALL; + + RtlZeroMemory(&SnmpInfo, sizeof(IPSNMP_INFO)); + + /* Count number of addresses */ + Count = 0; + KeAcquireSpinLock(&InterfaceListLock, &OldIrql); + + CurrentIFEntry = InterfaceListHead.Flink; + while (CurrentIFEntry != &InterfaceListHead) { + CurrentIF = CONTAINING_RECORD(CurrentIFEntry, IP_INTERFACE, ListEntry); + Count++; + CurrentIFEntry = CurrentIFEntry->Flink; + } + + KeReleaseSpinLock(&InterfaceListLock, OldIrql); + + SnmpInfo.NumAddr = Count; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&SnmpInfo, sizeof(IPSNMP_INFO)); + + return TDI_SUCCESS; + + default: + /* We can't handle this ID */ + return TDI_INVALID_PARAMETER; + } + } + + return TDI_INVALID_PARAMETER; +} + + +TDI_STATUS InfoTdiQueryInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PNDIS_BUFFER Buffer, + PUINT BufferSize, + PVOID Context) +/* + * FUNCTION: Returns extended information + * ARGUMENTS: + * Request = Pointer to TDI request structure for the request + * ID = TDI object ID + * Buffer = Pointer to buffer with data to use + * BufferSize = Pointer to buffer with size of Buffer. On return + * this is filled with number of bytes returned + * Context = Pointer to context buffer + * RETURNS: + * Status of operation + */ +{ + PLIST_ENTRY CurrentADFEntry; + PADDRESS_FILE CurrentADF; + ADDRESS_INFO Info; + KIRQL OldIrql; + UINT Entity; + UINT Count; + UINT Size; + ULONG Temp; + UINT Offset = 0; + UINT BufSize = *BufferSize; + + /* Check wether it is a query for a list of entities */ + Entity = ID->toi_entity.tei_entity; + if (Entity == GENERIC_ENTITY) { + if (ID->toi_class != INFO_CLASS_GENERIC || + ID->toi_type != INFO_TYPE_PROVIDER || + ID->toi_id != ENTITY_LIST_ID) + return TDI_INVALID_PARAMETER; + + *BufferSize = 0; + + Size = EntityCount * sizeof(TDIEntityID); + if (BufSize < Size) + /* The buffer is too small to contain requested data */ + return TDI_BUFFER_TOO_SMALL; + + /* Return entity list */ + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)EntityList, Size); + + *BufferSize = Size; + + return TDI_SUCCESS; + } + + if ((Entity != CL_TL_ENTITY) && (Entity != CO_TL_ENTITY)) { + /* We can't handle this entity, pass it on */ + return IPTdiQueryInformationEx( + Request, ID, Buffer, BufferSize, Context); + } + + /* Make return parameters consistent every time */ + *BufferSize = 0; + + if (ID->toi_entity.tei_instance != TL_INSTANCE) + /* We only support a single instance */ + return TDI_INVALID_REQUEST; + + if (ID->toi_class == INFO_CLASS_GENERIC) { + + if (ID->toi_type != INFO_TYPE_PROVIDER || + ID->toi_id != ENTITY_TYPE_ID) + return TDI_INVALID_PARAMETER; + + if (BufSize < sizeof(ULONG)) + return TDI_BUFFER_TOO_SMALL; + + if (Entity == CL_TL_ENTITY) + Temp = CL_TL_UDP; + else if (Entity == CO_TL_ENTITY) + Temp = CO_TL_TCP; + else + return TDI_INVALID_PARAMETER; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&Temp, sizeof(ULONG)); + + return TDI_SUCCESS; + } + + if (ID->toi_class == INFO_CLASS_PROTOCOL) { + + if (ID->toi_type != INFO_TYPE_PROVIDER) + return TDI_INVALID_PARAMETER; + + switch (ID->toi_id) { + case UDP_MIB_STAT_ID: + if (Entity != CL_TL_ENTITY) + return TDI_INVALID_PARAMETER; + + if (BufSize < sizeof(UDPStats)) + return TDI_BUFFER_TOO_SMALL; + + Count = CopyBufferToBufferChain(Buffer, 0, (PUCHAR)&UDPStats, sizeof(UDP_STATISTICS)); + + return TDI_SUCCESS; + + case UDP_MIB_TABLE_ID: + if (Entity != CL_TL_ENTITY) + return TDI_INVALID_PARAMETER; + + Offset = 0; + + KeAcquireSpinLock(&AddressFileListLock, &OldIrql); + + CurrentADFEntry = AddressFileListHead.Flink; + while (CurrentADFEntry != &AddressFileListHead) { + CurrentADF = CONTAINING_RECORD(CurrentADFEntry, ADDRESS_FILE, ListEntry); + + if (Offset + sizeof(ADDRESS_INFO) > BufSize) { + KeReleaseSpinLock(&AddressFileListLock, OldIrql); + *BufferSize = Offset; + return TDI_BUFFER_OVERFLOW; + } + + Info.LocalAddress = CurrentADF->ADE->Address->Address.IPv4Address; + Info.LocalPort = CurrentADF->Port; + + Count = CopyBufferToBufferChain(Buffer, Offset, (PUCHAR)&Info, sizeof(ADDRESS_INFO)); + + Offset += Count; + + CurrentADFEntry = CurrentADFEntry->Flink; + } + + KeReleaseSpinLock(&AddressFileListLock, OldIrql); + + *BufferSize = Offset; + + return STATUS_SUCCESS; + + default: + /* We can't handle this ID */ + return TDI_INVALID_PARAMETER; + } + } + + return TDI_INVALID_PARAMETER; +} + + +TDI_STATUS InfoTdiSetInformationEx( + PTDI_REQUEST Request, + TDIObjectID *ID, + PVOID Buffer, + UINT BufferSize) +/* + * FUNCTION: Sets extended information + * ARGUMENTS: + * Request = Pointer to TDI request structure for the request + * ID = Pointer to TDI object ID + * Buffer = Pointer to buffer with data to use + * BufferSize = Size of Buffer + * RETURNS: + * Status of operation + */ +{ + /* FIXME: Set extended information */ + + return TDI_INVALID_REQUEST; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/main.c b/reactos/drivers/net/tcpip/tcpip/main.c new file mode 100644 index 00000000000..3162b910c22 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/main.c @@ -0,0 +1,805 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: tcpip/main.c + * PURPOSE: Driver entry point + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef DBG +/* See debug.h for debug/trace constants */ +DWORD DebugTraceLevel = MIN_TRACE; +#endif /* DBG */ + +PDEVICE_OBJECT TCPDeviceObject = NULL; +PDEVICE_OBJECT UDPDeviceObject = NULL; +PDEVICE_OBJECT IPDeviceObject = NULL; +PDEVICE_OBJECT RawIPDeviceObject = NULL; +NDIS_HANDLE GlobalPacketPool = NULL; +NDIS_HANDLE GlobalBufferPool = NULL; +TDIEntityID *EntityList = NULL; +ULONG EntityCount = 0; +UDP_STATISTICS UDPStats; + + +VOID TiWriteErrorLog( + PDRIVER_OBJECT DriverContext, + NTSTATUS ErrorCode, + ULONG UniqueErrorValue, + NTSTATUS FinalStatus, + PWSTR String, + ULONG DumpDataCount, + PULONG DumpData) +/* + * FUNCTION: Writes an error log entry + * ARGUMENTS: + * DriverContext = Pointer to the driver or device object + * ErrorCode = An error code to put in the log entry + * UniqueErrorValue = UniqueErrorValue in the error log packet + * FinalStatus = FinalStatus in the error log packet + * String = If not NULL, a pointer to a string to put in log entry + * DumpDataCount = Number of ULONGs of dump data + * DumpData = Pointer to dump data for the log entry + */ +{ +#ifdef _MSC_VER + PIO_ERROR_LOG_PACKET LogEntry; + UCHAR EntrySize; + ULONG StringSize; + PUCHAR pString; + static WCHAR DriverName[] = L"TCP/IP"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)) + sizeof(DriverName); + + if (String) { + StringSize = (wcslen(String) * sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)StringSize; + } + + LogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + DriverContext, EntrySize); + + if (LogEntry) { + LogEntry->MajorFunctionCode = -1; + LogEntry->RetryCount = -1; + LogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + LogEntry->NumberOfStrings = (String == NULL) ? 1 : 2; + LogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + (DumpDataCount-1) * sizeof(ULONG); + LogEntry->EventCategory = 0; + LogEntry->ErrorCode = ErrorCode; + LogEntry->UniqueErrorValue = UniqueErrorValue; + LogEntry->FinalStatus = FinalStatus; + LogEntry->SequenceNumber = -1; + LogEntry->IoControlCode = 0; + + if (DumpDataCount) + RtlCopyMemory(LogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + + pString = ((PUCHAR)LogEntry) + LogEntry->StringOffset; + RtlCopyMemory(pString, DriverName, sizeof(DriverName)); + pString += sizeof(DriverName); + + if (String) + RtlCopyMemory(pString, String, StringSize); + + IoWriteErrorLogEntry(LogEntry); + } +#endif +} + + +/* + * FUNCTION: Creates a file object + * ARGUMENTS: + * DeviceObject = Pointer to a device object for this driver + * Irp = Pointer to a I/O request packet + * RETURNS: + * Status of the operation + */ +NTSTATUS TiCreateFileObject( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IrpSp; + PFILE_FULL_EA_INFORMATION EaInfo; + PTA_ADDRESS_IP Address; + PTRANSPORT_CONTEXT Context; + TDI_REQUEST Request; + NTSTATUS Status; + + TI_DbgPrint(DEBUG_IRP, ("Called. DeviceObject is at (0x%X), IRP is at (0x%X).\n", DeviceObject, Irp)); + + EaInfo = Irp->AssociatedIrp.SystemBuffer; + + /* Parameter check */ + if (!EaInfo) { + TI_DbgPrint(MIN_TRACE, ("No EA information in IRP.\n")); + return STATUS_INVALID_PARAMETER; + } + + /* Allocate resources here. We release them again if something failed */ + Context = ExAllocatePool(NonPagedPool, sizeof(TRANSPORT_CONTEXT)); + if (!Context) { + TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Context->RefCount = 1; + Context->CancelIrps = FALSE; + KeInitializeEvent(&Context->CleanupEvent, NotificationEvent, FALSE); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + IrpSp->FileObject->FsContext = Context; + Request.RequestContext = Irp; + + /* Branch to the right handler */ + if ((EaInfo->EaNameLength==TDI_TRANSPORT_ADDRESS_LENGTH) && + (RtlCompareMemory(&EaInfo->EaName, TdiTransportAddress, + TDI_TRANSPORT_ADDRESS_LENGTH) == TDI_TRANSPORT_ADDRESS_LENGTH)) { + /* This is a request to open an address */ + + /* Parameter checks */ + Address = (PTA_ADDRESS_IP)(EaInfo->EaName + EaInfo->EaNameLength + 1); + if ((EaInfo->EaValueLength < sizeof(TA_ADDRESS_IP)) || + (Address->TAAddressCount != 1) || + (Address->Address[0].AddressLength < sizeof(TDI_ADDRESS_IP)) || + (Address->Address[0].AddressType != TDI_ADDRESS_TYPE_IP)) { + TI_DbgPrint(MIN_TRACE, ("Parameters are invalid.\n")); + ExFreePool(Context); + return STATUS_INVALID_PARAMETER; + } + + /* Open address file object */ + /* FIXME: Protocol depends on device object */ + Status = FileOpenAddress(&Request, Address, IPPROTO_UDP, NULL); + if (NT_SUCCESS(Status)) { + IrpSp->FileObject->FsContext2 = (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + Context->Handle.AddressHandle = Request.Handle.AddressHandle; + } + } else { + TI_DbgPrint(MIN_TRACE, ("Connection point, and control connections are not supported.\n")); + /* FIXME: Open a connection endpoint, or control connection */ + Status = STATUS_NOT_IMPLEMENTED; + } + + if (!NT_SUCCESS(Status)) + ExFreePool(Context); + + TI_DbgPrint(DEBUG_IRP, ("Leaving. Status = (0x%X).\n", Status)); + + return Status; +} + + +VOID TiCleanupFileObjectComplete( + PVOID Context, + NTSTATUS Status) +/* + * FUNCTION: Completes an object cleanup IRP I/O request + * ARGUMENTS: + * Context = Pointer to the IRP for this request + * Status = Final status of the operation + */ +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTRANSPORT_CONTEXT TranContext; + KIRQL OldIrql; + + Irp = (PIRP)Context; + IrpSp = IoGetCurrentIrpStackLocation(Irp); + TranContext = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext; + + Irp->IoStatus.Status = Status; + + IoAcquireCancelSpinLock(&OldIrql); + + /* Remove the initial reference provided at object creation time */ + TranContext->RefCount--; + +#ifdef DBG + if (TranContext->RefCount != 0) + TI_DbgPrint(DEBUG_REFCOUNT, ("TranContext->RefCount is %i, should be 0.\n", TranContext->RefCount)); +#endif + + KeSetEvent(&TranContext->CleanupEvent, 0, FALSE); + + IoReleaseCancelSpinLock(OldIrql); +} + + +/* + * FUNCTION: Releases resources used by a file object + * ARGUMENTS: + * DeviceObject = Pointer to a device object for this driver + * Irp = Pointer to a I/O request packet + * RETURNS: + * Status of the operation + * NOTES: + * This function does not pend + */ +NTSTATUS TiCleanupFileObject( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +{ + PIO_STACK_LOCATION IrpSp; + PTRANSPORT_CONTEXT Context; + TDI_REQUEST Request; + NTSTATUS Status; + KIRQL OldIrql; + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + Context = IrpSp->FileObject->FsContext; + if (!Context) { + TI_DbgPrint(MIN_TRACE, ("Parameters are invalid.\n")); + return STATUS_INVALID_PARAMETER; + } + + IoAcquireCancelSpinLock(&OldIrql); + + Context->CancelIrps = TRUE; + KeResetEvent(&Context->CleanupEvent); + + IoReleaseCancelSpinLock(OldIrql); + + Request.RequestNotifyObject = TiCleanupFileObjectComplete; + Request.RequestContext = Irp; + + switch ((ULONG_PTR)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + Request.Handle.AddressHandle = Context->Handle.AddressHandle; + Status = FileCloseAddress(&Request); + break; + + case TDI_CONNECTION_FILE: + Request.Handle.ConnectionContext = Context->Handle.ConnectionContext; + Status = FileCloseConnection(&Request); + break; + + case TDI_CONTROL_CHANNEL_FILE: + Request.Handle.ControlChannel = Context->Handle.ControlChannel; + Status = FileCloseControlChannel(&Request); + break; + + default: + /* This should never happen */ + + TI_DbgPrint(MIN_TRACE, ("Unknown transport context.\n")); + + IoAcquireCancelSpinLock(&OldIrql); + Context->CancelIrps = FALSE; + IoReleaseCancelSpinLock(OldIrql); + + return STATUS_INVALID_PARAMETER; + } + + if (Status != STATUS_PENDING) + TiCleanupFileObjectComplete(Irp, Status); + + KeWaitForSingleObject(&Context->CleanupEvent, + UserRequest, KernelMode, FALSE, NULL); + + return Irp->IoStatus.Status; +} + + +NTSTATUS TiDispatchOpenClose( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Main dispath routine + * ARGUMENTS: + * DeviceObject = Pointer to a device object for this driver + * Irp = Pointer to a I/O request packet + * RETURNS: + * Status of the operation + */ +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + PTRANSPORT_CONTEXT Context; + + TI_DbgPrint(DEBUG_IRP, ("Called. DeviceObject is at (0x%X), IRP is at (0x%X).\n", DeviceObject, Irp)); + + IoMarkIrpPending(Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + switch (IrpSp->MajorFunction) { + /* Open an address file, connection endpoint, or control connection */ + case IRP_MJ_CREATE: + Status = TiCreateFileObject(DeviceObject, Irp); + break; + + /* Close an address file, connection endpoint, or control connection */ + case IRP_MJ_CLOSE: + Context = (PTRANSPORT_CONTEXT)IrpSp->FileObject->FsContext; + if (Context) + ExFreePool(Context); + Status = STATUS_SUCCESS; + break; + + /* Release resources bound to an address file, connection endpoint, + or control connection */ + case IRP_MJ_CLEANUP: + Status = TiCleanupFileObject(DeviceObject, Irp); + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + + TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp)); + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + TI_DbgPrint(DEBUG_IRP, ("Leaving. Status is (0x%X)\n", Status)); + + return Status; +} + + +NTSTATUS TiDispatchInternal( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Internal IOCTL dispatch routine + * ARGUMENTS: + * DeviceObject = Pointer to a device object for this driver + * Irp = Pointer to a I/O request packet + * RETURNS: + * Status of the operation + */ +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + TI_DbgPrint(DEBUG_IRP, ("Called. DeviceObject is at (0x%X), IRP is at (0x%X).\n", DeviceObject, Irp)); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + switch (IrpSp->MinorFunction) { + case TDI_RECEIVE: + Status = DispTdiReceive(Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = DispTdiReceiveDatagram(Irp); + break; + + case TDI_SEND: + Status = DispTdiSend(Irp); + break; + + case TDI_SEND_DATAGRAM: + Status = DispTdiSendDatagram(Irp); + break; + + case TDI_ACCEPT: + Status = DispTdiAccept(Irp); + break; + + case TDI_LISTEN: + Status = DispTdiListen(Irp); + break; + + case TDI_CONNECT: + Status = DispTdiConnect(Irp); + break; + + case TDI_DISCONNECT: + Status = DispTdiDisconnect(Irp); + break; + + case TDI_ASSOCIATE_ADDRESS: + Status = DispTdiAssociateAddress(Irp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + Status = DispTdiDisassociateAddress(Irp); + break; + + case TDI_QUERY_INFORMATION: + Status = DispTdiQueryInformation(DeviceObject, Irp); + break; + + case TDI_SET_INFORMATION: + Status = DispTdiSetInformation(Irp); + break; + + case TDI_SET_EVENT_HANDLER: + Status = DispTdiSetEventHandler(Irp); + break; + + case TDI_ACTION: + Status = STATUS_SUCCESS; + break; + + /* An unsupported IOCTL code was submitted */ + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (Status != STATUS_PENDING) { + Irp->IoStatus.Status = Status; + + TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp)); + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + TI_DbgPrint(DEBUG_IRP, ("Leaving. Status = (0x%X).\n", Status)); + + return Status; +} + + +NTSTATUS TiDispatch( + PDEVICE_OBJECT DeviceObject, + PIRP Irp) +/* + * FUNCTION: Dispath routine for IRP_MJ_DEVICE_CONTROL requests + * ARGUMENTS: + * DeviceObject = Pointer to a device object for this driver + * Irp = Pointer to a I/O request packet + * RETURNS: + * Status of the operation + */ +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + TI_DbgPrint(DEBUG_IRP, ("Called. IRP is at (0x%X).\n", Irp)); + + Irp->IoStatus.Information = 0; + + IrpSp = IoGetCurrentIrpStackLocation(Irp); +#ifdef _MSC_VER + Status = TdiMapUserRequest(DeviceObject, Irp, IrpSp); + if (NT_SUCCESS(Status)) { + TiDispatchInternal(DeviceObject, Irp); + Status = STATUS_PENDING; + } else { +#else + if (TRUE) { +#endif + /* See if this request is TCP/IP specific */ + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + case IOCTL_TCP_QUERY_INFORMATION_EX: + Status = DispTdiQueryInformationEx(Irp, IrpSp); + break; + + case IOCTL_TCP_SET_INFORMATION_EX: + Status = DispTdiSetInformationEx(Irp, IrpSp); + break; + + default: + TI_DbgPrint(MIN_TRACE, ("Unknown IOCTL 0x%X\n", + IrpSp->Parameters.DeviceIoControl.IoControlCode)); + Status = STATUS_NOT_IMPLEMENTED; + break; + } + } + + if (Status != STATUS_PENDING) { + Irp->IoStatus.Status = Status; + + TI_DbgPrint(DEBUG_IRP, ("Completing IRP at (0x%X).\n", Irp)); + + IoCompleteRequest(Irp, IO_NETWORK_INCREMENT); + } + + TI_DbgPrint(DEBUG_IRP, ("Leaving. Status = (0x%X).\n", Status)); + + return Status; +} + + +VOID TiUnload( + PDRIVER_OBJECT DriverObject) +/* + * FUNCTION: Unloads the driver + * ARGUMENTS: + * DriverObject = Pointer to driver object created by the system + */ +{ +#ifdef BDG + KIRQL OldIrql; + + KeAcquireSpinLock(&AddressFileListLock, &OldIrql); + if (!IsListEmpty(AddressFileList)) { + TI_DbgPrint(MIN_TRACE, ("Open address file objects exists.\n")); + } + KeReleaseSpinLock(&AddressFileListLock, OldIrql); +#endif + + /* Unregister loopback adapter */ + LoopUnregisterAdapter(NULL); + + /* Unregister protocol with NDIS */ +#ifdef _MSC_VER + LANUnregisterProtocol(); +#endif + + /* Shutdown transport level protocol subsystems */ + TCPShutdown(); + UDPShutdown(); + RawIPShutdown(); + DGShutdown(); + + /* Shutdown network level protocol subsystem */ + IPShutdown(); + + /* Free NDIS buffer descriptors */ + if (GlobalBufferPool) + NdisFreeBufferPool(GlobalBufferPool); + + /* Free NDIS packet descriptors */ + if (GlobalPacketPool) + NdisFreePacketPool(GlobalPacketPool); + + /* Release all device objects */ + + if (TCPDeviceObject) + IoDeleteDevice(TCPDeviceObject); + + if (UDPDeviceObject) + IoDeleteDevice(UDPDeviceObject); + + if (RawIPDeviceObject) + IoDeleteDevice(RawIPDeviceObject); + + if (IPDeviceObject) + IoDeleteDevice(IPDeviceObject); + + if (EntityList) + ExFreePool(EntityList); + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); +} + + +NTSTATUS +#ifndef _MSC_VER +STDCALL +#endif +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath) +/* + * FUNCTION: Main driver entry point + * ARGUMENTS: + * DriverObject = Pointer to a driver object for this driver + * RegistryPath = Registry node for configuration parameters + * RETURNS: + * Status of driver initialization + */ +{ + NTSTATUS Status; + UNICODE_STRING strDeviceName; + STRING strNdisDeviceName; + NDIS_STATUS NdisStatus; +#ifdef _MSC_VER + PLAN_ADAPTER Adapter; + NDIS_STRING DeviceName; +#endif + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + /* Create IP device object */ + RtlInitUnicodeString(&strDeviceName, DD_IP_DEVICE_NAME); + Status = IoCreateDevice(DriverObject, 0, &strDeviceName, + FILE_DEVICE_NETWORK, 0, FALSE, &IPDeviceObject); + if (!NT_SUCCESS(Status)) { + TI_DbgPrint(MIN_TRACE, ("Failed to create IP device object. Status (0x%X).\n", Status)); + return Status; + } + + /* Create RawIP device object */ + RtlInitUnicodeString(&strDeviceName, DD_RAWIP_DEVICE_NAME); + Status = IoCreateDevice(DriverObject, 0, &strDeviceName, + FILE_DEVICE_NETWORK, 0, FALSE, &RawIPDeviceObject); + if (!NT_SUCCESS(Status)) { + TI_DbgPrint(MIN_TRACE, ("Failed to create RawIP device object. Status (0x%X).\n", Status)); + TiUnload(DriverObject); + return Status; + } + + /* Create UDP device object */ + RtlInitUnicodeString(&strDeviceName, DD_UDP_DEVICE_NAME); + Status = IoCreateDevice(DriverObject, 0, &strDeviceName, + FILE_DEVICE_NETWORK, 0, FALSE, &UDPDeviceObject); + if (!NT_SUCCESS(Status)) { + TI_DbgPrint(MIN_TRACE, ("Failed to create UDP device object. Status (0x%X).\n", Status)); + TiUnload(DriverObject); + return Status; + } + + /* Create TCP device object */ + RtlInitUnicodeString(&strDeviceName, DD_TCP_DEVICE_NAME); + Status = IoCreateDevice(DriverObject, 0, &strDeviceName, + FILE_DEVICE_NETWORK, 0, FALSE, &TCPDeviceObject); + if (!NT_SUCCESS(Status)) { + TI_DbgPrint(MIN_TRACE, ("Failed to create TCP device object. Status (0x%X).\n", Status)); + TiUnload(DriverObject); + return Status; + } + + /* Allocate NDIS packet descriptors */ + NdisAllocatePacketPool(&NdisStatus, &GlobalPacketPool, 100, sizeof(PACKET_CONTEXT)); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TiUnload(DriverObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Allocate NDIS buffer descriptors */ + NdisAllocateBufferPool(&NdisStatus, &GlobalBufferPool, 100); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TiUnload(DriverObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Initialize address file list and protecting spin lock */ + InitializeListHead(&AddressFileListHead); + KeInitializeSpinLock(&AddressFileListLock); + + /* Initialize interface list and protecting spin lock */ + InitializeListHead(&InterfaceListHead); + KeInitializeSpinLock(&InterfaceListLock); + + /* Initialize network level protocol subsystem */ + IPStartup(DriverObject, RegistryPath); + + /* Initialize transport level protocol subsystems */ + DGStartup(); + RawIPStartup(); + UDPStartup(); + TCPStartup(); + + /* Register protocol with NDIS */ + RtlInitString(&strNdisDeviceName, IP_DEVICE_NAME); + Status = LANRegisterProtocol(&strNdisDeviceName); + if (!NT_SUCCESS(Status)) { + TiWriteErrorLog( + DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + TI_ERROR_DRIVERENTRY, + Status, + NULL, + 0, + NULL); + TiUnload(DriverObject); + return Status; + } + + /* Open loopback adapter */ + if (!NT_SUCCESS(LoopRegisterAdapter(NULL, NULL))) { + TI_DbgPrint(MIN_TRACE, ("Failed to create loopback adapter. Status (0x%X).\n", Status)); + TiUnload(DriverObject); + return STATUS_INSUFFICIENT_RESOURCES; + } +#if 1 +#ifdef _MSC_VER + /* Open underlying adapter(s) we are bound to */ + + /* FIXME: Get binding information from registry */ + + /* Put your own NDIS adapter device name here */ + +#if 0 + /* NT4 */ + NdisInitUnicodeString(&DeviceName, L"\\Device\\El90x1"); +#else + /* NT5 */ + NdisInitUnicodeString(&DeviceName, + L"\\Device\\{56388B49-67BB-4419-A3F4-28DF190B9149}"); +#endif + + NdisStatus = LANRegisterAdapter(&DeviceName, &Adapter); + if (!NT_SUCCESS(NdisStatus)) { + TI_DbgPrint(MIN_TRACE, ("Failed to intialize adapter. Status (0x%X).\n", Status)); + TiWriteErrorLog( + DriverObject, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + TI_ERROR_DRIVERENTRY, + NdisStatus, + NULL, + 0, + NULL); + TiUnload(DriverObject); + return STATUS_DEVICE_DOES_NOT_EXIST; + } +#endif +#endif + /* Setup network layer and transport layer entities */ + EntityList = ExAllocatePool(NonPagedPool, sizeof(TDIEntityID) * 2); + if (!NT_SUCCESS(Status)) { + TiUnload(DriverObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + EntityList[0].tei_entity = CL_NL_ENTITY; + EntityList[0].tei_instance = 0; + EntityList[1].tei_entity = CL_TL_ENTITY; + EntityList[1].tei_instance = 0; + EntityCount = 2; + + /* Use direct I/O */ + IPDeviceObject->Flags |= DO_DIRECT_IO; + RawIPDeviceObject->Flags |= DO_DIRECT_IO; + UDPDeviceObject->Flags |= DO_DIRECT_IO; + TCPDeviceObject->Flags |= DO_DIRECT_IO; + + /* Initialize the driver object with this driver's entry points */ + DriverObject->MajorFunction[IRP_MJ_CREATE] = TiDispatchOpenClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = TiDispatchOpenClose; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = TiDispatchOpenClose; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = TiDispatchInternal; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TiDispatch; + + DriverObject->DriverUnload = (PDRIVER_UNLOAD)TiUnload; + + return STATUS_SUCCESS; +} + + +VOID +#ifndef _MSC_VER +STDCALL +#endif +IPAddInterface( + DWORD Unknown0, + DWORD Unknown1, + DWORD Unknown2, + DWORD Unknown3, + DWORD Unknown4) +{ + UNIMPLEMENTED +} + + +VOID +#ifndef _MSC_VER +STDCALL +#endif +IPDelInterface( + DWORD Unknown0) +{ + UNIMPLEMENTED +} + + +VOID +#ifndef _MSC_VER +STDCALL +#endif +LookupRoute( + DWORD Unknown0, + DWORD Unknown1) +{ + UNIMPLEMENTED +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/pool.c b/reactos/drivers/net/tcpip/tcpip/pool.c new file mode 100644 index 00000000000..da7335247bf --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/pool.c @@ -0,0 +1,50 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS TCP/IP protocol driver + * FILE: tcpip/pool.c + * PURPOSE: Routines for controling pools + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/08-2000 Created + */ +#include +#include + + +PVOID PoolAllocateBuffer( + ULONG Size) +/* + * FUNCTION: Returns a buffer from the free buffer pool + * RETURNS: + * Pointer to buffer, NULL if there was not enough + * free resources + */ +{ + PVOID Buffer; + + /* FIXME: Get buffer from a free buffer pool with enough room */ + + Buffer = ExAllocatePool(NonPagedPool, Size); + + TI_DbgPrint(DEBUG_MEMORY, ("Allocated (%i) bytes at (0x%X).\n", Size, Buffer)); + + return Buffer; +} + + +VOID PoolFreeBuffer( + PVOID Buffer) +/* + * FUNCTION: Returns a buffer to the free buffer pool + * ARGUMENTS: + * Buffer = Buffer to return to free buffer pool + */ +{ + /* FIXME: Put buffer in free buffer pool */ + + TI_DbgPrint(DEBUG_MEMORY, ("Freeing buffer at (0x%X).\n", Buffer)); + + ExFreePool(Buffer); +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/tcpip/resource.rc b/reactos/drivers/net/tcpip/tcpip/resource.rc new file mode 100644 index 00000000000..0c634d20083 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/resource.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "TCP/IP protocol driver" +#define VER_INTERNALNAME_STR "TCPIP.SYS" +#define VER_ORIGINALFILENAME_STR "TCPIP.SYS" + +#include "common.ver" diff --git a/reactos/drivers/net/tcpip/tcpip/routines.c b/reactos/drivers/net/tcpip/tcpip/routines.c new file mode 100644 index 00000000000..223e76bb8d4 --- /dev/null +++ b/reactos/drivers/net/tcpip/tcpip/routines.c @@ -0,0 +1,403 @@ +/* + * 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 +#include +#include + + +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, + PUCHAR *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, Data, Size); + + if (Offset < *Size) { + ((ULONG_PTR)*Data) += Offset; + *Size -= Offset; + break; + } + + Offset -= *Size; + + NdisGetNextBuffer(Buffer, &Buffer); + } + + return Offset; +} + + +UINT CopyBufferToBufferChain( + PNDIS_BUFFER DstBuffer, + UINT DstOffset, + PUCHAR 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; + PUCHAR DstData; + + TI_DbgPrint(DEBUG_BUFFER, ("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; + (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, &DstData, &DstSize); + } + } + + return BytesCopied; +} + + +UINT CopyBufferChainToBuffer( + PUCHAR 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; + PUCHAR SrcData; + + TI_DbgPrint(DEBUG_BUFFER, ("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_BUFFER, ("Copying (%d) bytes from 0x%X to 0x%X\n", BytesToCopy, SrcData, DstData)); + + RtlCopyMemory((PVOID)DstData, (PVOID)SrcData, BytesToCopy); + BytesCopied += BytesToCopy; + (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, &SrcData, &SrcSize); + } + } + + return BytesCopied; +} + + +UINT CopyPacketToBuffer( + PUCHAR 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_BUFFER, ("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; + PUCHAR DstData, SrcData; + UINT DstSize, SrcSize; + UINT Count, Total; + + TI_DbgPrint(DEBUG_BUFFER, ("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, &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, &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, &SrcData, &SrcSize); + } + } + + return Total; +} + + +VOID FreeNdisPacket( + PNDIS_PACKET Packet) +/* + * FUNCTION: Frees an NDIS packet + * ARGUMENTS: + * Packet = Pointer to NDIS packet to be freed + */ +{ + PNDIS_BUFFER Buffer, NextBuffer; + + TI_DbgPrint(DEBUG_BUFFER, ("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); + NdisFreeBuffer(Buffer); + ExFreePool(Data); + } + + /* Finally free the NDIS packet discriptor */ + NdisFreePacket(Packet); +} + + +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_BUFFER, ("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) { +#if 0 + (ULONG_PTR)(NdisBuffer->MappedSystemVa) += Adjust; + (ULONG_PTR)(NdisBuffer->StartVa) += Adjust; + NdisBuffer->ByteCount -= Adjust; +#else + (ULONG_PTR)(NdisBuffer->MappedSystemVa) += Adjust; + NdisBuffer->ByteOffset += Adjust; + NdisBuffer->ByteCount -= Adjust; +#endif + } + + 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; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/transport/DIRS b/reactos/drivers/net/tcpip/transport/DIRS new file mode 100644 index 00000000000..901d4264f20 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/DIRS @@ -0,0 +1,5 @@ +DIRS= datagram \ + rawip \ + tcp \ + udp + diff --git a/reactos/drivers/net/tcpip/transport/datagram/Makefile b/reactos/drivers/net/tcpip/transport/datagram/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/datagram/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/transport/datagram/SOURCES b/reactos/drivers/net/tcpip/transport/datagram/SOURCES new file mode 100644 index 00000000000..aef69b1c025 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/datagram/SOURCES @@ -0,0 +1,13 @@ +TARGETNAME=datagram +TARGETPATH=..\..\objects +TARGETTYPE=LIBRARY + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib + +INCLUDES=..\..\include;$(BASEDIR)\INC;..\..\..\..\..\include\net + +SOURCES= datagram.c + +MSC_WARNING_LEVEL=/W3 /WX + diff --git a/reactos/drivers/net/tcpip/transport/datagram/datagram.c b/reactos/drivers/net/tcpip/transport/datagram/datagram.c new file mode 100644 index 00000000000..9f792feaf97 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/datagram/datagram.c @@ -0,0 +1,520 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + + +/* Pending request queue */ +LIST_ENTRY DGPendingListHead; +KSPIN_LOCK DGPendingListLock; +/* Work queue item for pending requests */ +WORK_QUEUE_ITEM DGWorkItem; + + +VOID DatagramWorker( + PVOID Context) +/* + * FUNCTION: Handles pending 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; + PNDIS_BUFFER NdisBuffer; + 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; + + /* Remove data buffer before releasing memory for packet buffers */ + NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL); + NdisUnchainBufferAtBack(Packet, &NdisBuffer); + FreeNdisPacket(Packet); + DereferenceObject(SendRequest->RemoteAddress); + PoolFreeBuffer(SendRequest); + + /* If there are pending send requests, shedule worker function */ + KeAcquireSpinLock(&DGPendingListLock, &OldIrql); + QueueWorkItem = (!IsListEmpty(&DGPendingListHead)); + KeReleaseSpinLock(&DGPendingListLock, OldIrql); + if (QueueWorkItem) + ExQueueWorkItem(&DGWorkItem, CriticalWorkQueue); + + /* Call completion routine for send request */ + (*Complete)(CompleteContext, 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); + 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 (;;) { + Status = SendRequest->Build(SendRequest, ADE->Address, LocalPort, &IPPacket); + if (!NT_SUCCESS(Status)) { + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + /* An error occurred, enqueue the send request again and return */ + InsertTailList(&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 */ + PC(IPPacket->NdisPacket)->Complete = SendDatagramComplete; + PC(IPPacket->NdisPacket)->Context = SendRequest; + if (IPSendDatagram(IPPacket, RCN) != STATUS_SUCCESS) + 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); + } + + PoolFreeBuffer(IPPacket); + + /* 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); + } 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 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); + PoolFreeBuffer(Current->RemoteAddress); + PoolFreeBuffer(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); + /* Remote address can be NULL if the caller wants to receive + packets sent from any address */ + if (Current->RemoteAddress) + PoolFreeBuffer(Current->RemoteAddress); + PoolFreeBuffer(Current); + } else { + TI_DbgPrint(MID_TRACE, ("Cannot find receive request.\n")); + } +} + + +NTSTATUS DGSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize, + DATAGRAM_BUILD_ROUTINE Build) +/* + * FUNCTION: Sends a 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 + * Build = Pointer to datagram build routine + * RETURNS: + * Status of operation + */ +{ + PADDRESS_FILE AddrFile; + KIRQL OldIrql; + NTSTATUS Status; + PDATAGRAM_SEND_REQUEST SendRequest = NULL; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + AddrFile = Request->Handle.AddressHandle; + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + + if (AF_IS_VALID(AddrFile)) { + SendRequest = PoolAllocateBuffer(sizeof(DATAGRAM_SEND_REQUEST)); + if (SendRequest) { + /* Initialize a send request */ + Status = AddrGetAddress(ConnInfo->RemoteAddress, + &SendRequest->RemoteAddress, &SendRequest->RemotePort, + &AddrFile->AddrCache); + if (NT_SUCCESS(Status)) { + SendRequest->Buffer = Buffer; + SendRequest->BufferSize = DataSize; + SendRequest->Complete = Request->RequestNotifyObject; + SendRequest->Context = Request->RequestContext; + SendRequest->Build = Build; + + if (AF_IS_BUSY(AddrFile)) { + /* Queue send request on the transmit queue */ + InsertTailList(&AddrFile->TransmitQueue, &SendRequest->ListEntry); + + /* Reference address file and set pending send request flag */ + AddrFile->RefCount++; + AF_SET_PENDING(AddrFile, AFF_SEND); + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + TI_DbgPrint(MAX_TRACE, ("Leaving (queued).\n")); + } else { + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + /* Send the datagram */ + DGSend(AddrFile, SendRequest); + + TI_DbgPrint(MAX_TRACE, ("Leaving (pending).\n")); + } + return STATUS_PENDING; + } + } 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 = PoolAllocateBuffer(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, + &AddrFile->AddrCache); + if (!NT_SUCCESS(Status)) { + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + PoolFreeBuffer(ReceiveRequest); + return Status; + } + } else { + ReceiveRequest->RemotePort = 0; + ReceiveRequest->RemoteAddress = NULL; + } + 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; +} + + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/transport/rawip/Makefile b/reactos/drivers/net/tcpip/transport/rawip/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/rawip/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/transport/rawip/SOURCES b/reactos/drivers/net/tcpip/transport/rawip/SOURCES new file mode 100644 index 00000000000..1568428d329 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/rawip/SOURCES @@ -0,0 +1,13 @@ +TARGETNAME=rawip +TARGETPATH=..\..\objects +TARGETTYPE=LIBRARY + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib + +INCLUDES=..\..\include;$(BASEDIR)\INC;..\..\..\..\..\include\net + +SOURCES= rawip.c + +MSC_WARNING_LEVEL=/W3 /WX + diff --git a/reactos/drivers/net/tcpip/transport/rawip/rawip.c b/reactos/drivers/net/tcpip/transport/rawip/rawip.c new file mode 100644 index 00000000000..1aa4a65ee60 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/rawip/rawip.c @@ -0,0 +1,113 @@ +/* + * 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 +#include +#include +#include + + +BOOLEAN RawIPInitialized = FALSE; + + +NTSTATUS BuildRawIPPacket( + PVOID Context, + PIP_ADDRESS LocalAddress, + USHORT LocalPort, + PIP_PACKET *IPPacket) +/* + * 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 + */ +{ + PIP_PACKET Packet; + NDIS_STATUS NdisStatus; + PDATAGRAM_SEND_REQUEST SendRequest = (PDATAGRAM_SEND_REQUEST)Context; + + /* Prepare packet */ + Packet = PoolAllocateBuffer(sizeof(IP_PACKET)); + if (!Packet) + return STATUS_INSUFFICIENT_RESOURCES; + + RtlZeroMemory(Packet, sizeof(IP_PACKET)); + Packet->RefCount = 1; + Packet->TotalSize = SendRequest->BufferSize; + + /* Allocate NDIS packet */ + NdisAllocatePacket(&NdisStatus, &Packet->NdisPacket, GlobalPacketPool); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PoolFreeBuffer(Packet); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Chain buffer to packet */ + NdisChainBufferAtFront(Packet->NdisPacket, SendRequest->Buffer); + + *IPPacket = Packet; + + return STATUS_SUCCESS; +} + + +NTSTATUS RawIPSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize) +/* + * 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 + */ +{ + return DGSendDatagram(Request, ConnInfo, + Buffer, DataSize, BuildRawIPPacket); +} + + +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/net/tcpip/transport/tcp/Makefile b/reactos/drivers/net/tcpip/transport/tcp/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/tcp/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/transport/tcp/SOURCES b/reactos/drivers/net/tcpip/transport/tcp/SOURCES new file mode 100644 index 00000000000..9c3e674e542 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/tcp/SOURCES @@ -0,0 +1,13 @@ +TARGETNAME=tcp +TARGETPATH=..\..\objects +TARGETTYPE=LIBRARY + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib + +INCLUDES=..\..\include;$(BASEDIR)\INC;..\..\..\..\..\include\net + +SOURCES= tcp.c + +MSC_WARNING_LEVEL=/W3 /WX + diff --git a/reactos/drivers/net/tcpip/transport/tcp/tcp.c b/reactos/drivers/net/tcpip/transport/tcp/tcp.c new file mode 100644 index 00000000000..11a6ca27e7a --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/tcp/tcp.c @@ -0,0 +1,44 @@ +/* + * 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 + + +BOOLEAN TCPInitialized = FALSE; + + +NTSTATUS TCPStartup( + VOID) +/* + * FUNCTION: Initializes the TCP subsystem + * RETURNS: + * Status of operation + */ +{ + TCPInitialized = TRUE; + + return STATUS_SUCCESS; +} + + +NTSTATUS TCPShutdown( + VOID) +/* + * FUNCTION: Shuts down the TCP subsystem + * RETURNS: + * Status of operation + */ +{ + if (!TCPInitialized) + return STATUS_SUCCESS; + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/drivers/net/tcpip/transport/udp/Makefile b/reactos/drivers/net/tcpip/transport/udp/Makefile new file mode 100644 index 00000000000..9c985f57bc6 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/udp/Makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/reactos/drivers/net/tcpip/transport/udp/SOURCES b/reactos/drivers/net/tcpip/transport/udp/SOURCES new file mode 100644 index 00000000000..ed16c826be1 --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/udp/SOURCES @@ -0,0 +1,12 @@ +TARGETNAME=udp +TARGETPATH=..\..\objects +TARGETTYPE=LIBRARY + +TARGETLIBS=$(DDK_LIB_PATH)\tdi.lib \ + $(DDK_LIB_PATH)\ndis.lib + +INCLUDES=..\..\include;$(BASEDIR)\INC;..\..\..\..\..\include\net + +SOURCES= udp.c + +MSC_WARNING_LEVEL=/W3 /WX diff --git a/reactos/drivers/net/tcpip/transport/udp/udp.c b/reactos/drivers/net/tcpip/transport/udp/udp.c new file mode 100644 index 00000000000..da6574646ce --- /dev/null +++ b/reactos/drivers/net/tcpip/transport/udp/udp.c @@ -0,0 +1,437 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + + +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 = PoolAllocateBuffer(BufferSize); + + if (!Header) { + TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet headers.\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* 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)); + PoolFreeBuffer(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, + PIP_PACKET *IPPacket) +/* + * 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; + PIP_PACKET Packet; + NDIS_STATUS NdisStatus; + PDATAGRAM_SEND_REQUEST SendRequest = (PDATAGRAM_SEND_REQUEST)Context; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + /* Prepare packet */ + Packet = PoolAllocateBuffer(sizeof(IP_PACKET)); + if (!Packet) { + TI_DbgPrint(MIN_TRACE, ("Cannot allocate memory for packet.\n")); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(Packet, sizeof(IP_PACKET)); + Packet->RefCount = 1; + Packet->TotalSize = sizeof(IPv4_HEADER) + + sizeof(UDP_HEADER) + + SendRequest->BufferSize; + + /* Allocate NDIS packet */ + NdisAllocatePacket(&NdisStatus, &Packet->NdisPacket, GlobalPacketPool); + if (NdisStatus != NDIS_STATUS_SUCCESS) { + TI_DbgPrint(MIN_TRACE, ("Cannot allocate NDIS packet. NdisStatus = (0x%X)\n", NdisStatus)); + PoolFreeBuffer(Packet); + return STATUS_INSUFFICIENT_RESOURCES; + } + + 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)); + NdisFreePacket(Packet->NdisPacket); + PoolFreeBuffer(Packet); + return Status; + } + + /* Chain data after header */ + NdisChainBufferAtBack(Packet->NdisPacket, SendRequest->Buffer); + + *IPPacket = Packet; + + return STATUS_SUCCESS; +} + + +VOID DeliverUDPData( + PADDRESS_FILE AddrFile, + PIP_ADDRESS Address, + PIP_PACKET IPPacket, + UINT DataSize) +/* + * FUNCTION: Delivers UDP 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 + * 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; + + TI_DbgPrint(MAX_TRACE, ("Called.\n")); + + KeAcquireSpinLock(&AddrFile->Lock, &OldIrql); + + if (!IsListEmpty(&AddrFile->ReceiveQueue)) { + PLIST_ENTRY CurrentEntry; + PDATAGRAM_RECEIVE_REQUEST Current; + BOOLEAN Found; + + /* 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 (!Current->RemoteAddress) + Found = TRUE; + else 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. Also a 'best fit' strategy could be used. */ + + /* Remove the request from the queue */ + RemoveEntryList(&Current->ListEntry); + AddrFile->RefCount--; + break; + } + CurrentEntry = CurrentEntry->Flink; + } + + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + if (Found) { + /* Copy the data into buffer provided by the user */ + CopyBufferToBufferChain(Current->Buffer, + 0, + IPPacket->Data, + DataSize); + + /* Complete the receive request */ + (*Current->Complete)(Current->Context, STATUS_SUCCESS, DataSize); + + /* Finally free the receive request */ + if (Current->RemoteAddress) + PoolFreeBuffer(Current->RemoteAddress); + PoolFreeBuffer(Current); + } + } else { + KeReleaseSpinLock(&AddrFile->Lock, OldIrql); + + /* FIXME: Call event handler */ + TI_DbgPrint(MAX_TRACE, ("Calling receive event handler.\n")); + } + + TI_DbgPrint(MAX_TRACE, ("Leaving.\n")); +} + + +NTSTATUS UDPSendDatagram( + PTDI_REQUEST Request, + PTDI_CONNECTION_INFORMATION ConnInfo, + PNDIS_BUFFER Buffer, + ULONG DataSize) +/* + * 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 + */ +{ + return DGSendDatagram(Request, + ConnInfo, + Buffer, + DataSize, + BuildUDPPacket); +} + + +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; + } + + 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 */ + (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 { + DeliverUDPData(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 + */ +{ + RtlZeroMemory(&UDPStats, sizeof(UDP_STATISTICS)); + + /* 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 */ -- 2.17.1