2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS TCP/IP protocol driver
4 * FILE: network/receive.c
5 * PURPOSE: Internet Protocol receive routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * NOTES: The IP datagram reassembly algorithm is taken from
10 * CSH 01/08-2000 Created
15 LIST_ENTRY ReassemblyListHead
;
16 KSPIN_LOCK ReassemblyListLock
;
17 NPAGED_LOOKASIDE_LIST IPDRList
;
18 NPAGED_LOOKASIDE_LIST IPFragmentList
;
19 NPAGED_LOOKASIDE_LIST IPHoleList
;
21 VOID
ReflectPacketComplete(
24 NDIS_STATUS Status
) {
27 PIPDATAGRAM_HOLE
CreateHoleDescriptor(
31 * FUNCTION: Returns a pointer to a IP datagram hole descriptor
33 * First = Offset of first octet of the hole
34 * Last = Offset of last octet of the hole
36 * Pointer to descriptor, NULL if there was not enough free
40 PIPDATAGRAM_HOLE Hole
;
42 TI_DbgPrint(DEBUG_IP
, ("Called. First (%d) Last (%d).\n", First
, Last
));
44 Hole
= TcpipAllocateFromNPagedLookasideList(&IPHoleList
);
46 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
53 TI_DbgPrint(DEBUG_IP
, ("Returning hole descriptor at (0x%X).\n", Hole
));
60 PIPDATAGRAM_REASSEMBLY IPDR
)
62 * FUNCTION: Frees an IP datagram reassembly structure
64 * IPDR = Pointer to IP datagram reassembly structure
67 PLIST_ENTRY CurrentEntry
;
68 PLIST_ENTRY NextEntry
;
69 PIPDATAGRAM_HOLE CurrentH
;
70 PIP_FRAGMENT CurrentF
;
72 TI_DbgPrint(DEBUG_IP
, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR
));
74 /* Free all descriptors */
75 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
76 while (CurrentEntry
!= &IPDR
->HoleListHead
) {
77 NextEntry
= CurrentEntry
->Flink
;
78 CurrentH
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
79 /* Unlink it from the list */
80 RemoveEntryList(CurrentEntry
);
82 TI_DbgPrint(DEBUG_IP
, ("Freeing hole descriptor at (0x%X).\n", CurrentH
));
84 /* And free the hole descriptor */
85 TcpipFreeToNPagedLookasideList(&IPHoleList
, CurrentH
);
87 CurrentEntry
= NextEntry
;
90 /* Free all fragments */
91 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
92 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
93 NextEntry
= CurrentEntry
->Flink
;
94 CurrentF
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
95 /* Unlink it from the list */
96 RemoveEntryList(CurrentEntry
);
98 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment data at (0x%X).\n", CurrentF
->Data
));
100 /* Free the fragment data buffer */
101 exFreePool(CurrentF
->Data
);
103 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment at (0x%X).\n", CurrentF
));
105 /* And free the fragment descriptor */
106 TcpipFreeToNPagedLookasideList(&IPFragmentList
, CurrentF
);
107 CurrentEntry
= NextEntry
;
110 TI_DbgPrint(DEBUG_IP
, ("Freeing IPDR data at (0x%X).\n", IPDR
));
112 TcpipFreeToNPagedLookasideList(&IPDRList
, IPDR
);
117 PIPDATAGRAM_REASSEMBLY IPDR
)
119 * FUNCTION: Removes an IP datagram reassembly structure from the global list
121 * IPDR = Pointer to IP datagram reassembly structure
126 TI_DbgPrint(DEBUG_IP
, ("Removing IPDR at (0x%X).\n", IPDR
));
128 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
129 RemoveEntryList(&IPDR
->ListEntry
);
130 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
134 PIPDATAGRAM_REASSEMBLY
GetReassemblyInfo(
137 * FUNCTION: Returns a pointer to an IP datagram reassembly structure
139 * IPPacket = Pointer to IP packet
141 * A datagram is identified by four paramters, which are
142 * Source and destination address, protocol number and
143 * identification number
147 PLIST_ENTRY CurrentEntry
;
148 PIPDATAGRAM_REASSEMBLY Current
;
149 PIPv4_HEADER Header
= (PIPv4_HEADER
)IPPacket
->Header
;
151 TI_DbgPrint(DEBUG_IP
, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket
));
153 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
155 /* FIXME: Assume IPv4 */
157 CurrentEntry
= ReassemblyListHead
.Flink
;
158 while (CurrentEntry
!= &ReassemblyListHead
) {
159 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
160 if (AddrIsEqual(&IPPacket
->SrcAddr
, &Current
->SrcAddr
) &&
161 (Header
->Id
== Current
->Id
) &&
162 (Header
->Protocol
== Current
->Protocol
) &&
163 (AddrIsEqual(&IPPacket
->DstAddr
, &Current
->DstAddr
))) {
164 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
168 CurrentEntry
= CurrentEntry
->Flink
;
171 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
177 PIP_PACKET
ReassembleDatagram(
178 PIPDATAGRAM_REASSEMBLY IPDR
)
180 * FUNCTION: Reassembles an IP datagram
182 * IPDR = Pointer to IP datagram reassembly structure
184 * This routine concatenates fragments into a complete IP datagram.
185 * The lock is held when this routine is called
187 * Pointer to IP packet, NULL if there was not enough free resources
189 * At this point, header is expected to point to the IP header
193 PLIST_ENTRY CurrentEntry
;
194 PIP_FRAGMENT Current
;
197 TI_DbgPrint(DEBUG_IP
, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR
));
198 TI_DbgPrint(DEBUG_IP
, ("IPDR->HeaderSize = %d\n", IPDR
->HeaderSize
));
199 TI_DbgPrint(DEBUG_IP
, ("IPDR->DataSize = %d\n", IPDR
->DataSize
));
201 TI_DbgPrint(DEBUG_IP
, ("Fragment header:\n"));
202 //OskitDumpBuffer((PCHAR)IPDR->IPv4Header, IPDR->HeaderSize);
204 /* FIXME: Assume IPv4 */
205 IPPacket
= IPCreatePacket(IP_ADDRESS_V4
);
209 IPPacket
->TotalSize
= IPDR
->HeaderSize
+ IPDR
->DataSize
;
210 IPPacket
->ContigSize
= IPPacket
->TotalSize
;
211 IPPacket
->HeaderSize
= IPDR
->HeaderSize
;
212 /*IPPacket->Position = IPDR->HeaderSize;*/
214 RtlCopyMemory(&IPPacket
->SrcAddr
, &IPDR
->SrcAddr
, sizeof(IP_ADDRESS
));
215 RtlCopyMemory(&IPPacket
->DstAddr
, &IPDR
->DstAddr
, sizeof(IP_ADDRESS
));
217 /* Allocate space for full IP datagram */
218 IPPacket
->Header
= exAllocatePool(NonPagedPool
, IPPacket
->TotalSize
);
219 if (!IPPacket
->Header
) {
220 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
221 (*IPPacket
->Free
)(IPPacket
);
225 /* Copy the header into the buffer */
226 RtlCopyMemory(IPPacket
->Header
, &IPDR
->IPv4Header
, IPDR
->HeaderSize
);
228 Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPDR
->HeaderSize
);
229 IPPacket
->Data
= Data
;
231 /* Copy data from all fragments into buffer */
232 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
233 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
234 Current
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
236 TI_DbgPrint(DEBUG_IP
, ("Copying (%d) bytes of fragment data from (0x%X) to offset (%d).\n",
237 Current
->Size
, Data
, Current
->Offset
));
238 /* Copy fragment data to the destination buffer at the correct offset */
239 RtlCopyMemory((PVOID
)((ULONG_PTR
)Data
+ Current
->Offset
),
242 //OskitDumpBuffer( Data, Current->Offset + Current->Size );
243 CurrentEntry
= CurrentEntry
->Flink
;
250 __inline VOID
Cleanup(
253 PIPDATAGRAM_REASSEMBLY IPDR
,
254 PVOID Buffer OPTIONAL
)
256 * FUNCTION: Performs cleaning operations on errors
258 * Lock = Pointer to spin lock to be released
259 * OldIrql = Value of IRQL when spin lock was acquired
260 * IPDR = Pointer to IP datagram reassembly structure to free
261 * Buffer = Optional pointer to a buffer to free
264 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
266 TcpipReleaseSpinLock(Lock
, OldIrql
);
274 VOID
ProcessFragment(
278 * FUNCTION: Processes an IP datagram or fragment
280 * IF = Pointer to IP interface packet was receive on
281 * IPPacket = Pointer to IP packet
283 * This routine reassembles fragments and, if a whole datagram can
284 * be assembled, passes the datagram on to the IP protocol dispatcher
288 PIPDATAGRAM_REASSEMBLY IPDR
;
289 PLIST_ENTRY CurrentEntry
;
290 PIPDATAGRAM_HOLE Hole
, NewHole
;
293 BOOLEAN MoreFragments
;
294 PIPv4_HEADER IPv4Header
;
296 PIP_FRAGMENT Fragment
;
298 /* FIXME: Assume IPv4 */
300 IPv4Header
= (PIPv4_HEADER
)IPPacket
->Header
;
302 /* Check if we already have an reassembly structure for this datagram */
303 IPDR
= GetReassemblyInfo(IPPacket
);
305 TI_DbgPrint(DEBUG_IP
, ("Continueing assembly.\n"));
306 /* We have a reassembly structure */
307 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
308 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
309 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
311 TI_DbgPrint(DEBUG_IP
, ("Starting new assembly.\n"));
313 /* We don't have a reassembly structure, create one */
314 IPDR
= TcpipAllocateFromNPagedLookasideList(&IPDRList
);
316 /* We don't have the resources to process this packet, discard it */
319 /* Create a descriptor spanning from zero to infinity.
320 Actually, we use a value slightly greater than the
321 maximum number of octets an IP datagram can contain */
322 Hole
= CreateHoleDescriptor(0, 65536);
324 /* We don't have the resources to process this packet, discard it */
325 TcpipFreeToNPagedLookasideList(&IPDRList
, IPDR
);
328 AddrInitIPv4(&IPDR
->SrcAddr
, IPv4Header
->SrcAddr
);
329 AddrInitIPv4(&IPDR
->DstAddr
, IPv4Header
->DstAddr
);
330 IPDR
->Id
= IPv4Header
->Id
;
331 IPDR
->Protocol
= IPv4Header
->Protocol
;
332 InitializeListHead(&IPDR
->FragmentListHead
);
333 InitializeListHead(&IPDR
->HoleListHead
);
334 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
335 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
337 TcpipInitializeSpinLock(&IPDR
->Lock
);
339 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
341 /* Update the reassembly list */
342 TcpipInterlockedInsertTailList(
345 &ReassemblyListLock
);
348 FragFirst
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_FRAGOFS_MASK
) << 3;
349 FragLast
= FragFirst
+ WN2H(IPv4Header
->TotalLength
);
350 MoreFragments
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_MF_MASK
) > 0;
353 if (CurrentEntry
== &IPDR
->HoleListHead
)
354 /* No more entries */
357 TI_DbgPrint(DEBUG_IP
, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
358 FragFirst
, FragLast
, Hole
->First
, Hole
->Last
));
360 if ((FragFirst
> Hole
->Last
) || (FragLast
< Hole
->First
)) {
361 TI_DbgPrint(MID_TRACE
, ("No overlap.\n"));
362 /* The fragment does not overlap with the hole, try next
363 descriptor in the list */
365 CurrentEntry
= CurrentEntry
->Flink
;
366 if (CurrentEntry
!= &IPDR
->HoleListHead
)
367 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
371 /* The fragment overlap with the hole, unlink the descriptor */
372 RemoveEntryList(CurrentEntry
);
374 if (FragFirst
> Hole
->First
) {
375 NewHole
= CreateHoleDescriptor(Hole
->First
, FragLast
- 1);
377 /* We don't have the resources to process this packet, discard it */
378 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, Hole
);
382 /* Put the new descriptor in the list */
383 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
386 if ((FragLast
< Hole
->Last
) && (MoreFragments
)) {
387 /* We can reuse the descriptor for the new hole */
388 Hole
->First
= FragLast
+ 1;
390 /* Put the new hole descriptor in the list */
391 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
393 TcpipFreeToNPagedLookasideList(&IPHoleList
, Hole
);
395 /* If this is the first fragment, save the IP header */
396 if (FragFirst
== 0) {
397 TI_DbgPrint(DEBUG_IP
, ("First fragment found. Header buffer is at (0x%X). "
398 "Header size is (%d).\n", &IPDR
->IPv4Header
, IPPacket
->HeaderSize
));
400 RtlCopyMemory(&IPDR
->IPv4Header
, IPPacket
->Header
, IPPacket
->HeaderSize
);
401 IPDR
->HeaderSize
= IPPacket
->HeaderSize
;
404 /* Create a buffer, copy the data into it and put it
405 in the fragment list */
407 Fragment
= TcpipAllocateFromNPagedLookasideList(&IPFragmentList
);
409 /* We don't have the resources to process this packet, discard it */
410 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, NULL
);
414 TI_DbgPrint(DEBUG_IP
, ("Fragment descriptor allocated at (0x%X).\n", Fragment
));
416 Fragment
->Size
= IPPacket
->TotalSize
- IPPacket
->HeaderSize
;
417 Fragment
->Data
= exAllocatePool(NonPagedPool
, Fragment
->Size
);
418 if (!Fragment
->Data
) {
419 /* We don't have the resources to process this packet, discard it */
420 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, Fragment
);
424 /* Position here is an offset from the NdisPacket start, not the header */
425 TI_DbgPrint(DEBUG_IP
, ("Fragment data buffer allocated at (0x%X) Size (%d) Pos (%d).\n",
426 Fragment
->Data
, Fragment
->Size
, IPPacket
->Position
));
428 /* Copy datagram data into fragment buffer */
429 CopyPacketToBuffer(Fragment
->Data
,
430 IPPacket
->NdisPacket
,
433 Fragment
->Offset
= FragFirst
;
435 /* If this is the last fragment, compute and save the datagram data size */
437 IPDR
->DataSize
= FragFirst
+ Fragment
->Size
;
439 /* Put the fragment in the list */
440 InsertTailList(&IPDR
->FragmentListHead
, &Fragment
->ListEntry
);
444 TI_DbgPrint(DEBUG_IP
, ("Done searching for hole descriptor.\n"));
446 if (IsListEmpty(&IPDR
->HoleListHead
)) {
447 /* Hole list is empty which means a complete datagram can be assembled.
448 Assemble the datagram and pass it to an upper layer protocol */
450 TI_DbgPrint(DEBUG_IP
, ("Complete datagram received.\n"));
452 Datagram
= ReassembleDatagram(IPDR
);
455 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
460 /* Not enough free resources, discard the packet */
463 DISPLAY_IP_PACKET(Datagram
);
465 /* Give the packet to the protocol dispatcher */
466 IPDispatchProtocol(IF
, Datagram
);
468 /* We're done with this datagram */
469 exFreePool(Datagram
->Header
);
470 TI_DbgPrint(MAX_TRACE
, ("Freeing datagram at (0x%X).\n", Datagram
));
471 (*Datagram
->Free
)(Datagram
);
473 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
477 VOID
IPFreeReassemblyList(
480 * FUNCTION: Frees all IP datagram reassembly structures in the list
484 PLIST_ENTRY CurrentEntry
;
485 PIPDATAGRAM_REASSEMBLY Current
;
487 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
489 CurrentEntry
= ReassemblyListHead
.Flink
;
490 while (CurrentEntry
!= &ReassemblyListHead
) {
491 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
492 /* Unlink it from the list */
493 RemoveEntryList(CurrentEntry
);
495 /* And free the descriptor */
498 CurrentEntry
= CurrentEntry
->Flink
;
501 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
505 VOID
IPDatagramReassemblyTimeout(
508 * FUNCTION: IP datagram reassembly timeout handler
510 * This routine is called by IPTimeout to free any resources used
511 * to hold IP fragments that have taken too long to reassemble
516 VOID
IPv4Receive(PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
518 * FUNCTION: Receives an IPv4 datagram (or fragment)
520 * Context = Pointer to context information (IP_INTERFACE)
521 * IPPacket = Pointer to IP packet
524 TI_DbgPrint(DEBUG_IP
, ("Received IPv4 datagram.\n"));
526 IPPacket
->HeaderSize
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
& 0x0F) << 2;
527 TI_DbgPrint(DEBUG_IP
, ("IPPacket->HeaderSize = %d\n", IPPacket
->HeaderSize
));
529 if (IPPacket
->HeaderSize
> IPv4_MAX_HEADER_SIZE
) {
532 ("Datagram received with incorrect header size (%d).\n",
533 IPPacket
->HeaderSize
));
538 /* Checksum IPv4 header */
539 if (!IPv4CorrectChecksum(IPPacket
->Header
, IPPacket
->HeaderSize
)) {
542 ("Datagram received with bad checksum. Checksum field (0x%X)\n",
543 WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->Checksum
)));
548 IPPacket
->TotalSize
= WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->TotalLength
);
550 AddrInitIPv4(&IPPacket
->SrcAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->SrcAddr
);
551 AddrInitIPv4(&IPPacket
->DstAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->DstAddr
);
553 IPPacket
->Position
+= IPPacket
->HeaderSize
;
554 IPPacket
->Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPPacket
->HeaderSize
);
556 TI_DbgPrint(MID_TRACE
,("IPPacket->Position = %d\n",
557 IPPacket
->Position
));
559 //OskitDumpBuffer(IPPacket->Header, IPPacket->TotalSize);
561 /* FIXME: Possibly forward packets with multicast addresses */
563 /* FIXME: Should we allow packets to be received on the wrong interface? */
564 /* XXX Find out if this packet is destined for us */
565 ProcessFragment(IF
, IPPacket
);
568 /* This packet is not destined for us. If we are a router,
569 try to find a route and forward the packet */
571 /* FIXME: Check if acting as a router */
574 PROUTE_CACHE_NODE RCN
;
576 /* FIXME: Possibly fragment datagram */
577 /* Forward the packet */
578 if(!RouteGetRouteToDestination( &IPPacket
->DstAddr
, NULL
, &RCN
))
579 IPSendDatagram(IPPacket
, RCN
, ReflectPacketComplete
, IPPacket
);
581 TI_DbgPrint(MIN_TRACE
, ("No route to destination (0x%X).\n",
582 IPPacket
->DstAddr
.Address
.IPv4Address
));
584 /* FIXME: Send ICMP error code */
591 VOID
IPReceive( PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
593 * FUNCTION: Receives an IP datagram (or fragment)
596 * IPPacket = Pointer to IP packet
601 /* Check that IP header has a supported version */
602 Version
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
>> 4);
606 IPPacket
->Type
= IP_ADDRESS_V4
;
607 IPv4Receive(IF
, IPPacket
);
610 IPPacket
->Type
= IP_ADDRESS_V6
;
611 TI_DbgPrint(MAX_TRACE
, ("Datagram of type IPv6 discarded.\n"));
614 TI_DbgPrint(MIN_TRACE
, ("Datagram has an unsupported IP version %d.\n", Version
));