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 PIPDATAGRAM_HOLE
CreateHoleDescriptor(
25 * FUNCTION: Returns a pointer to a IP datagram hole descriptor
27 * First = Offset of first octet of the hole
28 * Last = Offset of last octet of the hole
30 * Pointer to descriptor, NULL if there was not enough free
34 PIPDATAGRAM_HOLE Hole
;
36 TI_DbgPrint(DEBUG_IP
, ("Called. First (%d) Last (%d).\n", First
, Last
));
38 Hole
= ExAllocateFromNPagedLookasideList(&IPHoleList
);
40 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
47 TI_DbgPrint(DEBUG_IP
, ("Returning hole descriptor at (0x%X).\n", Hole
));
54 PIPDATAGRAM_REASSEMBLY IPDR
)
56 * FUNCTION: Frees an IP datagram reassembly structure
58 * IPDR = Pointer to IP datagram reassembly structure
61 PLIST_ENTRY CurrentEntry
;
62 PLIST_ENTRY NextEntry
;
63 PIPDATAGRAM_HOLE CurrentH
;
64 PIP_FRAGMENT CurrentF
;
66 TI_DbgPrint(DEBUG_IP
, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR
));
68 /* Free all descriptors */
69 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
70 while (CurrentEntry
!= &IPDR
->HoleListHead
) {
71 NextEntry
= CurrentEntry
->Flink
;
72 CurrentH
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
73 /* Unlink it from the list */
74 RemoveEntryList(CurrentEntry
);
76 TI_DbgPrint(DEBUG_IP
, ("Freeing hole descriptor at (0x%X).\n", CurrentH
));
78 /* And free the hole descriptor */
79 ExFreeToNPagedLookasideList(&IPHoleList
, CurrentH
);
81 CurrentEntry
= NextEntry
;
84 /* Free all fragments */
85 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
86 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
87 NextEntry
= CurrentEntry
->Flink
;
88 CurrentF
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
89 /* Unlink it from the list */
90 RemoveEntryList(CurrentEntry
);
92 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment data at (0x%X).\n", CurrentF
->Data
));
94 /* Free the fragment data buffer */
95 ExFreePoolWithTag(CurrentF
->Data
, FRAGMENT_DATA_TAG
);
97 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment at (0x%X).\n", CurrentF
));
99 /* And free the fragment descriptor */
100 ExFreeToNPagedLookasideList(&IPFragmentList
, CurrentF
);
101 CurrentEntry
= NextEntry
;
104 TI_DbgPrint(DEBUG_IP
, ("Freeing IPDR data at (0x%X).\n", IPDR
));
106 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
111 PIPDATAGRAM_REASSEMBLY IPDR
)
113 * FUNCTION: Removes an IP datagram reassembly structure from the global list
115 * IPDR = Pointer to IP datagram reassembly structure
120 TI_DbgPrint(DEBUG_IP
, ("Removing IPDR at (0x%X).\n", IPDR
));
122 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
123 RemoveEntryList(&IPDR
->ListEntry
);
124 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
128 PIPDATAGRAM_REASSEMBLY
GetReassemblyInfo(
131 * FUNCTION: Returns a pointer to an IP datagram reassembly structure
133 * IPPacket = Pointer to IP packet
135 * A datagram is identified by four paramters, which are
136 * Source and destination address, protocol number and
137 * identification number
141 PLIST_ENTRY CurrentEntry
;
142 PIPDATAGRAM_REASSEMBLY Current
;
143 PIPv4_HEADER Header
= (PIPv4_HEADER
)IPPacket
->Header
;
145 TI_DbgPrint(DEBUG_IP
, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket
));
147 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
149 /* FIXME: Assume IPv4 */
151 CurrentEntry
= ReassemblyListHead
.Flink
;
152 while (CurrentEntry
!= &ReassemblyListHead
) {
153 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
154 if (AddrIsEqual(&IPPacket
->SrcAddr
, &Current
->SrcAddr
) &&
155 (Header
->Id
== Current
->Id
) &&
156 (Header
->Protocol
== Current
->Protocol
) &&
157 (AddrIsEqual(&IPPacket
->DstAddr
, &Current
->DstAddr
))) {
158 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
162 CurrentEntry
= CurrentEntry
->Flink
;
165 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
174 PIPDATAGRAM_REASSEMBLY IPDR
)
176 * FUNCTION: Reassembles an IP datagram
178 * IPDR = Pointer to IP datagram reassembly structure
180 * This routine concatenates fragments into a complete IP datagram.
181 * The lock is held when this routine is called
183 * Pointer to IP packet, NULL if there was not enough free resources
185 * At this point, header is expected to point to the IP header
188 PLIST_ENTRY CurrentEntry
;
189 PIP_FRAGMENT Current
;
192 TI_DbgPrint(DEBUG_IP
, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR
));
193 TI_DbgPrint(DEBUG_IP
, ("IPDR->HeaderSize = %d\n", IPDR
->HeaderSize
));
194 TI_DbgPrint(DEBUG_IP
, ("IPDR->DataSize = %d\n", IPDR
->DataSize
));
196 TI_DbgPrint(DEBUG_IP
, ("Fragment header:\n"));
197 //OskitDumpBuffer((PCHAR)IPDR->IPv4Header, IPDR->HeaderSize);
199 IPPacket
->TotalSize
= IPDR
->HeaderSize
+ IPDR
->DataSize
;
200 IPPacket
->ContigSize
= IPPacket
->TotalSize
;
201 IPPacket
->HeaderSize
= IPDR
->HeaderSize
;
202 /*IPPacket->Position = IPDR->HeaderSize;*/
204 RtlCopyMemory(&IPPacket
->SrcAddr
, &IPDR
->SrcAddr
, sizeof(IP_ADDRESS
));
205 RtlCopyMemory(&IPPacket
->DstAddr
, &IPDR
->DstAddr
, sizeof(IP_ADDRESS
));
207 /* Allocate space for full IP datagram */
208 IPPacket
->Header
= ExAllocatePoolWithTag(NonPagedPool
, IPPacket
->TotalSize
, PACKET_BUFFER_TAG
);
209 if (!IPPacket
->Header
) {
210 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
211 (*IPPacket
->Free
)(IPPacket
);
215 /* Copy the header into the buffer */
216 RtlCopyMemory(IPPacket
->Header
, &IPDR
->IPv4Header
, IPDR
->HeaderSize
);
218 Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPDR
->HeaderSize
);
219 IPPacket
->Data
= Data
;
221 /* Copy data from all fragments into buffer */
222 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
223 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
224 Current
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
226 TI_DbgPrint(DEBUG_IP
, ("Copying (%d) bytes of fragment data from (0x%X) to offset (%d).\n",
227 Current
->Size
, Data
, Current
->Offset
));
228 /* Copy fragment data to the destination buffer at the correct offset */
229 RtlCopyMemory((PVOID
)((ULONG_PTR
)Data
+ Current
->Offset
),
232 //OskitDumpBuffer( Data, Current->Offset + Current->Size );
233 CurrentEntry
= CurrentEntry
->Flink
;
240 __inline VOID
Cleanup(
243 PIPDATAGRAM_REASSEMBLY IPDR
)
245 * FUNCTION: Performs cleaning operations on errors
247 * Lock = Pointer to spin lock to be released
248 * OldIrql = Value of IRQL when spin lock was acquired
249 * IPDR = Pointer to IP datagram reassembly structure to free
250 * Buffer = Optional pointer to a buffer to free
253 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
255 TcpipReleaseSpinLock(Lock
, OldIrql
);
261 VOID
ProcessFragment(
265 * FUNCTION: Processes an IP datagram or fragment
267 * IF = Pointer to IP interface packet was receive on
268 * IPPacket = Pointer to IP packet
270 * This routine reassembles fragments and, if a whole datagram can
271 * be assembled, passes the datagram on to the IP protocol dispatcher
275 PIPDATAGRAM_REASSEMBLY IPDR
;
276 PLIST_ENTRY CurrentEntry
;
277 PIPDATAGRAM_HOLE Hole
, NewHole
;
280 BOOLEAN MoreFragments
;
281 PIPv4_HEADER IPv4Header
;
283 PIP_FRAGMENT Fragment
;
286 /* FIXME: Assume IPv4 */
288 IPv4Header
= (PIPv4_HEADER
)IPPacket
->Header
;
290 /* Check if we already have an reassembly structure for this datagram */
291 IPDR
= GetReassemblyInfo(IPPacket
);
293 TI_DbgPrint(DEBUG_IP
, ("Continueing assembly.\n"));
294 /* We have a reassembly structure */
295 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
297 TI_DbgPrint(DEBUG_IP
, ("Starting new assembly.\n"));
299 /* We don't have a reassembly structure, create one */
300 IPDR
= ExAllocateFromNPagedLookasideList(&IPDRList
);
302 /* We don't have the resources to process this packet, discard it */
305 /* Create a descriptor spanning from zero to infinity.
306 Actually, we use a value slightly greater than the
307 maximum number of octets an IP datagram can contain */
308 Hole
= CreateHoleDescriptor(0, 65536);
310 /* We don't have the resources to process this packet, discard it */
311 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
314 AddrInitIPv4(&IPDR
->SrcAddr
, IPv4Header
->SrcAddr
);
315 AddrInitIPv4(&IPDR
->DstAddr
, IPv4Header
->DstAddr
);
316 IPDR
->Id
= IPv4Header
->Id
;
317 IPDR
->Protocol
= IPv4Header
->Protocol
;
318 IPDR
->TimeoutCount
= 0;
319 InitializeListHead(&IPDR
->FragmentListHead
);
320 InitializeListHead(&IPDR
->HoleListHead
);
321 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
323 TcpipInitializeSpinLock(&IPDR
->Lock
);
325 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
327 /* Update the reassembly list */
328 TcpipInterlockedInsertTailList(
331 &ReassemblyListLock
);
334 FragFirst
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_FRAGOFS_MASK
) << 3;
335 FragLast
= FragFirst
+ WN2H(IPv4Header
->TotalLength
);
336 MoreFragments
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_MF_MASK
) > 0;
338 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
340 if (CurrentEntry
== &IPDR
->HoleListHead
)
343 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
345 TI_DbgPrint(DEBUG_IP
, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
346 FragFirst
, FragLast
, Hole
->First
, Hole
->Last
));
348 if ((FragFirst
> Hole
->Last
) || (FragLast
< Hole
->First
)) {
349 TI_DbgPrint(MID_TRACE
, ("No overlap.\n"));
350 /* The fragment does not overlap with the hole, try next
351 descriptor in the list */
353 CurrentEntry
= CurrentEntry
->Flink
;
357 /* The fragment overlap with the hole, unlink the descriptor */
358 RemoveEntryList(CurrentEntry
);
360 if (FragFirst
> Hole
->First
) {
361 NewHole
= CreateHoleDescriptor(Hole
->First
, FragFirst
- 1);
363 /* We don't have the resources to process this packet, discard it */
364 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
365 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
369 /* Put the new descriptor in the list */
370 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
373 if ((FragLast
< Hole
->Last
) && MoreFragments
) {
374 NewHole
= CreateHoleDescriptor(FragLast
+ 1, Hole
->Last
);
376 /* We don't have the resources to process this packet, discard it */
377 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
378 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
382 /* Put the new hole descriptor in the list */
383 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
386 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
388 /* If this is the first fragment, save the IP header */
389 if (FragFirst
== 0) {
390 TI_DbgPrint(DEBUG_IP
, ("First fragment found. Header buffer is at (0x%X). "
391 "Header size is (%d).\n", &IPDR
->IPv4Header
, IPPacket
->HeaderSize
));
393 RtlCopyMemory(&IPDR
->IPv4Header
, IPPacket
->Header
, IPPacket
->HeaderSize
);
394 IPDR
->HeaderSize
= IPPacket
->HeaderSize
;
397 /* Create a buffer, copy the data into it and put it
398 in the fragment list */
400 Fragment
= ExAllocateFromNPagedLookasideList(&IPFragmentList
);
402 /* We don't have the resources to process this packet, discard it */
403 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
407 TI_DbgPrint(DEBUG_IP
, ("Fragment descriptor allocated at (0x%X).\n", Fragment
));
409 Fragment
->Size
= IPPacket
->TotalSize
- IPPacket
->HeaderSize
;
410 Fragment
->Data
= ExAllocatePoolWithTag(NonPagedPool
, Fragment
->Size
, FRAGMENT_DATA_TAG
);
411 if (!Fragment
->Data
) {
412 /* We don't have the resources to process this packet, discard it */
413 ExFreeToNPagedLookasideList(&IPFragmentList
, Fragment
);
414 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
418 /* Position here is an offset from the NdisPacket start, not the header */
419 TI_DbgPrint(DEBUG_IP
, ("Fragment data buffer allocated at (0x%X) Size (%d) Pos (%d).\n",
420 Fragment
->Data
, Fragment
->Size
, IPPacket
->Position
));
422 /* Copy datagram data into fragment buffer */
423 CopyPacketToBuffer(Fragment
->Data
,
424 IPPacket
->NdisPacket
,
425 IPPacket
->HeaderSize
,
427 Fragment
->Offset
= FragFirst
;
429 /* If this is the last fragment, compute and save the datagram data size */
431 IPDR
->DataSize
= FragFirst
+ Fragment
->Size
;
433 /* Put the fragment in the list */
434 InsertTailList(&IPDR
->FragmentListHead
, &Fragment
->ListEntry
);
438 TI_DbgPrint(DEBUG_IP
, ("Done searching for hole descriptor.\n"));
440 if (IsListEmpty(&IPDR
->HoleListHead
)) {
441 /* Hole list is empty which means a complete datagram can be assembled.
442 Assemble the datagram and pass it to an upper layer protocol */
444 TI_DbgPrint(DEBUG_IP
, ("Complete datagram received.\n"));
446 /* FIXME: Assumes IPv4 */
447 IPInitializePacket(&Datagram
, IP_ADDRESS_V4
);
449 Success
= ReassembleDatagram(&Datagram
, IPDR
);
452 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
457 /* Not enough free resources, discard the packet */
460 DISPLAY_IP_PACKET(&Datagram
);
462 /* Give the packet to the protocol dispatcher */
463 IPDispatchProtocol(IF
, &Datagram
);
465 IF
->Stats
.InBytes
+= Datagram
.TotalSize
;
467 /* We're done with this datagram */
468 ExFreePoolWithTag(Datagram
.Header
, PACKET_BUFFER_TAG
);
469 TI_DbgPrint(MAX_TRACE
, ("Freeing datagram at (0x%X).\n", Datagram
));
470 (*Datagram
.Free
)(&Datagram
);
472 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
476 VOID
IPFreeReassemblyList(
479 * FUNCTION: Frees all IP datagram reassembly structures in the list
483 PLIST_ENTRY CurrentEntry
;
484 PIPDATAGRAM_REASSEMBLY Current
;
486 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
488 CurrentEntry
= ReassemblyListHead
.Flink
;
489 while (CurrentEntry
!= &ReassemblyListHead
) {
490 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
491 /* Unlink it from the list */
492 RemoveEntryList(CurrentEntry
);
494 /* And free the descriptor */
497 CurrentEntry
= CurrentEntry
->Flink
;
500 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
504 VOID
IPDatagramReassemblyTimeout(
507 * FUNCTION: IP datagram reassembly timeout handler
509 * This routine is called by IPTimeout to free any resources used
510 * to hold IP fragments that have taken too long to reassemble
514 PLIST_ENTRY CurrentEntry
, NextEntry
;
515 PIPDATAGRAM_REASSEMBLY CurrentIPDR
;
517 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
519 CurrentEntry
= ReassemblyListHead
.Flink
;
520 while (CurrentEntry
!= &ReassemblyListHead
)
522 NextEntry
= CurrentEntry
->Flink
;
523 CurrentIPDR
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
525 TcpipAcquireSpinLockAtDpcLevel(&CurrentIPDR
->Lock
);
527 if (++CurrentIPDR
->TimeoutCount
== MAX_TIMEOUT_COUNT
)
529 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
530 RemoveEntryList(CurrentEntry
);
531 FreeIPDR(CurrentIPDR
);
535 ASSERT(CurrentIPDR
->TimeoutCount
< MAX_TIMEOUT_COUNT
);
536 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
539 CurrentEntry
= NextEntry
;
542 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
545 VOID
IPv4Receive(PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
547 * FUNCTION: Receives an IPv4 datagram (or fragment)
549 * Context = Pointer to context information (IP_INTERFACE)
550 * IPPacket = Pointer to IP packet
553 TI_DbgPrint(DEBUG_IP
, ("Received IPv4 datagram.\n"));
555 IPPacket
->HeaderSize
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
& 0x0F) << 2;
556 TI_DbgPrint(DEBUG_IP
, ("IPPacket->HeaderSize = %d\n", IPPacket
->HeaderSize
));
558 if (IPPacket
->HeaderSize
> IPv4_MAX_HEADER_SIZE
) {
561 ("Datagram received with incorrect header size (%d).\n",
562 IPPacket
->HeaderSize
));
567 /* Checksum IPv4 header */
568 if (!IPv4CorrectChecksum(IPPacket
->Header
, IPPacket
->HeaderSize
)) {
571 ("Datagram received with bad checksum. Checksum field (0x%X)\n",
572 WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->Checksum
)));
577 IPPacket
->TotalSize
= WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->TotalLength
);
579 AddrInitIPv4(&IPPacket
->SrcAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->SrcAddr
);
580 AddrInitIPv4(&IPPacket
->DstAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->DstAddr
);
582 IPPacket
->Position
+= IPPacket
->HeaderSize
;
583 IPPacket
->Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPPacket
->HeaderSize
);
585 TI_DbgPrint(MID_TRACE
,("IPPacket->Position = %d\n",
586 IPPacket
->Position
));
588 //OskitDumpBuffer(IPPacket->Header, IPPacket->TotalSize);
590 /* FIXME: Possibly forward packets with multicast addresses */
592 /* FIXME: Should we allow packets to be received on the wrong interface? */
593 /* XXX Find out if this packet is destined for us */
594 ProcessFragment(IF
, IPPacket
);
597 /* This packet is not destined for us. If we are a router,
598 try to find a route and forward the packet */
600 /* FIXME: Check if acting as a router */
603 PROUTE_CACHE_NODE RCN
;
605 /* FIXME: Possibly fragment datagram */
606 /* Forward the packet */
607 if(!RouteGetRouteToDestination( &IPPacket
->DstAddr
, NULL
, &RCN
))
608 IPSendDatagram(IPPacket
, RCN
, ReflectPacketComplete
, IPPacket
);
610 TI_DbgPrint(MIN_TRACE
, ("No route to destination (0x%X).\n",
611 IPPacket
->DstAddr
.Address
.IPv4Address
));
613 /* FIXME: Send ICMP error code */
620 VOID
IPReceive( PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
622 * FUNCTION: Receives an IP datagram (or fragment)
625 * IPPacket = Pointer to IP packet
630 /* Check that IP header has a supported version */
631 Version
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
>> 4);
635 IPPacket
->Type
= IP_ADDRESS_V4
;
636 IPv4Receive(IF
, IPPacket
);
639 IPPacket
->Type
= IP_ADDRESS_V6
;
640 TI_DbgPrint(MAX_TRACE
, ("Datagram of type IPv6 discarded.\n"));
643 TI_DbgPrint(MIN_TRACE
, ("Datagram has an unsupported IP version %d.\n", Version
));
647 IPPacket
->Free(IPPacket
);