/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS TCP/IP protocol driver * FILE: network/transmit.c * PURPOSE: Internet Protocol transmit routines * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) * REVISIONS: * CSH 01/08-2000 Created */ #include "precomp.h" BOOLEAN PrepareNextFragment(PIPFRAGMENT_CONTEXT IFC); NTSTATUS IPSendFragment(PNDIS_PACKET NdisPacket, PNEIGHBOR_CACHE_ENTRY NCE, PIPFRAGMENT_CONTEXT IFC); 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 */ { PIPFRAGMENT_CONTEXT IFC = (PIPFRAGMENT_CONTEXT)Context; TI_DbgPrint (MAX_TRACE, ("Called. Context (0x%X) NdisPacket (0x%X) NdisStatus (0x%X)\n", Context, NdisPacket, NdisStatus)); IFC->Status = NdisStatus; KeSetEvent(&IFC->Event, 0, FALSE); } NTSTATUS IPSendFragment( PNDIS_PACKET NdisPacket, PNEIGHBOR_CACHE_ENTRY NCE, PIPFRAGMENT_CONTEXT IFC) /* * FUNCTION: Sends an IP datagram fragment to a neighbor * ARGUMENTS: * NdisPacket = Pointer to an NDIS packet containing fragment * NCE = Pointer to NCE for first hop to destination * RETURNS: * Status of operation * NOTES: * Lowest level IP send routine */ { TI_DbgPrint(MAX_TRACE, ("Called. NdisPacket (0x%X) NCE (0x%X).\n", NdisPacket, NCE)); TI_DbgPrint(MAX_TRACE, ("NCE->State = %d.\n", NCE->State)); return NBQueuePacket(NCE, NdisPacket, IPSendComplete, IFC); } BOOLEAN PrepareNextFragment( PIPFRAGMENT_CONTEXT IFC) /* * FUNCTION: Prepares the next fragment of an IP datagram for transmission * ARGUMENTS: * IFC = Pointer to IP fragment context * RETURNS: * TRUE if a fragment was prepared for transmission, FALSE if * there are no more fragments to send */ { UINT MaxData; UINT DataSize; PIPv4_HEADER Header; BOOLEAN MoreFragments; USHORT FragOfs; TI_DbgPrint(MAX_TRACE, ("Called. IFC (0x%X)\n", IFC)); if (IFC->BytesLeft > 0) { TI_DbgPrint(MAX_TRACE, ("Preparing 1 fragment.\n")); MaxData = IFC->PathMTU - IFC->HeaderSize; /* Make fragment a multiplum of 64bit */ MaxData -= MaxData % 8; if (IFC->BytesLeft > MaxData) { DataSize = MaxData; MoreFragments = TRUE; } else { DataSize = IFC->BytesLeft; MoreFragments = FALSE; } TI_DbgPrint(MID_TRACE,("Copying data from %x to %x (%d)\n", IFC->DatagramData, IFC->Data, DataSize)); RtlCopyMemory(IFC->Data, IFC->DatagramData, DataSize); // SAFE /* Fragment offset is in 8 byte blocks */ FragOfs = (USHORT)(IFC->Position / 8); if (MoreFragments) FragOfs |= IPv4_MF_MASK; else FragOfs &= ~IPv4_MF_MASK; Header = IFC->Header; Header->FlagsFragOfs = WH2N(FragOfs); Header->TotalLength = WH2N((USHORT)(DataSize + IFC->HeaderSize)); /* FIXME: Handle options */ /* Calculate checksum of IP header */ Header->Checksum = 0; Header->Checksum = (USHORT)IPv4Checksum(Header, IFC->HeaderSize, 0); TI_DbgPrint(MID_TRACE,("IP Check: %x\n", Header->Checksum)); /* Update pointers */ IFC->DatagramData = (PVOID)((ULONG_PTR)IFC->DatagramData + DataSize); IFC->Position += DataSize; IFC->BytesLeft -= DataSize; return TRUE; } else { TI_DbgPrint(MAX_TRACE, ("No more fragments.\n")); return FALSE; } } NTSTATUS SendFragments( PIP_PACKET IPPacket, PNEIGHBOR_CACHE_ENTRY NCE, UINT PathMTU) /* * FUNCTION: Fragments and sends the first fragment of an IP datagram * ARGUMENTS: * IPPacket = Pointer to an IP packet * NCE = Pointer to NCE for first hop to destination * PathMTU = Size of Maximum Transmission Unit of path * RETURNS: * Status of operation * NOTES: * IP datagram is larger than PathMTU when this is called */ { PIPFRAGMENT_CONTEXT IFC; NDIS_STATUS NdisStatus; PVOID Data; UINT BufferSize = PathMTU, InSize; PCHAR InData; TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) NCE (0x%X) PathMTU (%d).\n", IPPacket, NCE, PathMTU)); /* Make a smaller buffer if we will only send one fragment */ GetDataPtr( IPPacket->NdisPacket, IPPacket->Position, &InData, &InSize ); if( InSize < BufferSize ) BufferSize = InSize; TI_DbgPrint(MAX_TRACE, ("Fragment buffer is %d bytes\n", BufferSize)); IFC = ExAllocatePoolWithTag(NonPagedPool, sizeof(IPFRAGMENT_CONTEXT), IFC_TAG); if (IFC == NULL) { IPPacket->Free(IPPacket); return STATUS_INSUFFICIENT_RESOURCES; } /* Allocate NDIS packet */ NdisStatus = AllocatePacketWithBuffer ( &IFC->NdisPacket, NULL, BufferSize ); if( !NT_SUCCESS(NdisStatus) ) { IPPacket->Free(IPPacket); ExFreePoolWithTag( IFC, IFC_TAG ); return NdisStatus; } GetDataPtr( IFC->NdisPacket, 0, (PCHAR *)&Data, &InSize ); IFC->Header = ((PCHAR)Data); IFC->Datagram = IPPacket->NdisPacket; IFC->DatagramData = ((PCHAR)IPPacket->Header) + IPPacket->HeaderSize; 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); KeInitializeEvent(&IFC->Event, NotificationEvent, FALSE); TI_DbgPrint(MID_TRACE,("Copying header from %x to %x (%d)\n", IPPacket->Header, IFC->Header, IPPacket->HeaderSize)); RtlCopyMemory( IFC->Header, IPPacket->Header, IPPacket->HeaderSize ); while (PrepareNextFragment(IFC)) { NdisStatus = IPSendFragment(IFC->NdisPacket, NCE, IFC); if (NT_SUCCESS(NdisStatus)) { KeWaitForSingleObject(&IFC->Event, Executive, KernelMode, FALSE, NULL); NdisStatus = IFC->Status; } if (!NT_SUCCESS(NdisStatus)) break; } FreeNdisPacket(IFC->NdisPacket); ExFreePoolWithTag(IFC, IFC_TAG); IPPacket->Free(IPPacket); return NdisStatus; } NTSTATUS IPSendDatagram(PIP_PACKET IPPacket, PNEIGHBOR_CACHE_ENTRY NCE) /* * 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) */ { TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) NCE (0x%X)\n", IPPacket, NCE)); DISPLAY_IP_PACKET(IPPacket); /* Fetch path MTU now, because it may change */ TI_DbgPrint(MID_TRACE,("PathMTU: %d\n", NCE->Interface->MTU)); return SendFragments(IPPacket, NCE, NCE->Interface->MTU); } /* EOF */