2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/transmit.c
5 * PURPOSE: Internet Protocol transmit routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 01/08-2000 Created
19 BOOLEAN PrepareNextFragment(
20 PIPFRAGMENT_CONTEXT IFC)
22 * FUNCTION: Prepares the next fragment of an IP datagram for transmission
24 * IFC = Pointer to IP fragment context
26 * TRUE if a fragment was prepared for transmission, FALSE if
27 * there are no more fragments to send
33 BOOLEAN MoreFragments;
36 TI_DbgPrint(MAX_TRACE, ("Called. IFC (0x%X)\n", IFC));
38 if (IFC->BytesLeft != 0) {
40 TI_DbgPrint(MAX_TRACE, ("Preparing 1 fragment.\n"));
42 MaxData = IFC->PathMTU - IFC->HeaderSize;
43 /* Make fragment a multiplum of 64bit */
44 MaxData -= MaxData % 8;
45 if (IFC->BytesLeft > MaxData) {
49 DataSize = IFC->BytesLeft;
50 MoreFragments = FALSE;
53 RtlCopyMemory(IFC->Data, IFC->DatagramData, DataSize);
55 FragOfs = (USHORT)IFC->Position; // Swap?
57 FragOfs |= IPv4_MF_MASK;
59 FragOfs &= ~IPv4_MF_MASK;
62 Header->FlagsFragOfs = FragOfs;
64 /* FIXME: Handle options */
66 /* Calculate checksum of IP header */
68 Header->Checksum = (USHORT)IPv4Checksum(Header, IFC->HeaderSize, 0);
71 (ULONG_PTR)IFC->DatagramData += DataSize;
72 IFC->Position += DataSize;
73 IFC->BytesLeft -= DataSize;
77 TI_DbgPrint(MAX_TRACE, ("No more fragments.\n"));
83 NTSTATUS SendFragments(
85 PNEIGHBOR_CACHE_ENTRY NCE,
88 * FUNCTION: Fragments and sends the first fragment of an IP datagram
90 * IPPacket = Pointer to an IP packet
91 * NCE = Pointer to NCE for first hop to destination
92 * PathMTU = Size of Maximum Transmission Unit of path
96 * IP datagram is larger than PathMTU when this is called
99 PIPFRAGMENT_CONTEXT IFC;
100 NDIS_STATUS NdisStatus;
103 TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) NCE (0x%X) PathMTU (%d).\n",
104 IPPacket, NCE, PathMTU));
106 IFC = ExAllocatePool(NonPagedPool, sizeof(IPFRAGMENT_CONTEXT));
108 return STATUS_INSUFFICIENT_RESOURCES;
110 /* We allocate a buffer for a PathMTU sized packet and reuse
111 it for all fragments */
112 Data = ExAllocatePool(NonPagedPool, MaxLLHeaderSize + PathMTU);
115 return STATUS_INSUFFICIENT_RESOURCES;
118 /* Allocate NDIS packet */
119 NdisAllocatePacket(&NdisStatus, &IFC->NdisPacket, GlobalPacketPool);
120 if (NdisStatus != NDIS_STATUS_SUCCESS) {
121 //ExFreePool(Data); // RobD - why are we trying to free this here?, not allocated yet!
123 return STATUS_INSUFFICIENT_RESOURCES;
126 /* Allocate NDIS buffer */
127 NdisAllocateBuffer(&NdisStatus, &IFC->NdisBuffer,
128 GlobalBufferPool, Data, MaxLLHeaderSize + PathMTU);
129 if (NdisStatus != NDIS_STATUS_SUCCESS) {
130 NdisFreePacket(IFC->NdisPacket);
133 return STATUS_INSUFFICIENT_RESOURCES;
136 /* Link NDIS buffer into packet */
137 NdisChainBufferAtFront(IFC->NdisPacket, IFC->NdisBuffer);
139 IFC->Header = (PVOID)((ULONG_PTR)Data + MaxLLHeaderSize);
140 IFC->Datagram = IPPacket->NdisPacket;
141 IFC->DatagramData = IPPacket->Header;
142 IFC->HeaderSize = IPPacket->HeaderSize;
143 IFC->PathMTU = PathMTU;
146 IFC->BytesLeft = IPPacket->TotalSize - IPPacket->HeaderSize;
147 IFC->Data = (PVOID)((ULONG_PTR)IFC->Header + IPPacket->HeaderSize);
149 PC(IFC->NdisPacket)->DLComplete = IPSendComplete;
150 /* Set upper layer completion function to NULL to indicate that
151 this packet is an IP datagram fragment and thus we should
152 check for more fragments to send. If this is NULL the
153 Context field is a pointer to an IPFRAGMENT_CONTEXT structure */
154 PC(IFC->NdisPacket)->Complete = NULL;
155 PC(IFC->NdisPacket)->Context = IFC;
157 /* Copy IP datagram header to fragment buffer */
158 RtlCopyMemory(IFC->Header, IPPacket->Header, IPPacket->HeaderSize);
160 /* Prepare next fragment for transmission and send it */
162 PrepareNextFragment(IFC);
164 IPSendFragment(IFC->NdisPacket, NCE);
166 return STATUS_SUCCESS;
172 PNDIS_PACKET NdisPacket,
173 NDIS_STATUS NdisStatus)
175 * FUNCTION: IP datagram fragment send completion handler
177 * Context = Pointer to context information (IP_INTERFACE)
178 * Packet = Pointer to NDIS packet that was sent
179 * NdisStatus = NDIS status of operation
181 * This routine is called when an IP datagram fragment has been sent
184 TI_DbgPrint(MAX_TRACE, ("Called. Context (0x%X) NdisPacket (0x%X) NdisStatus (0x%X)\n",
185 Context, NdisPacket, NdisStatus));
187 /* FIXME: Stop sending fragments and cleanup datagram buffers if
188 there was an error */
190 if (PC(NdisPacket)->Complete)
191 /* This datagram was only one fragment long so call completion handler now */
192 (*PC(NdisPacket)->Complete)(PC(NdisPacket)->Context, NdisPacket, NdisStatus);
194 /* This was one of many fragments of an IP datagram. Prepare
195 next fragment and send it or if there are no more fragments,
196 call upper layer completion routine */
198 PIPFRAGMENT_CONTEXT IFC = (PIPFRAGMENT_CONTEXT)PC(NdisPacket)->Context;
200 if (PrepareNextFragment(IFC)) {
201 /* A fragment was prepared for transmission, so send it */
202 IPSendFragment(IFC->NdisPacket, IFC->NCE);
204 TI_DbgPrint(MAX_TRACE, ("Calling completion handler.\n"));
206 /* There are no more fragments to transmit, so call completion handler */
207 NdisPacket = IFC->Datagram;
208 FreeNdisPacket(IFC->NdisPacket);
210 (*PC(NdisPacket)->Complete)(PC(NdisPacket)->Context, NdisPacket, NdisStatus);
216 NTSTATUS IPSendFragment(
217 PNDIS_PACKET NdisPacket,
218 PNEIGHBOR_CACHE_ENTRY NCE)
220 * FUNCTION: Sends an IP datagram fragment to a neighbor
222 * NdisPacket = Pointer to an NDIS packet containing fragment
223 * NCE = Pointer to NCE for first hop to destination
225 * Status of operation
227 * Lowest level IP send routine
230 TI_DbgPrint(MAX_TRACE, ("Called. NdisPacket (0x%X) NCE (0x%X).\n", NdisPacket, NCE));
232 TI_DbgPrint(MAX_TRACE, ("NCE->State = %d.\n", NCE->State));
234 switch (NCE->State) {
236 /* Neighbor is always valid */
240 /* Neighbor is reachable */
242 /* FIXME: Set reachable timer */
247 /* Enter delay state and send packet */
249 /* FIXME: Enter delay state */
255 /* In these states we send the packet and hope the neighbor
256 hasn't changed hardware address */
260 TI_DbgPrint(MAX_TRACE, ("Queueing packet.\n"));
262 /* We don't know the hardware address of the first hop to
263 the destination. Queue the packet on the NCE and return */
264 NBQueuePacket(NCE, NdisPacket);
266 return STATUS_SUCCESS;
268 /* Should not happen */
269 TI_DbgPrint(MIN_TRACE, ("Unknown NCE state.\n"));
271 return STATUS_SUCCESS;
274 PC(NdisPacket)->DLComplete = IPSendComplete;
275 (*NCE->Interface->Transmit)(NCE->Interface->Context,
281 return STATUS_SUCCESS;
285 NTSTATUS IPSendDatagram(
287 PROUTE_CACHE_NODE RCN)
289 * FUNCTION: Sends an IP datagram to a remote address
291 * IPPacket = Pointer to an IP packet
292 * RCN = Pointer to route cache node
294 * Status of operation
296 * This is the highest level IP send routine. It possibly breaks the packet
297 * into two or more fragments before passing it on to the next lower level
298 * send routine (IPSendFragment)
301 PNEIGHBOR_CACHE_ENTRY NCE;
304 TI_DbgPrint(MAX_TRACE, ("Called. IPPacket (0x%X) RCN (0x%X)\n", IPPacket, RCN));
306 DISPLAY_IP_PACKET(IPPacket);
312 TI_DbgPrint(MIN_TRACE, ("No NCE to use.\n"));
313 FreeNdisPacket(IPPacket->NdisPacket);
314 return STATUS_SUCCESS;
318 /* Fetch path MTU now, because it may change */
319 PathMTU = RCN->PathMTU;
321 if (IPPacket->TotalSize > PathMTU) {
322 return SendFragments(IPPacket, NCE, PathMTU);
324 if ((IPPacket->Flags & IP_PACKET_FLAG_RAW) == 0) {
325 /* Calculate checksum of IP header */
326 ((PIPv4_HEADER)IPPacket->Header)->Checksum = 0;
328 ((PIPv4_HEADER)IPPacket->Header)->Checksum = (USHORT)
329 IPv4Checksum(IPPacket->Header, IPPacket->HeaderSize, 0);
331 TI_DbgPrint(MAX_TRACE, ("Sending packet (length is %d).\n",
332 WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength)));
334 TI_DbgPrint(MAX_TRACE, ("Sending raw packet (flags are 0x%X).\n",
338 return IPSendFragment(IPPacket->NdisPacket, NCE);