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 packet at (0x%X).\n", CurrentF
->Packet
));
94 /* Free the fragment data buffer */
95 if (CurrentF
->ReturnPacket
)
97 NdisReturnPackets(&CurrentF
->Packet
, 1);
101 FreeNdisPacket(CurrentF
->Packet
);
104 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment at (0x%X).\n", CurrentF
));
106 /* And free the fragment descriptor */
107 ExFreeToNPagedLookasideList(&IPFragmentList
, CurrentF
);
108 CurrentEntry
= NextEntry
;
111 if (IPDR
->IPv4Header
)
113 TI_DbgPrint(DEBUG_IP
, ("Freeing IPDR header at (0x%X).\n", IPDR
->IPv4Header
));
114 ExFreePoolWithTag(IPDR
->IPv4Header
, PACKET_BUFFER_TAG
);
117 TI_DbgPrint(DEBUG_IP
, ("Freeing IPDR data at (0x%X).\n", IPDR
));
119 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
124 PIPDATAGRAM_REASSEMBLY IPDR
)
126 * FUNCTION: Removes an IP datagram reassembly structure from the global list
128 * IPDR = Pointer to IP datagram reassembly structure
133 TI_DbgPrint(DEBUG_IP
, ("Removing IPDR at (0x%X).\n", IPDR
));
135 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
136 RemoveEntryList(&IPDR
->ListEntry
);
137 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
141 PIPDATAGRAM_REASSEMBLY
GetReassemblyInfo(
144 * FUNCTION: Returns a pointer to an IP datagram reassembly structure
146 * IPPacket = Pointer to IP packet
148 * A datagram is identified by four paramters, which are
149 * Source and destination address, protocol number and
150 * identification number
154 PLIST_ENTRY CurrentEntry
;
155 PIPDATAGRAM_REASSEMBLY Current
;
156 PIPv4_HEADER Header
= (PIPv4_HEADER
)IPPacket
->Header
;
158 TI_DbgPrint(DEBUG_IP
, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket
));
160 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
162 /* FIXME: Assume IPv4 */
164 CurrentEntry
= ReassemblyListHead
.Flink
;
165 while (CurrentEntry
!= &ReassemblyListHead
) {
166 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
167 if (AddrIsEqual(&IPPacket
->SrcAddr
, &Current
->SrcAddr
) &&
168 (Header
->Id
== Current
->Id
) &&
169 (Header
->Protocol
== Current
->Protocol
) &&
170 (AddrIsEqual(&IPPacket
->DstAddr
, &Current
->DstAddr
))) {
171 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
175 CurrentEntry
= CurrentEntry
->Flink
;
178 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
187 PIPDATAGRAM_REASSEMBLY IPDR
)
189 * FUNCTION: Reassembles an IP datagram
191 * IPDR = Pointer to IP datagram reassembly structure
193 * This routine concatenates fragments into a complete IP datagram.
194 * The lock is held when this routine is called
196 * Pointer to IP packet, NULL if there was not enough free resources
198 * At this point, header is expected to point to the IP header
201 PLIST_ENTRY CurrentEntry
;
202 PIP_FRAGMENT Fragment
;
207 TI_DbgPrint(DEBUG_IP
, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR
));
208 TI_DbgPrint(DEBUG_IP
, ("IPDR->HeaderSize = %d\n", IPDR
->HeaderSize
));
209 TI_DbgPrint(DEBUG_IP
, ("IPDR->DataSize = %d\n", IPDR
->DataSize
));
211 IPPacket
->TotalSize
= IPDR
->HeaderSize
+ IPDR
->DataSize
;
212 IPPacket
->HeaderSize
= 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
= ExAllocatePoolWithTag(PagedPool
, IPPacket
->TotalSize
, PACKET_BUFFER_TAG
);
219 if (!IPPacket
->Header
) {
220 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
221 (*IPPacket
->Free
)(IPPacket
);
224 IPPacket
->MappedHeader
= FALSE
;
226 /* Copy the header into the buffer */
227 RtlCopyMemory(IPPacket
->Header
, IPDR
->IPv4Header
, IPDR
->HeaderSize
);
229 Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPDR
->HeaderSize
);
230 IPPacket
->Data
= Data
;
232 /* Copy data from all fragments into buffer */
233 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
234 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
235 Fragment
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
237 /* Copy fragment data into datagram buffer */
238 CopyPacketToBuffer(Data
+ Fragment
->Offset
,
240 Fragment
->PacketOffset
,
243 CurrentEntry
= CurrentEntry
->Flink
;
250 __inline VOID
Cleanup(
253 PIPDATAGRAM_REASSEMBLY IPDR
)
255 * FUNCTION: Performs cleaning operations on errors
257 * Lock = Pointer to spin lock to be released
258 * OldIrql = Value of IRQL when spin lock was acquired
259 * IPDR = Pointer to IP datagram reassembly structure to free
260 * Buffer = Optional pointer to a buffer to free
263 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
265 TcpipReleaseSpinLock(Lock
, OldIrql
);
271 VOID
ProcessFragment(
275 * FUNCTION: Processes an IP datagram or fragment
277 * IF = Pointer to IP interface packet was receive on
278 * IPPacket = Pointer to IP packet
280 * This routine reassembles fragments and, if a whole datagram can
281 * be assembled, passes the datagram on to the IP protocol dispatcher
285 PIPDATAGRAM_REASSEMBLY IPDR
;
286 PLIST_ENTRY CurrentEntry
;
287 PIPDATAGRAM_HOLE Hole
, NewHole
;
290 BOOLEAN MoreFragments
;
291 PIPv4_HEADER IPv4Header
;
293 PIP_FRAGMENT Fragment
;
296 /* FIXME: Assume IPv4 */
298 IPv4Header
= (PIPv4_HEADER
)IPPacket
->Header
;
300 /* Check if we already have an reassembly structure for this datagram */
301 IPDR
= GetReassemblyInfo(IPPacket
);
303 TI_DbgPrint(DEBUG_IP
, ("Continueing assembly.\n"));
304 /* We have a reassembly structure */
305 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
307 /* Reset the timeout since we received a fragment */
308 IPDR
->TimeoutCount
= 0;
310 TI_DbgPrint(DEBUG_IP
, ("Starting new assembly.\n"));
312 /* We don't have a reassembly structure, create one */
313 IPDR
= ExAllocateFromNPagedLookasideList(&IPDRList
);
315 /* We don't have the resources to process this packet, discard it */
318 /* Create a descriptor spanning from zero to infinity.
319 Actually, we use a value slightly greater than the
320 maximum number of octets an IP datagram can contain */
321 Hole
= CreateHoleDescriptor(0, 65536);
323 /* We don't have the resources to process this packet, discard it */
324 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
327 AddrInitIPv4(&IPDR
->SrcAddr
, IPv4Header
->SrcAddr
);
328 AddrInitIPv4(&IPDR
->DstAddr
, IPv4Header
->DstAddr
);
329 IPDR
->Id
= IPv4Header
->Id
;
330 IPDR
->Protocol
= IPv4Header
->Protocol
;
331 IPDR
->TimeoutCount
= 0;
332 InitializeListHead(&IPDR
->FragmentListHead
);
333 InitializeListHead(&IPDR
->HoleListHead
);
334 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
336 TcpipInitializeSpinLock(&IPDR
->Lock
);
338 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
340 /* Update the reassembly list */
341 TcpipInterlockedInsertTailList(
344 &ReassemblyListLock
);
347 FragFirst
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_FRAGOFS_MASK
) << 3;
348 FragLast
= FragFirst
+ WN2H(IPv4Header
->TotalLength
);
349 MoreFragments
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_MF_MASK
) > 0;
351 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
353 if (CurrentEntry
== &IPDR
->HoleListHead
)
356 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
358 TI_DbgPrint(DEBUG_IP
, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
359 FragFirst
, FragLast
, Hole
->First
, Hole
->Last
));
361 if ((FragFirst
> Hole
->Last
) || (FragLast
< Hole
->First
)) {
362 TI_DbgPrint(MID_TRACE
, ("No overlap.\n"));
363 /* The fragment does not overlap with the hole, try next
364 descriptor in the list */
366 CurrentEntry
= CurrentEntry
->Flink
;
370 /* The fragment overlap with the hole, unlink the descriptor */
371 RemoveEntryList(CurrentEntry
);
373 if (FragFirst
> Hole
->First
) {
374 NewHole
= CreateHoleDescriptor(Hole
->First
, FragFirst
- 1);
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 descriptor in the list */
383 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
386 if ((FragLast
< Hole
->Last
) && MoreFragments
) {
387 NewHole
= CreateHoleDescriptor(FragLast
+ 1, Hole
->Last
);
389 /* We don't have the resources to process this packet, discard it */
390 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
391 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
395 /* Put the new hole descriptor in the list */
396 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
399 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
401 /* If this is the first fragment, save the IP header */
402 if (FragFirst
== 0) {
403 IPDR
->IPv4Header
= ExAllocatePoolWithTag(NonPagedPool
,
404 IPPacket
->HeaderSize
,
406 if (!IPDR
->IPv4Header
)
408 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
412 RtlCopyMemory(IPDR
->IPv4Header
, IPPacket
->Header
, IPPacket
->HeaderSize
);
413 IPDR
->HeaderSize
= IPPacket
->HeaderSize
;
415 TI_DbgPrint(DEBUG_IP
, ("First fragment found. Header buffer is at (0x%X). "
416 "Header size is (%d).\n", &IPDR
->IPv4Header
, IPPacket
->HeaderSize
));
420 /* Create a buffer, copy the data into it and put it
421 in the fragment list */
423 Fragment
= ExAllocateFromNPagedLookasideList(&IPFragmentList
);
425 /* We don't have the resources to process this packet, discard it */
426 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
430 TI_DbgPrint(DEBUG_IP
, ("Fragment descriptor allocated at (0x%X).\n", Fragment
));
432 Fragment
->Size
= IPPacket
->TotalSize
- IPPacket
->HeaderSize
;
433 Fragment
->Packet
= IPPacket
->NdisPacket
;
434 Fragment
->ReturnPacket
= IPPacket
->ReturnPacket
;
435 Fragment
->PacketOffset
= IPPacket
->Position
+ IPPacket
->HeaderSize
;
436 Fragment
->Offset
= FragFirst
;
438 /* Disassociate the NDIS packet so it isn't freed upon return from IPReceive() */
439 IPPacket
->NdisPacket
= NULL
;
441 /* If this is the last fragment, compute and save the datagram data size */
443 IPDR
->DataSize
= FragFirst
+ Fragment
->Size
;
445 /* Put the fragment in the list */
446 InsertTailList(&IPDR
->FragmentListHead
, &Fragment
->ListEntry
);
450 TI_DbgPrint(DEBUG_IP
, ("Done searching for hole descriptor.\n"));
452 if (IsListEmpty(&IPDR
->HoleListHead
)) {
453 /* Hole list is empty which means a complete datagram can be assembled.
454 Assemble the datagram and pass it to an upper layer protocol */
456 TI_DbgPrint(DEBUG_IP
, ("Complete datagram received.\n"));
459 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
461 /* FIXME: Assumes IPv4 */
462 IPInitializePacket(&Datagram
, IP_ADDRESS_V4
);
464 Success
= ReassembleDatagram(&Datagram
, IPDR
);
469 /* Not enough free resources, discard the packet */
472 DISPLAY_IP_PACKET(&Datagram
);
474 /* Give the packet to the protocol dispatcher */
475 IPDispatchProtocol(IF
, &Datagram
);
477 /* We're done with this datagram */
478 TI_DbgPrint(MAX_TRACE
, ("Freeing datagram at (0x%X).\n", Datagram
));
479 Datagram
.Free(&Datagram
);
481 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
485 VOID
IPFreeReassemblyList(
488 * FUNCTION: Frees all IP datagram reassembly structures in the list
492 PLIST_ENTRY CurrentEntry
;
493 PIPDATAGRAM_REASSEMBLY Current
;
495 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
497 CurrentEntry
= ReassemblyListHead
.Flink
;
498 while (CurrentEntry
!= &ReassemblyListHead
) {
499 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
500 /* Unlink it from the list */
501 RemoveEntryList(CurrentEntry
);
503 /* And free the descriptor */
506 CurrentEntry
= CurrentEntry
->Flink
;
509 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
513 VOID
IPDatagramReassemblyTimeout(
516 * FUNCTION: IP datagram reassembly timeout handler
518 * This routine is called by IPTimeout to free any resources used
519 * to hold IP fragments that have taken too long to reassemble
522 PLIST_ENTRY CurrentEntry
, NextEntry
;
523 PIPDATAGRAM_REASSEMBLY CurrentIPDR
;
525 TcpipAcquireSpinLockAtDpcLevel(&ReassemblyListLock
);
527 CurrentEntry
= ReassemblyListHead
.Flink
;
528 while (CurrentEntry
!= &ReassemblyListHead
)
530 NextEntry
= CurrentEntry
->Flink
;
531 CurrentIPDR
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
533 TcpipAcquireSpinLockAtDpcLevel(&CurrentIPDR
->Lock
);
535 if (++CurrentIPDR
->TimeoutCount
== MAX_TIMEOUT_COUNT
)
537 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
538 RemoveEntryList(CurrentEntry
);
539 FreeIPDR(CurrentIPDR
);
543 ASSERT(CurrentIPDR
->TimeoutCount
< MAX_TIMEOUT_COUNT
);
544 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
547 CurrentEntry
= NextEntry
;
550 TcpipReleaseSpinLockFromDpcLevel(&ReassemblyListLock
);
553 VOID
IPv4Receive(PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
555 * FUNCTION: Receives an IPv4 datagram (or fragment)
557 * Context = Pointer to context information (IP_INTERFACE)
558 * IPPacket = Pointer to IP packet
564 TI_DbgPrint(DEBUG_IP
, ("Received IPv4 datagram.\n"));
566 /* Read in the first IP header byte for size information */
567 BytesCopied
= CopyPacketToBuffer((PCHAR
)&FirstByte
,
568 IPPacket
->NdisPacket
,
571 if (BytesCopied
!= sizeof(UCHAR
))
573 TI_DbgPrint(MIN_TRACE
, ("Failed to copy in first byte\n"));
578 IPPacket
->HeaderSize
= (FirstByte
& 0x0F) << 2;
579 TI_DbgPrint(DEBUG_IP
, ("IPPacket->HeaderSize = %d\n", IPPacket
->HeaderSize
));
581 if (IPPacket
->HeaderSize
> IPv4_MAX_HEADER_SIZE
) {
582 TI_DbgPrint(MIN_TRACE
, ("Datagram received with incorrect header size (%d).\n",
583 IPPacket
->HeaderSize
));
588 /* This is freed by IPPacket->Free() */
589 IPPacket
->Header
= ExAllocatePoolWithTag(NonPagedPool
,
590 IPPacket
->HeaderSize
,
592 if (!IPPacket
->Header
)
594 TI_DbgPrint(MIN_TRACE
, ("No resources to allocate header\n"));
599 IPPacket
->MappedHeader
= FALSE
;
601 BytesCopied
= CopyPacketToBuffer((PCHAR
)IPPacket
->Header
,
602 IPPacket
->NdisPacket
,
604 IPPacket
->HeaderSize
);
605 if (BytesCopied
!= IPPacket
->HeaderSize
)
607 TI_DbgPrint(MIN_TRACE
, ("Failed to copy in header\n"));
612 /* Checksum IPv4 header */
613 if (!IPv4CorrectChecksum(IPPacket
->Header
, IPPacket
->HeaderSize
)) {
614 TI_DbgPrint(MIN_TRACE
, ("Datagram received with bad checksum. Checksum field (0x%X)\n",
615 WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->Checksum
)));
620 IPPacket
->TotalSize
= WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->TotalLength
);
622 AddrInitIPv4(&IPPacket
->SrcAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->SrcAddr
);
623 AddrInitIPv4(&IPPacket
->DstAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->DstAddr
);
625 TI_DbgPrint(MID_TRACE
,("IPPacket->Position = %d\n",
626 IPPacket
->Position
));
628 /* FIXME: Possibly forward packets with multicast addresses */
630 /* FIXME: Should we allow packets to be received on the wrong interface? */
631 /* XXX Find out if this packet is destined for us */
632 ProcessFragment(IF
, IPPacket
);
636 VOID
IPReceive( PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
638 * FUNCTION: Receives an IP datagram (or fragment)
641 * IPPacket = Pointer to IP packet
645 UINT Version
, BytesCopied
;
647 /* Read in the first IP header byte for version information */
648 BytesCopied
= CopyPacketToBuffer((PCHAR
)&FirstByte
,
649 IPPacket
->NdisPacket
,
652 if (BytesCopied
!= sizeof(UCHAR
))
654 TI_DbgPrint(MIN_TRACE
, ("Failed to copy in first byte\n"));
655 IPPacket
->Free(IPPacket
);
659 /* Check that IP header has a supported version */
660 Version
= (FirstByte
>> 4);
664 IPPacket
->Type
= IP_ADDRESS_V4
;
665 IPv4Receive(IF
, IPPacket
);
668 IPPacket
->Type
= IP_ADDRESS_V6
;
669 TI_DbgPrint(MAX_TRACE
, ("Datagram of type IPv6 discarded.\n"));
672 TI_DbgPrint(MIN_TRACE
, ("Datagram has an unsupported IP version %d.\n", Version
));
676 IPPacket
->Free(IPPacket
);