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 FreeNdisPacket(CurrentF
->Packet
);
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 Fragment
;
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 IPPacket
->TotalSize
= IPDR
->HeaderSize
+ IPDR
->DataSize
;
197 IPPacket
->ContigSize
= IPPacket
->TotalSize
;
198 IPPacket
->HeaderSize
= IPDR
->HeaderSize
;
200 RtlCopyMemory(&IPPacket
->SrcAddr
, &IPDR
->SrcAddr
, sizeof(IP_ADDRESS
));
201 RtlCopyMemory(&IPPacket
->DstAddr
, &IPDR
->DstAddr
, sizeof(IP_ADDRESS
));
203 /* Allocate space for full IP datagram */
204 IPPacket
->Header
= ExAllocatePoolWithTag(NonPagedPool
, IPPacket
->TotalSize
, PACKET_BUFFER_TAG
);
205 if (!IPPacket
->Header
) {
206 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
207 (*IPPacket
->Free
)(IPPacket
);
211 /* Copy the header into the buffer */
212 RtlCopyMemory(IPPacket
->Header
, &IPDR
->IPv4Header
, IPDR
->HeaderSize
);
214 Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPDR
->HeaderSize
);
215 IPPacket
->Data
= Data
;
217 /* Copy data from all fragments into buffer */
218 CurrentEntry
= IPDR
->FragmentListHead
.Flink
;
219 while (CurrentEntry
!= &IPDR
->FragmentListHead
) {
220 Fragment
= CONTAINING_RECORD(CurrentEntry
, IP_FRAGMENT
, ListEntry
);
222 /* Copy fragment data into datagram buffer */
223 CopyPacketToBuffer(Data
+ Fragment
->Offset
,
225 Fragment
->PacketOffset
,
228 CurrentEntry
= CurrentEntry
->Flink
;
235 __inline VOID
Cleanup(
238 PIPDATAGRAM_REASSEMBLY IPDR
)
240 * FUNCTION: Performs cleaning operations on errors
242 * Lock = Pointer to spin lock to be released
243 * OldIrql = Value of IRQL when spin lock was acquired
244 * IPDR = Pointer to IP datagram reassembly structure to free
245 * Buffer = Optional pointer to a buffer to free
248 TI_DbgPrint(MIN_TRACE
, ("Insufficient resources.\n"));
250 TcpipReleaseSpinLock(Lock
, OldIrql
);
256 VOID
ProcessFragment(
260 * FUNCTION: Processes an IP datagram or fragment
262 * IF = Pointer to IP interface packet was receive on
263 * IPPacket = Pointer to IP packet
265 * This routine reassembles fragments and, if a whole datagram can
266 * be assembled, passes the datagram on to the IP protocol dispatcher
270 PIPDATAGRAM_REASSEMBLY IPDR
;
271 PLIST_ENTRY CurrentEntry
;
272 PIPDATAGRAM_HOLE Hole
, NewHole
;
275 BOOLEAN MoreFragments
;
276 PIPv4_HEADER IPv4Header
;
278 PIP_FRAGMENT Fragment
;
281 /* FIXME: Assume IPv4 */
283 IPv4Header
= (PIPv4_HEADER
)IPPacket
->Header
;
285 /* Check if we already have an reassembly structure for this datagram */
286 IPDR
= GetReassemblyInfo(IPPacket
);
288 TI_DbgPrint(DEBUG_IP
, ("Continueing assembly.\n"));
289 /* We have a reassembly structure */
290 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
292 /* Reset the timeout since we received a fragment */
293 IPDR
->TimeoutCount
= 0;
295 TI_DbgPrint(DEBUG_IP
, ("Starting new assembly.\n"));
297 /* We don't have a reassembly structure, create one */
298 IPDR
= ExAllocateFromNPagedLookasideList(&IPDRList
);
300 /* We don't have the resources to process this packet, discard it */
303 /* Create a descriptor spanning from zero to infinity.
304 Actually, we use a value slightly greater than the
305 maximum number of octets an IP datagram can contain */
306 Hole
= CreateHoleDescriptor(0, 65536);
308 /* We don't have the resources to process this packet, discard it */
309 ExFreeToNPagedLookasideList(&IPDRList
, IPDR
);
312 AddrInitIPv4(&IPDR
->SrcAddr
, IPv4Header
->SrcAddr
);
313 AddrInitIPv4(&IPDR
->DstAddr
, IPv4Header
->DstAddr
);
314 IPDR
->Id
= IPv4Header
->Id
;
315 IPDR
->Protocol
= IPv4Header
->Protocol
;
316 IPDR
->TimeoutCount
= 0;
317 InitializeListHead(&IPDR
->FragmentListHead
);
318 InitializeListHead(&IPDR
->HoleListHead
);
319 InsertTailList(&IPDR
->HoleListHead
, &Hole
->ListEntry
);
321 TcpipInitializeSpinLock(&IPDR
->Lock
);
323 TcpipAcquireSpinLock(&IPDR
->Lock
, &OldIrql
);
325 /* Update the reassembly list */
326 TcpipInterlockedInsertTailList(
329 &ReassemblyListLock
);
332 FragFirst
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_FRAGOFS_MASK
) << 3;
333 FragLast
= FragFirst
+ WN2H(IPv4Header
->TotalLength
);
334 MoreFragments
= (WN2H(IPv4Header
->FlagsFragOfs
) & IPv4_MF_MASK
) > 0;
336 CurrentEntry
= IPDR
->HoleListHead
.Flink
;
338 if (CurrentEntry
== &IPDR
->HoleListHead
)
341 Hole
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_HOLE
, ListEntry
);
343 TI_DbgPrint(DEBUG_IP
, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
344 FragFirst
, FragLast
, Hole
->First
, Hole
->Last
));
346 if ((FragFirst
> Hole
->Last
) || (FragLast
< Hole
->First
)) {
347 TI_DbgPrint(MID_TRACE
, ("No overlap.\n"));
348 /* The fragment does not overlap with the hole, try next
349 descriptor in the list */
351 CurrentEntry
= CurrentEntry
->Flink
;
355 /* The fragment overlap with the hole, unlink the descriptor */
356 RemoveEntryList(CurrentEntry
);
358 if (FragFirst
> Hole
->First
) {
359 NewHole
= CreateHoleDescriptor(Hole
->First
, FragFirst
- 1);
361 /* We don't have the resources to process this packet, discard it */
362 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
363 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
367 /* Put the new descriptor in the list */
368 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
371 if ((FragLast
< Hole
->Last
) && MoreFragments
) {
372 NewHole
= CreateHoleDescriptor(FragLast
+ 1, Hole
->Last
);
374 /* We don't have the resources to process this packet, discard it */
375 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
376 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
380 /* Put the new hole descriptor in the list */
381 InsertTailList(&IPDR
->HoleListHead
, &NewHole
->ListEntry
);
384 ExFreeToNPagedLookasideList(&IPHoleList
, Hole
);
386 /* If this is the first fragment, save the IP header */
387 if (FragFirst
== 0) {
388 TI_DbgPrint(DEBUG_IP
, ("First fragment found. Header buffer is at (0x%X). "
389 "Header size is (%d).\n", &IPDR
->IPv4Header
, IPPacket
->HeaderSize
));
391 RtlCopyMemory(&IPDR
->IPv4Header
, IPPacket
->Header
, IPPacket
->HeaderSize
);
392 IPDR
->HeaderSize
= IPPacket
->HeaderSize
;
395 /* Create a buffer, copy the data into it and put it
396 in the fragment list */
398 Fragment
= ExAllocateFromNPagedLookasideList(&IPFragmentList
);
400 /* We don't have the resources to process this packet, discard it */
401 Cleanup(&IPDR
->Lock
, OldIrql
, IPDR
);
405 TI_DbgPrint(DEBUG_IP
, ("Fragment descriptor allocated at (0x%X).\n", Fragment
));
407 Fragment
->Size
= IPPacket
->TotalSize
- IPPacket
->HeaderSize
;
408 Fragment
->Packet
= IPPacket
->NdisPacket
;
409 Fragment
->PacketOffset
= IPPacket
->Position
+ IPPacket
->HeaderSize
;
410 Fragment
->Offset
= FragFirst
;
412 /* Disassociate the NDIS packet so it isn't freed upon return from IPReceive() */
413 IPPacket
->NdisPacket
= NULL
;
415 /* If this is the last fragment, compute and save the datagram data size */
417 IPDR
->DataSize
= FragFirst
+ Fragment
->Size
;
419 /* Put the fragment in the list */
420 InsertTailList(&IPDR
->FragmentListHead
, &Fragment
->ListEntry
);
424 TI_DbgPrint(DEBUG_IP
, ("Done searching for hole descriptor.\n"));
426 if (IsListEmpty(&IPDR
->HoleListHead
)) {
427 /* Hole list is empty which means a complete datagram can be assembled.
428 Assemble the datagram and pass it to an upper layer protocol */
430 TI_DbgPrint(DEBUG_IP
, ("Complete datagram received.\n"));
432 /* FIXME: Assumes IPv4 */
433 IPInitializePacket(&Datagram
, IP_ADDRESS_V4
);
435 Success
= ReassembleDatagram(&Datagram
, IPDR
);
438 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
443 /* Not enough free resources, discard the packet */
446 DISPLAY_IP_PACKET(&Datagram
);
448 /* Give the packet to the protocol dispatcher */
449 IPDispatchProtocol(IF
, &Datagram
);
451 IF
->Stats
.InBytes
+= Datagram
.TotalSize
;
453 /* We're done with this datagram */
454 ExFreePoolWithTag(Datagram
.Header
, PACKET_BUFFER_TAG
);
455 TI_DbgPrint(MAX_TRACE
, ("Freeing datagram at (0x%X).\n", Datagram
));
456 (*Datagram
.Free
)(&Datagram
);
458 TcpipReleaseSpinLock(&IPDR
->Lock
, OldIrql
);
462 VOID
IPFreeReassemblyList(
465 * FUNCTION: Frees all IP datagram reassembly structures in the list
469 PLIST_ENTRY CurrentEntry
;
470 PIPDATAGRAM_REASSEMBLY Current
;
472 TcpipAcquireSpinLock(&ReassemblyListLock
, &OldIrql
);
474 CurrentEntry
= ReassemblyListHead
.Flink
;
475 while (CurrentEntry
!= &ReassemblyListHead
) {
476 Current
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
477 /* Unlink it from the list */
478 RemoveEntryList(CurrentEntry
);
480 /* And free the descriptor */
483 CurrentEntry
= CurrentEntry
->Flink
;
486 TcpipReleaseSpinLock(&ReassemblyListLock
, OldIrql
);
490 VOID
IPDatagramReassemblyTimeout(
493 * FUNCTION: IP datagram reassembly timeout handler
495 * This routine is called by IPTimeout to free any resources used
496 * to hold IP fragments that have taken too long to reassemble
499 PLIST_ENTRY CurrentEntry
, NextEntry
;
500 PIPDATAGRAM_REASSEMBLY CurrentIPDR
;
502 TcpipAcquireSpinLockAtDpcLevel(&ReassemblyListLock
);
504 CurrentEntry
= ReassemblyListHead
.Flink
;
505 while (CurrentEntry
!= &ReassemblyListHead
)
507 NextEntry
= CurrentEntry
->Flink
;
508 CurrentIPDR
= CONTAINING_RECORD(CurrentEntry
, IPDATAGRAM_REASSEMBLY
, ListEntry
);
510 TcpipAcquireSpinLockAtDpcLevel(&CurrentIPDR
->Lock
);
512 if (++CurrentIPDR
->TimeoutCount
== MAX_TIMEOUT_COUNT
)
514 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
515 RemoveEntryList(CurrentEntry
);
516 FreeIPDR(CurrentIPDR
);
520 ASSERT(CurrentIPDR
->TimeoutCount
< MAX_TIMEOUT_COUNT
);
521 TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR
->Lock
);
524 CurrentEntry
= NextEntry
;
527 TcpipReleaseSpinLockFromDpcLevel(&ReassemblyListLock
);
530 VOID
IPv4Receive(PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
532 * FUNCTION: Receives an IPv4 datagram (or fragment)
534 * Context = Pointer to context information (IP_INTERFACE)
535 * IPPacket = Pointer to IP packet
538 TI_DbgPrint(DEBUG_IP
, ("Received IPv4 datagram.\n"));
540 IPPacket
->HeaderSize
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
& 0x0F) << 2;
541 TI_DbgPrint(DEBUG_IP
, ("IPPacket->HeaderSize = %d\n", IPPacket
->HeaderSize
));
543 if (IPPacket
->HeaderSize
> IPv4_MAX_HEADER_SIZE
) {
546 ("Datagram received with incorrect header size (%d).\n",
547 IPPacket
->HeaderSize
));
552 /* Checksum IPv4 header */
553 if (!IPv4CorrectChecksum(IPPacket
->Header
, IPPacket
->HeaderSize
)) {
556 ("Datagram received with bad checksum. Checksum field (0x%X)\n",
557 WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->Checksum
)));
562 IPPacket
->TotalSize
= WN2H(((PIPv4_HEADER
)IPPacket
->Header
)->TotalLength
);
564 AddrInitIPv4(&IPPacket
->SrcAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->SrcAddr
);
565 AddrInitIPv4(&IPPacket
->DstAddr
, ((PIPv4_HEADER
)IPPacket
->Header
)->DstAddr
);
567 IPPacket
->Data
= (PVOID
)((ULONG_PTR
)IPPacket
->Header
+ IPPacket
->HeaderSize
);
569 TI_DbgPrint(MID_TRACE
,("IPPacket->Position = %d\n",
570 IPPacket
->Position
));
572 //OskitDumpBuffer(IPPacket->Header, IPPacket->TotalSize);
574 /* FIXME: Possibly forward packets with multicast addresses */
576 /* FIXME: Should we allow packets to be received on the wrong interface? */
577 /* XXX Find out if this packet is destined for us */
578 ProcessFragment(IF
, IPPacket
);
581 /* This packet is not destined for us. If we are a router,
582 try to find a route and forward the packet */
584 /* FIXME: Check if acting as a router */
587 PROUTE_CACHE_NODE RCN
;
589 /* FIXME: Possibly fragment datagram */
590 /* Forward the packet */
591 if(!RouteGetRouteToDestination( &IPPacket
->DstAddr
, NULL
, &RCN
))
592 IPSendDatagram(IPPacket
, RCN
, ReflectPacketComplete
, IPPacket
);
594 TI_DbgPrint(MIN_TRACE
, ("No route to destination (0x%X).\n",
595 IPPacket
->DstAddr
.Address
.IPv4Address
));
597 /* FIXME: Send ICMP error code */
604 VOID
IPReceive( PIP_INTERFACE IF
, PIP_PACKET IPPacket
)
606 * FUNCTION: Receives an IP datagram (or fragment)
609 * IPPacket = Pointer to IP packet
614 /* Check that IP header has a supported version */
615 Version
= (((PIPv4_HEADER
)IPPacket
->Header
)->VerIHL
>> 4);
619 IPPacket
->Type
= IP_ADDRESS_V4
;
620 IPv4Receive(IF
, IPPacket
);
623 IPPacket
->Type
= IP_ADDRESS_V6
;
624 TI_DbgPrint(MAX_TRACE
, ("Datagram of type IPv6 discarded.\n"));
627 TI_DbgPrint(MIN_TRACE
, ("Datagram has an unsupported IP version %d.\n", Version
));
631 IPPacket
->Free(IPPacket
);