mirror of
https://github.com/reactos/reactos.git
synced 2025-01-05 22:12:46 +00:00
508 lines
13 KiB
C
508 lines
13 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS TCP/IP protocol driver
|
|
* FILE: network/ip.c
|
|
* PURPOSE: Internet Protocol module
|
|
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
* REVISIONS:
|
|
* CSH 01/08-2000 Created
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#define __LWIP_INET_H__
|
|
#include "lwip/netifapi.h"
|
|
|
|
|
|
LIST_ENTRY InterfaceListHead;
|
|
KSPIN_LOCK InterfaceListLock;
|
|
LIST_ENTRY NetTableListHead;
|
|
KSPIN_LOCK NetTableListLock;
|
|
BOOLEAN IPInitialized = FALSE;
|
|
BOOLEAN IpWorkItemQueued = FALSE;
|
|
/* Work around calling timer at Dpc level */
|
|
|
|
IP_PROTOCOL_HANDLER ProtocolTable[IP_PROTOCOL_TABLE_SIZE];
|
|
|
|
ULONG IpTimerExpirations;
|
|
|
|
VOID
|
|
TCPRegisterInterface(PIP_INTERFACE IF);
|
|
|
|
VOID
|
|
TCPUnregisterInterface(PIP_INTERFACE IF);
|
|
|
|
VOID DeinitializePacket(
|
|
PVOID Object)
|
|
/*
|
|
* FUNCTION: Frees buffers attached to the packet
|
|
* ARGUMENTS:
|
|
* Object = Pointer to an IP packet structure
|
|
*/
|
|
{
|
|
PIP_PACKET IPPacket = Object;
|
|
|
|
TI_DbgPrint(MAX_TRACE, ("Freeing object: 0x%p\n", Object));
|
|
|
|
/* Detect double free */
|
|
ASSERT(IPPacket->Type != 0xFF);
|
|
IPPacket->Type = 0xFF;
|
|
|
|
/* Check if there's a packet to free */
|
|
if (IPPacket->NdisPacket != NULL)
|
|
{
|
|
if (IPPacket->ReturnPacket)
|
|
{
|
|
/* Return the packet to the miniport driver */
|
|
TI_DbgPrint(MAX_TRACE, ("Returning packet 0x%p\n",
|
|
IPPacket->NdisPacket));
|
|
NdisReturnPackets(&IPPacket->NdisPacket, 1);
|
|
}
|
|
else
|
|
{
|
|
/* Free it the conventional way */
|
|
TI_DbgPrint(MAX_TRACE, ("Freeing packet 0x%p\n",
|
|
IPPacket->NdisPacket));
|
|
FreeNdisPacket(IPPacket->NdisPacket);
|
|
}
|
|
}
|
|
|
|
/* Check if we have a pool-allocated header */
|
|
if (!IPPacket->MappedHeader && IPPacket->Header)
|
|
{
|
|
/* Free it */
|
|
TI_DbgPrint(MAX_TRACE, ("Freeing header: 0x%p\n",
|
|
IPPacket->Header));
|
|
ExFreePoolWithTag(IPPacket->Header,
|
|
PACKET_BUFFER_TAG);
|
|
}
|
|
}
|
|
|
|
VOID FreeIF(
|
|
PVOID Object)
|
|
/*
|
|
* FUNCTION: Frees an interface object
|
|
* ARGUMENTS:
|
|
* Object = Pointer to an interface structure
|
|
*/
|
|
{
|
|
ExFreePoolWithTag(Object, IP_INTERFACE_TAG);
|
|
}
|
|
|
|
PIP_PACKET IPInitializePacket(
|
|
PIP_PACKET IPPacket,
|
|
ULONG Type)
|
|
/*
|
|
* FUNCTION: Creates an IP packet object
|
|
* ARGUMENTS:
|
|
* Type = Type of IP packet
|
|
* RETURNS:
|
|
* Pointer to the created IP packet. NULL if there was not enough free resources.
|
|
*/
|
|
{
|
|
RtlZeroMemory(IPPacket, sizeof(IP_PACKET));
|
|
|
|
IPPacket->Free = DeinitializePacket;
|
|
IPPacket->Type = Type;
|
|
|
|
return IPPacket;
|
|
}
|
|
|
|
|
|
VOID NTAPI IPTimeoutDpcFn(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 maintenance jobs
|
|
*/
|
|
{
|
|
IpTimerExpirations++;
|
|
|
|
if ((IpTimerExpirations % 10) == 0)
|
|
{
|
|
LogActiveObjects();
|
|
}
|
|
|
|
/* Check if datagram fragments have taken too long to assemble */
|
|
IPDatagramReassemblyTimeout();
|
|
|
|
/* Clean possible outdated cached neighbor addresses */
|
|
NBTimeout();
|
|
}
|
|
|
|
|
|
VOID IPDispatchProtocol(
|
|
PIP_INTERFACE Interface,
|
|
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;
|
|
IP_ADDRESS SrcAddress;
|
|
|
|
switch (IPPacket->Type) {
|
|
case IP_ADDRESS_V4:
|
|
Protocol = ((PIPv4_HEADER)(IPPacket->Header))->Protocol;
|
|
AddrInitIPv4(&SrcAddress, ((PIPv4_HEADER)(IPPacket->Header))->SrcAddr);
|
|
break;
|
|
case IP_ADDRESS_V6:
|
|
/* FIXME: IPv6 adresses not supported */
|
|
TI_DbgPrint(MIN_TRACE, ("IPv6 datagram discarded.\n"));
|
|
return;
|
|
default:
|
|
TI_DbgPrint(MIN_TRACE, ("Unrecognized datagram discarded.\n"));
|
|
return;
|
|
}
|
|
|
|
NBResetNeighborTimeout(&SrcAddress);
|
|
|
|
if (Protocol < IP_PROTOCOL_TABLE_SIZE)
|
|
{
|
|
/* Call the appropriate protocol handler */
|
|
(*ProtocolTable[Protocol])(Interface, 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 DBG
|
|
if (BindInfo->Address) {
|
|
PUCHAR A = BindInfo->Address;
|
|
TI_DbgPrint(DEBUG_IP, ("Interface address (%02X %02X %02X %02X %02X %02X).\n",
|
|
A[0], A[1], A[2], A[3], A[4], A[5]));
|
|
}
|
|
#endif
|
|
|
|
IF = ExAllocatePoolWithTag(NonPagedPool, sizeof(IP_INTERFACE),
|
|
IP_INTERFACE_TAG);
|
|
if (!IF) {
|
|
TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(IF, sizeof(IP_INTERFACE));
|
|
|
|
IF->Free = FreeIF;
|
|
IF->Context = BindInfo->Context;
|
|
IF->HeaderSize = BindInfo->HeaderSize;
|
|
IF->MinFrameSize = BindInfo->MinFrameSize;
|
|
IF->Address = BindInfo->Address;
|
|
IF->AddressLength = BindInfo->AddressLength;
|
|
IF->Transmit = BindInfo->Transmit;
|
|
|
|
IF->Unicast.Type = IP_ADDRESS_V4;
|
|
IF->PointToPoint.Type = IP_ADDRESS_V4;
|
|
IF->Netmask.Type = IP_ADDRESS_V4;
|
|
IF->Broadcast.Type = IP_ADDRESS_V4;
|
|
|
|
TcpipInitializeSpinLock(&IF->Lock);
|
|
|
|
IF->TCPContext = ExAllocatePool
|
|
( NonPagedPool, sizeof(struct netif));
|
|
if (!IF->TCPContext) {
|
|
ExFreePoolWithTag(IF, IP_INTERFACE_TAG);
|
|
return NULL;
|
|
}
|
|
|
|
TCPRegisterInterface(IF);
|
|
|
|
#ifdef __NTDRIVER__
|
|
InsertTDIInterfaceEntity( IF );
|
|
#endif
|
|
|
|
return IF;
|
|
}
|
|
|
|
|
|
VOID IPDestroyInterface(
|
|
PIP_INTERFACE IF)
|
|
/*
|
|
* FUNCTION: Destroys an IP interface
|
|
* ARGUMENTS:
|
|
* IF = Pointer to interface to destroy
|
|
*/
|
|
{
|
|
TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
|
|
|
|
#ifdef __NTDRIVER__
|
|
RemoveTDIInterfaceEntity( IF );
|
|
#endif
|
|
|
|
TCPUnregisterInterface(IF);
|
|
|
|
ExFreePool(IF->TCPContext);
|
|
ExFreePoolWithTag(IF, IP_INTERFACE_TAG);
|
|
}
|
|
|
|
VOID IPAddInterfaceRoute( PIP_INTERFACE IF ) {
|
|
PNEIGHBOR_CACHE_ENTRY NCE;
|
|
IP_ADDRESS NetworkAddress;
|
|
|
|
/* Add a permanent neighbor for this NTE */
|
|
NCE = NBAddNeighbor(IF, &IF->Unicast,
|
|
IF->Address, IF->AddressLength,
|
|
NUD_PERMANENT, 0);
|
|
if (!NCE) {
|
|
TI_DbgPrint(MIN_TRACE, ("Could not create NCE.\n"));
|
|
return;
|
|
}
|
|
|
|
AddrWidenAddress( &NetworkAddress, &IF->Unicast, &IF->Netmask );
|
|
|
|
if (!RouterAddRoute(&NetworkAddress, &IF->Netmask, NCE, 1)) {
|
|
TI_DbgPrint(MIN_TRACE, ("Could not add route due to insufficient resources.\n"));
|
|
}
|
|
|
|
/* Send a gratuitous ARP packet to update the route caches of
|
|
* other computers */
|
|
if (IF != Loopback)
|
|
ARPTransmit(NULL, NULL, IF);
|
|
|
|
TCPUpdateInterfaceIPInformation(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;
|
|
UINT ChosenIndex = 0;
|
|
BOOLEAN IndexHasBeenChosen;
|
|
IF_LIST_ITER(Interface);
|
|
|
|
TI_DbgPrint(MID_TRACE, ("Called. IF (0x%X).\n", IF));
|
|
|
|
TcpipAcquireSpinLock(&IF->Lock, &OldIrql);
|
|
|
|
/* Choose an index */
|
|
do {
|
|
IndexHasBeenChosen = TRUE;
|
|
ForEachInterface(Interface) {
|
|
if( Interface->Index == ChosenIndex ) {
|
|
ChosenIndex++;
|
|
IndexHasBeenChosen = FALSE;
|
|
}
|
|
} EndFor(Interface);
|
|
} while( !IndexHasBeenChosen );
|
|
|
|
IF->Index = ChosenIndex;
|
|
|
|
/* Add interface to the global interface list */
|
|
TcpipInterlockedInsertTailList(&InterfaceListHead,
|
|
&IF->ListEntry,
|
|
&InterfaceListLock);
|
|
|
|
TcpipReleaseSpinLock(&IF->Lock, OldIrql);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID IPRemoveInterfaceRoute( PIP_INTERFACE IF ) {
|
|
PNEIGHBOR_CACHE_ENTRY NCE;
|
|
IP_ADDRESS GeneralRoute;
|
|
|
|
NCE = NBLocateNeighbor(&IF->Unicast, IF);
|
|
if (NCE)
|
|
{
|
|
TI_DbgPrint(DEBUG_IP,("Removing interface Addr %s\n", A2S(&IF->Unicast)));
|
|
TI_DbgPrint(DEBUG_IP,(" Mask %s\n", A2S(&IF->Netmask)));
|
|
|
|
AddrWidenAddress(&GeneralRoute,&IF->Unicast,&IF->Netmask);
|
|
|
|
RouterRemoveRoute(&GeneralRoute, &IF->Unicast);
|
|
|
|
NBRemoveNeighbor(NCE);
|
|
}
|
|
}
|
|
|
|
VOID IPUnregisterInterface(
|
|
PIP_INTERFACE IF)
|
|
/*
|
|
* FUNCTION: Unregisters an IP interface with IP layer
|
|
* ARGUMENTS:
|
|
* IF = Pointer to interface to unregister
|
|
*/
|
|
{
|
|
KIRQL OldIrql3;
|
|
|
|
TI_DbgPrint(DEBUG_IP, ("Called. IF (0x%X).\n", IF));
|
|
|
|
IPRemoveInterfaceRoute( IF );
|
|
|
|
TcpipAcquireSpinLock(&InterfaceListLock, &OldIrql3);
|
|
RemoveEntryList(&IF->ListEntry);
|
|
TcpipReleaseSpinLock(&InterfaceListLock, OldIrql3);
|
|
}
|
|
|
|
|
|
VOID DefaultProtocolHandler(
|
|
PIP_INTERFACE Interface,
|
|
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, ("[IF %x] Packet of unknown Internet protocol "
|
|
"discarded.\n", Interface));
|
|
|
|
Interface->Stats.InDiscardedUnknownProto++;
|
|
}
|
|
|
|
|
|
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
|
|
*/
|
|
{
|
|
if (ProtocolNumber >= IP_PROTOCOL_TABLE_SIZE) {
|
|
TI_DbgPrint(MIN_TRACE, ("Protocol number is out of range (%d).\n", ProtocolNumber));
|
|
return;
|
|
}
|
|
|
|
ProtocolTable[ProtocolNumber] = Handler ? Handler : DefaultProtocolHandler;
|
|
}
|
|
|
|
|
|
NTSTATUS IPStartup(PUNICODE_STRING RegistryPath)
|
|
/*
|
|
* FUNCTION: Initializes the IP subsystem
|
|
* ARGUMENTS:
|
|
* RegistryPath = Our registry node for configuration parameters
|
|
* RETURNS:
|
|
* Status of operation
|
|
*/
|
|
{
|
|
UINT i;
|
|
|
|
TI_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Initialize lookaside lists */
|
|
ExInitializeNPagedLookasideList(
|
|
&IPDRList, /* Lookaside list */
|
|
NULL, /* Allocate routine */
|
|
NULL, /* Free routine */
|
|
0, /* Flags */
|
|
sizeof(IPDATAGRAM_REASSEMBLY), /* Size of each entry */
|
|
DATAGRAM_REASSEMBLY_TAG, /* Tag */
|
|
0); /* Depth */
|
|
|
|
ExInitializeNPagedLookasideList(
|
|
&IPFragmentList, /* Lookaside list */
|
|
NULL, /* Allocate routine */
|
|
NULL, /* Free routine */
|
|
0, /* Flags */
|
|
sizeof(IP_FRAGMENT), /* Size of each entry */
|
|
DATAGRAM_FRAGMENT_TAG, /* Tag */
|
|
0); /* Depth */
|
|
|
|
ExInitializeNPagedLookasideList(
|
|
&IPHoleList, /* Lookaside list */
|
|
NULL, /* Allocate routine */
|
|
NULL, /* Free routine */
|
|
0, /* Flags */
|
|
sizeof(IPDATAGRAM_HOLE), /* Size of each entry */
|
|
DATAGRAM_HOLE_TAG, /* Tag */
|
|
0); /* Depth */
|
|
|
|
/* Start routing subsystem */
|
|
RouterStartup();
|
|
|
|
/* 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);
|
|
|
|
/* Initialize NTE list and protecting lock */
|
|
InitializeListHead(&NetTableListHead);
|
|
TcpipInitializeSpinLock(&NetTableListLock);
|
|
|
|
/* Initialize reassembly list and protecting lock */
|
|
InitializeListHead(&ReassemblyListHead);
|
|
TcpipInitializeSpinLock(&ReassemblyListLock);
|
|
|
|
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;
|
|
|
|
/* Shutdown neighbor cache subsystem */
|
|
NBShutdown();
|
|
|
|
/* Shutdown routing subsystem */
|
|
RouterShutdown();
|
|
|
|
IPFreeReassemblyList();
|
|
|
|
/* Destroy lookaside lists */
|
|
ExDeleteNPagedLookasideList(&IPHoleList);
|
|
ExDeleteNPagedLookasideList(&IPDRList);
|
|
ExDeleteNPagedLookasideList(&IPFragmentList);
|
|
|
|
IPInitialized = FALSE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* EOF */
|