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 TI_DbgPrint(DEBUG_IP
, ("Freeing IPDR data at (0x%X).\n", IPDR
));
113 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
118 PIPDATAGRAM_REASSEMBLY IPDR
)
120 * FUNCTION: Removes an IP datagram reassembly structure from the global list
122 * IPDR = Pointer to IP datagram reassembly structure
127 TI_DbgPrint(DEBUG_IP
, ("Removing IPDR at (0x%X).\n", IPDR
));
129 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
130 RemoveEntryList(&IPDR
->ListEntry
);
131 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
135 PIPDATAGRAM_REASSEMBLY
GetReassemblyInfo(
138 * FUNCTION: Returns a pointer to an IP datagram reassembly structure
140 * IPPacket = Pointer to IP packet
142 * A datagram is identified by four paramters, which are
143 * Source and destination address, protocol number and
144 * identification number
148 PLIST_ENTRY CurrentEntry
;
149 PIPDATAGRAM_REASSEMBLY Current
;
150 PIPv4_HEADER Header
= (PIPv4_HEADER
)IPPacket
->Header
;
152 TI_DbgPrint(DEBUG_IP
, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket
));
154 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
156 /* FIXME: Assume IPv4 */
158 CurrentEntry
= ReassemblyListHead
.Flink
;
159 while (CurrentEntry
!= &ReassemblyListHead
) {
160 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
161 if (AddrIsEqual(&IPPacket
->SrcAddr
, &Current
->SrcAddr
) &&
162 (Header
->Id
== Current
->Id
) &&
163 (Header
->Protocol
== Current
->Protocol
) &&
164 (AddrIsEqual(&IPPacket
->DstAddr
, &Current
->DstAddr
))) {
165 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
169 CurrentEntry
= CurrentEntry
->Flink
;
172 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
181 PIPDATAGRAM_REASSEMBLY IPDR
)
183 * FUNCTION: Reassembles an IP datagram
185 * IPDR = Pointer to IP datagram reassembly structure
187 * This routine concatenates fragments into a complete IP datagram.
188 * The lock is held when this routine is called
190 * Pointer to IP packet, NULL if there was not enough free resources
192 * At this point, header is expected to point to the IP header
195 PLIST_ENTRY CurrentEntry
;
196 PIP_FRAGMENT Fragment
;
201 TI_DbgPrint(DEBUG_IP
, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR
));
202 TI_DbgPrint(DEBUG_IP
, ("IPDR->HeaderSize = %d\n", IPDR
->HeaderSize
));
203 TI_DbgPrint(DEBUG_IP
, ("IPDR->DataSize = %d\n", IPDR
->DataSize
));
205 IPPacket
->TotalSize
= IPDR
->HeaderSize
+ IPDR
->DataSize
;
206 IPPacket
->HeaderSize
= IPDR
->HeaderSize
;
208 RtlCopyMemory(&IPPacket
->SrcAddr
, &IPDR
->SrcAddr
, sizeof(IP_ADDRESS
));
209 RtlCopyMemory(&IPPacket
->DstAddr
, &IPDR
->DstAddr
, sizeof(IP_ADDRESS
));
211 /* Allocate space for full IP datagram */
212 IPPacket
->Header
= ExAllocatePoolWithTag(PagedPool
, IPPacket
->TotalSize
, PACKET_BUFFER_TAG
);
213 if (!IPPacket
->Header
) {
214 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
215 (*IPPacket
->Free
)(IPPacket
);
218 IPPacket
->MappedHeader
= FALSE
;
220 /* Copy the header into the buffer */
221 RtlCopyMemory(IPPacket
->Header
, &IPDR
->IPv4Header
, sizeof(IPDR
->IPv4Header
));
223 Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPDR
->HeaderSize
);
224 IPPacket
->Data
= Data
;
226 /* Copy data from all fragments into buffer */
227 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
228 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
229 Fragment
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
231 /* Copy fragment data into datagram buffer */
232 CopyPacketToBuffer(Data
+ Fragment
->Offset
,
234 Fragment
->PacketOffset
,
237 CurrentEntry
= CurrentEntry
->Flink
;
244 __inline VOID
Cleanup(
247 PIPDATAGRAM_REASSEMBLY IPDR
)
249 * FUNCTION: Performs cleaning operations on errors
251 * Lock = Pointer to spin lock to be released
252 * OldIrql = Value of IRQL when spin lock was acquired
253 * IPDR = Pointer to IP datagram reassembly structure to free
254 * Buffer = Optional pointer to a buffer to free
257 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
259 TcpipReleaseSpinLock(Lock
, OldIrql
);
265 VOID
ProcessFragment(
269 * FUNCTION: Processes an IP datagram or fragment
271 * IF = Pointer to IP interface packet was receive on
272 * IPPacket = Pointer to IP packet
274 * This routine reassembles fragments and, if a whole datagram can
275 * be assembled, passes the datagram on to the IP protocol dispatcher
279 PIPDATAGRAM_REASSEMBLY IPDR
;
280 PLIST_ENTRY CurrentEntry
;
281 PIPDATAGRAM_HOLE Hole
, NewHole
;
284 BOOLEAN MoreFragments
;
285 PIPv4_HEADER IPv4Header
;
287 PIP_FRAGMENT Fragment
;
290 /* FIXME: Assume IPv4 */
292 IPv4Header
= (PIPv4_HEADER
)IPPacket
->Header
;
294 /* Check if we already have an reassembly structure for this datagram */
295 IPDR
= GetReassemblyInfo(IPPacket
);
297 TI_DbgPrint(DEBUG_IP
, ("Continueing assembly.\n"));
298 /* We have a reassembly structure */
299 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
301 /* Reset the timeout since we received a fragment */
302 IPDR
->TimeoutCount
= 0;
304 TI_DbgPrint(DEBUG_IP
, ("Starting new assembly.\n"));
306 /* We don't have a reassembly structure, create one */
307 IPDR
= ExAllocateFromNPagedLookasideList(&IPDRList
);
309 /* We don't have the resources to process this packet, discard it */
312 /* Create a descriptor spanning from zero to infinity.
313 Actually, we use a value slightly greater than the
314 maximum number of octets an IP datagram can contain */
315 Hole
= CreateHoleDescriptor(0, 65536);
317 /* We don't have the resources to process this packet, discard it */
318 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
321 AddrInitIPv4(&IPDR
->SrcAddr
, IPv4Header
->SrcAddr
);
322 AddrInitIPv4(&IPDR
->DstAddr
, IPv4Header
->DstAddr
);
323 IPDR
->Id
= IPv4Header
->Id
;
324 IPDR
->Protocol
= IPv4Header
->Protocol
;
325 IPDR
->TimeoutCount
= 0;
326 InitializeListHead(&IPDR
->FragmentListHead
);
327 InitializeListHead(&IPDR
->HoleListHead
);
328 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
330 TcpipInitializeSpinLock(&IPDR
->Lock
);
332 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
334 /* Update the reassembly list */
335 TcpipInterlockedInsertTailList(
338 &ReassemblyListLock
);
341 FragFirst
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_FRAGOFS_MASK
) << 3;
342 FragLast
= FragFirst
+ WN2H(IPv4Header
->TotalLength
);
343 MoreFragments
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_MF_MASK
) > 0;
345 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
347 if (CurrentEntry
== &IPDR
->HoleListHead
)
350 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
352 TI_DbgPrint(DEBUG_IP
, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
353 FragFirst
, FragLast
, Hole
->First
, Hole
->Last
));
355 if ((FragFirst
> Hole
->Last
) || (FragLast
< Hole
->First
)) {
356 TI_DbgPrint(MID_TRACE
, ("No overlap.\n"));
357 /* The fragment does not overlap with the hole, try next
358 descriptor in the list */
360 CurrentEntry
= CurrentEntry
->Flink
;
364 /* The fragment overlap with the hole, unlink the descriptor */
365 RemoveEntryList(CurrentEntry
);
367 if (FragFirst
> Hole
->First
) {
368 NewHole
= CreateHoleDescriptor(Hole
->First
, FragFirst
- 1);
370 /* We don't have the resources to process this packet, discard it */
371 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
372 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
376 /* Put the new descriptor in the list */
377 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
380 if ((FragLast
< Hole
->Last
) && MoreFragments
) {
381 NewHole
= CreateHoleDescriptor(FragLast
+ 1, Hole
->Last
);
383 /* We don't have the resources to process this packet, discard it */
384 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
385 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
389 /* Put the new hole descriptor in the list */
390 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
393 ExFreeToNPagedLookasideList(&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
, sizeof(IPDR
->IPv4Header
));
401 IPDR
->HeaderSize
= sizeof(IPDR
->IPv4Header
);
404 /* Create a buffer, copy the data into it and put it
405 in the fragment list */
407 Fragment
= ExAllocateFromNPagedLookasideList(&IPFragmentList
);
409 /* We don't have the resources to process this packet, discard it */
410 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
414 TI_DbgPrint(DEBUG_IP
, ("Fragment descriptor allocated at (0x%X).\n", Fragment
));
416 Fragment
->Size
= IPPacket
->TotalSize
- IPPacket
->HeaderSize
;
417 Fragment
->Packet
= IPPacket
->NdisPacket
;
418 Fragment
->ReturnPacket
= IPPacket
->ReturnPacket
;
419 Fragment
->PacketOffset
= IPPacket
->Position
+ IPPacket
->HeaderSize
;
420 Fragment
->Offset
= FragFirst
;
422 /* Disassociate the NDIS packet so it isn't freed upon return from IPReceive() */
423 IPPacket
->NdisPacket
= NULL
;
425 /* If this is the last fragment, compute and save the datagram data size */
427 IPDR
->DataSize
= FragFirst
+ Fragment
->Size
;
429 /* Put the fragment in the list */
430 InsertTailList(&IPDR
->FragmentListHead
, &Fragment
->ListEntry
);
434 TI_DbgPrint(DEBUG_IP
, ("Done searching for hole descriptor.\n"));
436 if (IsListEmpty(&IPDR
->HoleListHead
)) {
437 /* Hole list is empty which means a complete datagram can be assembled.
438 Assemble the datagram and pass it to an upper layer protocol */
440 TI_DbgPrint(DEBUG_IP
, ("Complete datagram received.\n"));
443 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
445 /* FIXME: Assumes IPv4 */
446 IPInitializePacket(&Datagram
, IP_ADDRESS_V4
);
448 Success
= ReassembleDatagram(&Datagram
, IPDR
);
453 /* Not enough free resources, discard the packet */
456 DISPLAY_IP_PACKET(&Datagram
);
458 /* Give the packet to the protocol dispatcher */
459 IPDispatchProtocol(IF
, &Datagram
);
461 /* We're done with this datagram */
462 TI_DbgPrint(MAX_TRACE
, ("Freeing datagram at (0x%X).\n", Datagram
));
463 Datagram
.Free(&Datagram
);
465 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
469 VOID
IPFreeReassemblyList(
472 * FUNCTION: Frees all IP datagram reassembly structures in the list
476 PLIST_ENTRY CurrentEntry
;
477 PIPDATAGRAM_REASSEMBLY Current
;
479 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
481 CurrentEntry
= ReassemblyListHead
.Flink
;
482 while (CurrentEntry
!= &ReassemblyListHead
) {
483 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
484 /* Unlink it from the list */
485 RemoveEntryList(CurrentEntry
);
487 /* And free the descriptor */
490 CurrentEntry
= CurrentEntry
->Flink
;
493 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
497 VOID
IPDatagramReassemblyTimeout(
500 * FUNCTION: IP datagram reassembly timeout handler
502 * This routine is called by IPTimeout to free any resources used
503 * to hold IP fragments that have taken too long to reassemble
506 PLIST_ENTRY CurrentEntry
, NextEntry
;
507 PIPDATAGRAM_REASSEMBLY CurrentIPDR
;
509 TcpipAcquireSpinLockAtDpcLevel(&ReassemblyListLock
);
511 CurrentEntry
= ReassemblyListHead
.Flink
;
512 while (CurrentEntry
!= &ReassemblyListHead
)
514 NextEntry
= CurrentEntry
->Flink
;
515 CurrentIPDR
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
517 TcpipAcquireSpinLockAtDpcLevel(&CurrentIPDR
->Lock
);
519 if (++CurrentIPDR
->TimeoutCount
== MAX_TIMEOUT_COUNT
)
521 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
522 RemoveEntryList(CurrentEntry
);
523 FreeIPDR(CurrentIPDR
);
527 ASSERT(CurrentIPDR
->TimeoutCount
< MAX_TIMEOUT_COUNT
);
528 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
531 CurrentEntry
= NextEntry
;
534 TcpipReleaseSpinLockFromDpcLevel(&ReassemblyListLock
);
537 VOID
IPv4Receive(PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
539 * FUNCTION: Receives an IPv4 datagram (or fragment)
541 * Context = Pointer to context information (IP_INTERFACE)
542 * IPPacket = Pointer to IP packet
548 TI_DbgPrint(DEBUG_IP
, ("Received IPv4 datagram.\n"));
550 /* Read in the first IP header byte for size information */
551 BytesCopied
= CopyPacketToBuffer((PCHAR
)&FirstByte
,
552 IPPacket
->NdisPacket
,
555 if (BytesCopied
!= sizeof(UCHAR
))
557 TI_DbgPrint(MIN_TRACE
, ("Failed to copy in first byte\n"));
562 IPPacket
->HeaderSize
= (FirstByte
& 0x0F) << 2;
563 TI_DbgPrint(DEBUG_IP
, ("IPPacket->HeaderSize = %d\n", IPPacket
->HeaderSize
));
565 if (IPPacket
->HeaderSize
> IPv4_MAX_HEADER_SIZE
) {
566 TI_DbgPrint(MIN_TRACE
, ("Datagram received with incorrect header size (%d).\n",
567 IPPacket
->HeaderSize
));
572 /* This is freed by IPPacket->Free() */
573 IPPacket
->Header
= ExAllocatePoolWithTag(NonPagedPool
,
574 IPPacket
->HeaderSize
,
576 if (!IPPacket
->Header
)
578 TI_DbgPrint(MIN_TRACE
, ("No resources to allocate header\n"));
583 IPPacket
->MappedHeader
= FALSE
;
585 BytesCopied
= CopyPacketToBuffer((PCHAR
)IPPacket
->Header
,
586 IPPacket
->NdisPacket
,
588 IPPacket
->HeaderSize
);
589 if (BytesCopied
!= IPPacket
->HeaderSize
)
591 TI_DbgPrint(MIN_TRACE
, ("Failed to copy in header\n"));
596 /* Checksum IPv4 header */
597 if (!IPv4CorrectChecksum(IPPacket
->Header
, IPPacket
->HeaderSize
)) {
598 TI_DbgPrint(MIN_TRACE
, ("Datagram received with bad checksum. Checksum field (0x%X)\n",
599 WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->Checksum
)));
604 IPPacket
->TotalSize
= WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->TotalLength
);
606 AddrInitIPv4(&IPPacket
->SrcAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->SrcAddr
);
607 AddrInitIPv4(&IPPacket
->DstAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->DstAddr
);
609 TI_DbgPrint(MID_TRACE
,("IPPacket->Position = %d\n",
610 IPPacket
->Position
));
612 /* FIXME: Possibly forward packets with multicast addresses */
614 /* FIXME: Should we allow packets to be received on the wrong interface? */
615 /* XXX Find out if this packet is destined for us */
616 ProcessFragment(IF
, IPPacket
);
620 VOID
IPReceive( PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
622 * FUNCTION: Receives an IP datagram (or fragment)
625 * IPPacket = Pointer to IP packet
629 UINT Version
, BytesCopied
;
631 /* Read in the first IP header byte for version information */
632 BytesCopied
= CopyPacketToBuffer((PCHAR
)&FirstByte
,
633 IPPacket
->NdisPacket
,
636 if (BytesCopied
!= sizeof(UCHAR
))
638 TI_DbgPrint(MIN_TRACE
, ("Failed to copy in first byte\n"));
639 IPPacket
->Free(IPPacket
);
643 /* Check that IP header has a supported version */
644 Version
= (FirstByte
>> 4);
648 IPPacket
->Type
= IP_ADDRESS_V4
;
649 IPv4Receive(IF
, IPPacket
);
652 IPPacket
->Type
= IP_ADDRESS_V6
;
653 TI_DbgPrint(MAX_TRACE
, ("Datagram of type IPv6 discarded.\n"));
656 TI_DbgPrint(MIN_TRACE
, ("Datagram has an unsupported IP version %d.\n", Version
));
660 IPPacket
->Free(IPPacket
);