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
22 LIST_ENTRY ReassemblyListHead
;
23 KSPIN_LOCK ReassemblyListLock
;
26 PIPDATAGRAM_HOLE
CreateHoleDescriptor(
30 * FUNCTION: Returns a pointer to a IP datagram hole descriptor
32 * First = Offset of first octet of the hole
33 * Last = Offset of last octet of the hole
35 * Pointer to descriptor, NULL if there was not enough free
39 PIPDATAGRAM_HOLE Hole
;
41 TI_DbgPrint(DEBUG_IP
, ("Called. First (%d) Last (%d).\n", First
, Last
));
43 Hole
= PoolAllocateBuffer(sizeof(IPDATAGRAM_HOLE
));
45 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
52 TI_DbgPrint(DEBUG_IP
, ("Returning hole descriptor at (0x%X).\n", Hole
));
59 PIPDATAGRAM_REASSEMBLY IPDR
)
61 * FUNCTION: Frees an IP datagram reassembly structure
63 * IPDR = Pointer to IP datagram reassembly structure
66 PLIST_ENTRY CurrentEntry
;
67 PLIST_ENTRY NextEntry
;
68 PIPDATAGRAM_HOLE CurrentH
;
69 PIP_FRAGMENT CurrentF
;
71 TI_DbgPrint(DEBUG_IP
, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR
));
73 /* Free all descriptors */
74 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
75 while (CurrentEntry
!= &IPDR
->HoleListHead
) {
76 NextEntry
= CurrentEntry
->Flink
;
77 CurrentH
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
78 /* Unlink it from the list */
79 RemoveEntryList(CurrentEntry
);
81 TI_DbgPrint(DEBUG_IP
, ("Freeing hole descriptor at (0x%X).\n", CurrentH
));
83 /* And free the hole descriptor */
84 PoolFreeBuffer(CurrentH
);
86 CurrentEntry
= NextEntry
;
89 /* Free all fragments */
90 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
91 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
92 NextEntry
= CurrentEntry
->Flink
;
93 CurrentF
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
94 /* Unlink it from the list */
95 RemoveEntryList(CurrentEntry
);
97 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment data at (0x%X).\n", CurrentF
->Data
));
99 /* Free the fragment data buffer */
100 ExFreePool(CurrentF
->Data
);
102 TI_DbgPrint(DEBUG_IP
, ("Freeing fragment at (0x%X).\n", CurrentF
));
104 /* And free the fragment descriptor */
105 PoolFreeBuffer(CurrentF
);
106 CurrentEntry
= NextEntry
;
109 /* Free resources for the header, if it exists */
110 if (IPDR
->IPv4Header
) {
111 TI_DbgPrint(DEBUG_IP
, ("Freeing IPv4 header data at (0x%X).\n", IPDR
->IPv4Header
));
112 ExFreePool(IPDR
->IPv4Header
);
115 TI_DbgPrint(DEBUG_IP
, ("Freeing IPDR data at (0x%X).\n", IPDR
));
117 PoolFreeBuffer(IPDR
);
122 PIPDATAGRAM_REASSEMBLY IPDR
)
124 * FUNCTION: Removes an IP datagram reassembly structure from the global list
126 * IPDR = Pointer to IP datagram reassembly structure
131 TI_DbgPrint(DEBUG_IP
, ("Removing IPDR at (0x%X).\n", IPDR
));
133 KeAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
134 RemoveEntryList(&IPDR
->ListEntry
);
135 KeReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
139 PIPDATAGRAM_REASSEMBLY
GetReassemblyInfo(
142 * FUNCTION: Returns a pointer to an IP datagram reassembly structure
144 * IPPacket = Pointer to IP packet
146 * A datagram is identified by four paramters, which are
147 * Source and destination address, protocol number and
148 * identification number
152 PLIST_ENTRY CurrentEntry
;
153 PIPDATAGRAM_REASSEMBLY Current
;
154 PIPv4_HEADER Header
= (PIPv4_HEADER
)IPPacket
->Header
;
156 TI_DbgPrint(DEBUG_IP
, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket
));
158 KeAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
160 /* FIXME: Assume IPv4 */
162 CurrentEntry
= ReassemblyListHead
.Flink
;
163 while (CurrentEntry
!= &ReassemblyListHead
) {
164 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
165 if (AddrIsEqual(&IPPacket
->SrcAddr
, &Current
->SrcAddr
) &&
166 (Header
->Id
== Current
->Id
) &&
167 (Header
->Protocol
== Current
->Protocol
) &&
168 (AddrIsEqual(&IPPacket
->DstAddr
, &Current
->DstAddr
))) {
169 KeReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
173 CurrentEntry
= CurrentEntry
->Flink
;
176 KeReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
182 PIP_PACKET
ReassembleDatagram(
183 PIPDATAGRAM_REASSEMBLY IPDR
)
185 * FUNCTION: Reassembles an IP datagram
187 * IPDR = Pointer to IP datagram reassembly structure
189 * This routine concatenates fragments into a complete IP datagram.
190 * The lock is held when this routine is called
192 * Pointer to IP packet, NULL if there was not enough free resources
196 PLIST_ENTRY CurrentEntry
;
197 PIP_FRAGMENT Current
;
200 TI_DbgPrint(DEBUG_IP
, ("Reassembling datagram from IPDR at (0x%X).\n", IPDR
));
202 IPPacket
= PoolAllocateBuffer(sizeof(IP_PACKET
));
204 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
208 /* FIXME: Assume IPv4 */
210 IPPacket
->Type
= IP_ADDRESS_V4
;
211 IPPacket
->RefCount
= 1;
212 IPPacket
->TotalSize
= IPDR
->HeaderSize
+ IPDR
->DataSize
;
213 IPPacket
->HeaderSize
= IPDR
->HeaderSize
;
214 IPPacket
->Position
= IPDR
->HeaderSize
;
216 RtlCopyMemory(&IPPacket
->SrcAddr
, &IPDR
->SrcAddr
, sizeof(IP_ADDRESS
));
217 RtlCopyMemory(&IPPacket
->DstAddr
, &IPDR
->DstAddr
, sizeof(IP_ADDRESS
));
219 /* Allocate space for full IP datagram */
220 IPPacket
->Header
= ExAllocatePool(NonPagedPool
, IPPacket
->TotalSize
);
221 if (!IPPacket
->Header
) {
222 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
223 PoolFreeBuffer(IPPacket
);
227 /* Copy the header into the buffer */
228 RtlCopyMemory(IPPacket
->Header
, IPDR
->IPv4Header
, IPDR
->HeaderSize
);
230 Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPDR
->HeaderSize
);
231 IPPacket
->Data
= Data
;
233 /* Copy data from all fragments into buffer */
234 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
235 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
236 Current
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
238 TI_DbgPrint(DEBUG_IP
, ("Copying (%d) bytes of fragment data from (0x%X) to offset (%d).\n",
239 Current
->Size
, Data
, Current
->Offset
));
240 /* Copy fragment data to the destination buffer at the correct offset */
241 RtlCopyMemory((PVOID
)((ULONG_PTR
)Data
+ Current
->Offset
),
245 CurrentEntry
= CurrentEntry
->Flink
;
252 __inline VOID
Cleanup(
255 PIPDATAGRAM_REASSEMBLY IPDR
,
256 PVOID Buffer OPTIONAL
)
258 * FUNCTION: Performs cleaning operations on errors
260 * Lock = Pointer to spin lock to be released
261 * OldIrql = Value of IRQL when spin lock was acquired
262 * IPDR = Pointer to IP datagram reassembly structure to free
263 * Buffer = Optional pointer to a buffer to free
266 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
268 KeReleaseSpinLock(Lock
, OldIrql
);
272 PoolFreeBuffer(Buffer
);
276 VOID
ProcessFragment(
279 PNET_TABLE_ENTRY NTE
)
281 * FUNCTION: Processes an IP datagram or fragment
283 * IF = Pointer to IP interface packet was receive on
284 * IPPacket = Pointer to IP packet
285 * NTE = Pointer to NTE packet was received on
287 * This routine reassembles fragments and, if a whole datagram can
288 * be assembled, passes the datagram on to the IP protocol dispatcher
292 PIPDATAGRAM_REASSEMBLY IPDR
;
293 PLIST_ENTRY CurrentEntry
;
294 PIPDATAGRAM_HOLE Hole
, NewHole
;
297 BOOLEAN MoreFragments
;
298 PIPv4_HEADER IPv4Header
;
300 PIP_FRAGMENT Fragment
;
302 /* FIXME: Assume IPv4 */
304 IPv4Header
= (PIPv4_HEADER
)IPPacket
->Header
;
306 /* Check if we already have an reassembly structure for this datagram */
307 IPDR
= GetReassemblyInfo(IPPacket
);
309 TI_DbgPrint(DEBUG_IP
, ("Continueing assembly.\n"));
310 /* We have a reassembly structure */
311 KeAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
312 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
313 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
315 TI_DbgPrint(DEBUG_IP
, ("Starting new assembly.\n"));
317 /* We don't have a reassembly structure, create one */
318 IPDR
= PoolAllocateBuffer(sizeof(IPDATAGRAM_REASSEMBLY
));
320 /* We don't have the resources to process this packet, discard it */
321 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
325 /* Create a descriptor spanning from zero to infinity.
326 Actually, we use a value slightly greater than the
327 maximum number of octets an IP datagram can contain */
328 Hole
= CreateHoleDescriptor(0, 65536);
330 /* We don't have the resources to process this packet, discard it */
331 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
332 PoolFreeBuffer(IPDR
);
335 AddrInitIPv4(&IPDR
->SrcAddr
, IPv4Header
->SrcAddr
);
336 AddrInitIPv4(&IPDR
->DstAddr
, IPv4Header
->DstAddr
);
337 IPDR
->Id
= IPv4Header
->Id
;
338 IPDR
->Protocol
= IPv4Header
->Protocol
;
339 IPDR
->IPv4Header
= NULL
;
340 InitializeListHead(&IPDR
->FragmentListHead
);
341 InitializeListHead(&IPDR
->HoleListHead
);
342 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
343 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
345 KeInitializeSpinLock(&IPDR
->Lock
);
347 KeAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
349 /* Update the reassembly list */
350 ExInterlockedInsertTailList(&ReassemblyListHead
,
352 &ReassemblyListLock
);
355 FragFirst
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_FRAGOFS_MASK
) << 3;
356 FragLast
= FragFirst
+ WN2H(IPv4Header
->TotalLength
);
357 MoreFragments
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_MF_MASK
) > 0;
360 if (CurrentEntry
== &IPDR
->HoleListHead
)
361 /* No more entries */
364 TI_DbgPrint(DEBUG_IP
, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
365 FragFirst
, FragLast
, Hole
->First
, Hole
->Last
));
367 if ((FragFirst
> Hole
->Last
) || (FragLast
< Hole
->First
)) {
368 TI_DbgPrint(MID_TRACE
, ("No overlap.\n"));
369 /* The fragment does not overlap with the hole, try next
370 descriptor in the list */
372 CurrentEntry
= CurrentEntry
->Flink
;
373 if (CurrentEntry
!= &IPDR
->HoleListHead
)
374 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
378 /* The fragment overlap with the hole, unlink the descriptor */
379 RemoveEntryList(CurrentEntry
);
381 if (FragFirst
> Hole
->First
) {
382 NewHole
= CreateHoleDescriptor(Hole
->First
, FragLast
- 1);
384 /* We don't have the resources to process this packet, discard it */
385 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, Hole
);
389 /* Put the new descriptor in the list */
390 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
393 if ((FragLast
< Hole
->Last
) && (MoreFragments
)) {
394 /* We can reuse the descriptor for the new hole */
395 Hole
->First
= FragLast
+ 1;
397 /* Put the new hole descriptor in the list */
398 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
400 PoolFreeBuffer(Hole
);
402 /* If this is the first fragment, save the IP header */
403 if (FragFirst
== 0) {
404 IPDR
->IPv4Header
= ExAllocatePool(NonPagedPool
, IPPacket
->HeaderSize
);
405 if (!IPDR
->IPv4Header
) {
406 /* We don't have the resources to process this packet, discard it */
407 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, NULL
);
411 TI_DbgPrint(DEBUG_IP
, ("First fragment found. Header buffer is at (0x%X). "
412 "Header size is (%d).\n", IPDR
->IPv4Header
, IPPacket
->HeaderSize
));
414 RtlCopyMemory(IPDR
->IPv4Header
, IPPacket
->Header
, IPPacket
->HeaderSize
);
415 IPDR
->HeaderSize
= IPPacket
->HeaderSize
;
418 /* Create a buffer, copy the data into it and put it
419 in the fragment list */
421 Fragment
= PoolAllocateBuffer(sizeof(IP_FRAGMENT
));
423 /* We don't have the resources to process this packet, discard it */
424 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, NULL
);
428 TI_DbgPrint(DEBUG_IP
, ("Fragment descriptor allocated at (0x%X).\n", Fragment
));
430 Fragment
->Size
= IPPacket
->TotalSize
- IPPacket
->HeaderSize
;
431 Fragment
->Data
= ExAllocatePool(NonPagedPool
, Fragment
->Size
);
432 if (!Fragment
->Data
) {
433 /* We don't have the resources to process this packet, discard it */
434 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
, Fragment
);
438 TI_DbgPrint(DEBUG_IP
, ("Fragment data buffer allocated at (0x%X) Size (%d).\n",
439 Fragment
->Data
, Fragment
->Size
));
441 /* Copy datagram data into fragment buffer */
442 CopyPacketToBuffer(Fragment
->Data
,
443 IPPacket
->NdisPacket
,
446 Fragment
->Offset
= FragFirst
;
448 /* If this is the last fragment, compute and save the datagram data size */
450 IPDR
->DataSize
= FragFirst
+ Fragment
->Size
;
452 /* Put the fragment in the list */
453 InsertTailList(&IPDR
->FragmentListHead
, &Fragment
->ListEntry
);
457 TI_DbgPrint(DEBUG_IP
, ("Done searching for hole descriptor.\n"));
459 if (IsListEmpty(&IPDR
->HoleListHead
)) {
460 /* Hole list is empty which means a complete datagram can be assembled.
461 Assemble the datagram and pass it to an upper layer protocol */
463 TI_DbgPrint(DEBUG_IP
, ("Complete datagram received.\n"));
465 Datagram
= ReassembleDatagram(IPDR
);
466 KeReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
472 /* Not enough free resources, discard the packet */
473 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
477 /* Give the packet to the protocol dispatcher */
478 IPDispatchProtocol(NTE
, Datagram
);
480 /* We're done with this datagram */
481 ExFreePool(Datagram
->Header
);
482 PoolFreeBuffer(Datagram
);
484 KeReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
488 VOID
IPFreeReassemblyList(
491 * FUNCTION: Frees all IP datagram reassembly structures in the list
495 PLIST_ENTRY CurrentEntry
;
496 PIPDATAGRAM_REASSEMBLY Current
;
498 KeAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
500 CurrentEntry
= ReassemblyListHead
.Flink
;
501 while (CurrentEntry
!= &ReassemblyListHead
) {
502 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
503 /* Unlink it from the list */
504 RemoveEntryList(CurrentEntry
);
506 /* And free the descriptor */
509 CurrentEntry
= CurrentEntry
->Flink
;
512 KeReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
516 VOID
IPDatagramReassemblyTimeout(
519 * FUNCTION: IP datagram reassembly timeout handler
521 * This routine is called by IPTimeout to free any resources used
522 * to hold IP fragments that are being reassembled to form a
523 * complete IP datagram
533 * FUNCTION: Receives an IPv4 datagram (or fragment)
535 * Context = Pointer to context information (IP_INTERFACE)
536 * IPPacket = Pointer to IP packet
539 // PNEIGHBOR_CACHE_ENTRY NCE;
540 PNET_TABLE_ENTRY NTE
;
543 TI_DbgPrint(MAX_TRACE
, ("Received IPv4 datagram.\n"));
545 IPPacket
->HeaderSize
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
& 0x0F) << 2;
547 if (IPPacket
->HeaderSize
> IPv4_MAX_HEADER_SIZE
) {
548 TI_DbgPrint(MIN_TRACE
, ("Datagram received with incorrect header size (%d).\n",
549 IPPacket
->HeaderSize
));
554 /* Checksum IPv4 header */
555 if (!CorrectChecksum(IPPacket
->Header
, IPPacket
->HeaderSize
)) {
556 TI_DbgPrint(MIN_TRACE
, ("Datagram received with bad checksum. Checksum field (0x%X)\n",
557 WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->Checksum
)));
562 TI_DbgPrint(MAX_TRACE
, ("TotalSize (datalink) is (%d).\n", IPPacket
->TotalSize
));
564 IPPacket
->TotalSize
= WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->TotalLength
);
566 TI_DbgPrint(MAX_TRACE
, ("TotalSize (IPv4) is (%d).\n", IPPacket
->TotalSize
));
568 AddrInitIPv4(&IPPacket
->SrcAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->SrcAddr
);
569 AddrInitIPv4(&IPPacket
->DstAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->DstAddr
);
571 IPPacket
->Position
= IPPacket
->HeaderSize
;
572 IPPacket
->Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPPacket
->HeaderSize
);
574 /* FIXME: Possibly forward packets with multicast addresses */
576 /* FIXME: Should we allow packets to be received on the wrong interface? */
578 NTE
= IPLocateNTE(&IPPacket
->DstAddr
, &AddressType
);
580 NTE
= IPLocateNTEOnInterface((PIP_INTERFACE
)Context
, &IPPacket
->DstAddr
, &AddressType
);
583 /* This packet is destined for us */
584 ProcessFragment((PIP_INTERFACE
)Context
, IPPacket
, NTE
);
585 /* Done with this NTE */
586 DereferenceObject(NTE
);
588 /* This packet is not destined for us. If we are a router,
589 try to find a route and forward the packet */
591 /* FIXME: Check if acting as a router */
593 NCE
= RouteFindRouter(&IPPacket
->DstAddr
, NULL
);
595 /* FIXME: Possibly fragment datagram */
596 /* Forward the packet */
597 IPSendFragment(IPPacket
, NCE
);
599 TI_DbgPrint(MIN_TRACE
, ("No route to destination (0x%X).\n",
600 IPPacket
->DstAddr
.Address
.IPv4Address
));
602 /* FIXME: Send ICMP error code */
613 * FUNCTION: Receives an IP datagram (or fragment)
615 * Context = Pointer to context information (IP_INTERFACE)
616 * IPPacket = Pointer to IP packet
621 /* Check that IP header has a supported version */
622 Version
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
>> 4);
625 IPPacket
->Type
= IP_ADDRESS_V4
;
626 IPv4Receive(Context
, IPPacket
);
629 IPPacket
->Type
= IP_ADDRESS_V6
;
630 TI_DbgPrint(MIN_TRACE
, ("Datagram of type IPv6 discarded.\n"));
633 TI_DbgPrint(MIN_TRACE
, ("Datagram has an unsupported IP version %d.\n", Version
));
635 DISPLAY_IP_PACKET(IPPacket
);