1 /*
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
8  *              from RFC 815
9  * REVISIONS:
10  *   CSH 01/08-2000 Created
11  */
12 
13 #include "precomp.h"
14 
15 LIST_ENTRY ReassemblyListHead;
16 KSPIN_LOCK ReassemblyListLock;
17 NPAGED_LOOKASIDE_LIST IPDRList;
18 NPAGED_LOOKASIDE_LIST IPFragmentList;
19 NPAGED_LOOKASIDE_LIST IPHoleList;
20 
CreateHoleDescriptor(ULONG First,ULONG Last)21 PIPDATAGRAM_HOLE CreateHoleDescriptor(
22   ULONG First,
23   ULONG Last)
24 /*
25  * FUNCTION: Returns a pointer to a IP datagram hole descriptor
26  * ARGUMENTS:
27  *     First = Offset of first octet of the hole
28  *     Last  = Offset of last octet of the hole
29  * RETURNS:
30  *     Pointer to descriptor, NULL if there was not enough free
31  *     resources
32  */
33 {
34 	PIPDATAGRAM_HOLE Hole;
35 
36 	TI_DbgPrint(DEBUG_IP, ("Called. First (%d)  Last (%d).\n", First, Last));
37 
38 	Hole = ExAllocateFromNPagedLookasideList(&IPHoleList);
39 	if (!Hole) {
40 	    TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
41 	    return NULL;
42 	}
43 
44 	Hole->First = First;
45 	Hole->Last  = Last;
46 
47 	TI_DbgPrint(DEBUG_IP, ("Returning hole descriptor at (0x%X).\n", Hole));
48 
49 	return Hole;
50 }
51 
52 
FreeIPDR(PIPDATAGRAM_REASSEMBLY IPDR)53 VOID FreeIPDR(
54   PIPDATAGRAM_REASSEMBLY IPDR)
55 /*
56  * FUNCTION: Frees an IP datagram reassembly structure
57  * ARGUMENTS:
58  *     IPDR = Pointer to IP datagram reassembly structure
59  */
60 {
61   PLIST_ENTRY CurrentEntry;
62   PLIST_ENTRY NextEntry;
63   PIPDATAGRAM_HOLE CurrentH;
64   PIP_FRAGMENT CurrentF;
65 
66   TI_DbgPrint(DEBUG_IP, ("Freeing IP datagram reassembly descriptor (0x%X).\n", IPDR));
67 
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);
75 
76     TI_DbgPrint(DEBUG_IP, ("Freeing hole descriptor at (0x%X).\n", CurrentH));
77 
78     /* And free the hole descriptor */
79     ExFreeToNPagedLookasideList(&IPHoleList, CurrentH);
80 
81     CurrentEntry = NextEntry;
82   }
83 
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);
91 
92     TI_DbgPrint(DEBUG_IP, ("Freeing fragment packet at (0x%X).\n", CurrentF->Packet));
93 
94     /* Free the fragment data buffer */
95     if (CurrentF->ReturnPacket)
96     {
97         NdisReturnPackets(&CurrentF->Packet, 1);
98     }
99     else
100     {
101         FreeNdisPacket(CurrentF->Packet);
102     }
103 
104     TI_DbgPrint(DEBUG_IP, ("Freeing fragment at (0x%X).\n", CurrentF));
105 
106     /* And free the fragment descriptor */
107     ExFreeToNPagedLookasideList(&IPFragmentList, CurrentF);
108     CurrentEntry = NextEntry;
109   }
110 
111   if (IPDR->IPv4Header)
112   {
113       TI_DbgPrint(DEBUG_IP, ("Freeing IPDR header at (0x%X).\n", IPDR->IPv4Header));
114       ExFreePoolWithTag(IPDR->IPv4Header, PACKET_BUFFER_TAG);
115   }
116 
117   TI_DbgPrint(DEBUG_IP, ("Freeing IPDR data at (0x%X).\n", IPDR));
118 
119   ExFreeToNPagedLookasideList(&IPDRList, IPDR);
120 }
121 
122 
RemoveIPDR(PIPDATAGRAM_REASSEMBLY IPDR)123 VOID RemoveIPDR(
124   PIPDATAGRAM_REASSEMBLY IPDR)
125 /*
126  * FUNCTION: Removes an IP datagram reassembly structure from the global list
127  * ARGUMENTS:
128  *     IPDR = Pointer to IP datagram reassembly structure
129  */
130 {
131   KIRQL OldIrql;
132 
133   TI_DbgPrint(DEBUG_IP, ("Removing IPDR at (0x%X).\n", IPDR));
134 
135   TcpipAcquireSpinLock(&ReassemblyListLock, &OldIrql);
136   RemoveEntryList(&IPDR->ListEntry);
137   TcpipReleaseSpinLock(&ReassemblyListLock, OldIrql);
138 }
139 
140 
GetReassemblyInfo(PIP_PACKET IPPacket)141 PIPDATAGRAM_REASSEMBLY GetReassemblyInfo(
142   PIP_PACKET IPPacket)
143 /*
144  * FUNCTION: Returns a pointer to an IP datagram reassembly structure
145  * ARGUMENTS:
146  *     IPPacket = Pointer to IP packet
147  * NOTES:
148  *     A datagram is identified by four paramters, which are
149  *     Source and destination address, protocol number and
150  *     identification number
151  */
152 {
153   KIRQL OldIrql;
154   PLIST_ENTRY CurrentEntry;
155   PIPDATAGRAM_REASSEMBLY Current;
156   PIPv4_HEADER Header = (PIPv4_HEADER)IPPacket->Header;
157 
158   TI_DbgPrint(DEBUG_IP, ("Searching for IPDR for IP packet at (0x%X).\n", IPPacket));
159 
160   TcpipAcquireSpinLock(&ReassemblyListLock, &OldIrql);
161 
162   /* FIXME: Assume IPv4 */
163 
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);
172 
173       return Current;
174     }
175     CurrentEntry = CurrentEntry->Flink;
176   }
177 
178   TcpipReleaseSpinLock(&ReassemblyListLock, OldIrql);
179 
180   return NULL;
181 }
182 
183 
184 BOOLEAN
ReassembleDatagram(PIP_PACKET IPPacket,PIPDATAGRAM_REASSEMBLY IPDR)185 ReassembleDatagram(
186   PIP_PACKET             IPPacket,
187   PIPDATAGRAM_REASSEMBLY IPDR)
188 /*
189  * FUNCTION: Reassembles an IP datagram
190  * ARGUMENTS:
191  *     IPDR = Pointer to IP datagram reassembly structure
192  * NOTES:
193  *     This routine concatenates fragments into a complete IP datagram.
194  *     The lock is held when this routine is called
195  * RETURNS:
196  *     Pointer to IP packet, NULL if there was not enough free resources
197  * NOTES:
198  *     At this point, header is expected to point to the IP header
199  */
200 {
201   PLIST_ENTRY CurrentEntry;
202   PIP_FRAGMENT Fragment;
203   PCHAR Data;
204 
205   PAGED_CODE();
206 
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));
210 
211   IPPacket->TotalSize  = IPDR->HeaderSize + IPDR->DataSize;
212   IPPacket->HeaderSize = IPDR->HeaderSize;
213 
214   RtlCopyMemory(&IPPacket->SrcAddr, &IPDR->SrcAddr, sizeof(IP_ADDRESS));
215   RtlCopyMemory(&IPPacket->DstAddr, &IPDR->DstAddr, sizeof(IP_ADDRESS));
216 
217   /* Allocate space for full IP datagram */
218   IPPacket->Header = ExAllocatePoolWithTag(NonPagedPool, IPPacket->TotalSize, PACKET_BUFFER_TAG);
219   if (!IPPacket->Header) {
220     TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
221     (*IPPacket->Free)(IPPacket);
222     return FALSE;
223   }
224   IPPacket->MappedHeader = FALSE;
225 
226   /* Copy the header into the buffer */
227   RtlCopyMemory(IPPacket->Header, IPDR->IPv4Header, IPDR->HeaderSize);
228 
229   Data = (PVOID)((ULONG_PTR)IPPacket->Header + IPDR->HeaderSize);
230   IPPacket->Data = Data;
231 
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);
236 
237     /* Copy fragment data into datagram buffer */
238     CopyPacketToBuffer(Data + Fragment->Offset,
239                        Fragment->Packet,
240                        Fragment->PacketOffset,
241                        Fragment->Size);
242 
243     CurrentEntry = CurrentEntry->Flink;
244   }
245 
246   return TRUE;
247 }
248 
249 
Cleanup(PKSPIN_LOCK Lock,KIRQL OldIrql,PIPDATAGRAM_REASSEMBLY IPDR)250 static inline VOID Cleanup(
251   PKSPIN_LOCK Lock,
252   KIRQL OldIrql,
253   PIPDATAGRAM_REASSEMBLY IPDR)
254 /*
255  * FUNCTION: Performs cleaning operations on errors
256  * ARGUMENTS:
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
261  */
262 {
263   TI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
264 
265   TcpipReleaseSpinLock(Lock, OldIrql);
266   RemoveIPDR(IPDR);
267   FreeIPDR(IPDR);
268 }
269 
270 
ProcessFragment(PIP_INTERFACE IF,PIP_PACKET IPPacket)271 VOID ProcessFragment(
272   PIP_INTERFACE IF,
273   PIP_PACKET IPPacket)
274 /*
275  * FUNCTION: Processes an IP datagram or fragment
276  * ARGUMENTS:
277  *     IF       = Pointer to IP interface packet was receive on
278  *     IPPacket = Pointer to IP packet
279  * NOTES:
280  *     This routine reassembles fragments and, if a whole datagram can
281  *     be assembled, passes the datagram on to the IP protocol dispatcher
282  */
283 {
284   KIRQL OldIrql;
285   PIPDATAGRAM_REASSEMBLY IPDR;
286   PLIST_ENTRY CurrentEntry;
287   PIPDATAGRAM_HOLE Hole, NewHole;
288   USHORT FragFirst;
289   USHORT FragLast;
290   BOOLEAN MoreFragments;
291   PIPv4_HEADER IPv4Header;
292   IP_PACKET Datagram;
293   PIP_FRAGMENT Fragment;
294   BOOLEAN Success;
295 
296   /* FIXME: Assume IPv4 */
297 
298   IPv4Header = (PIPv4_HEADER)IPPacket->Header;
299 
300   /* Check if we already have an reassembly structure for this datagram */
301   IPDR = GetReassemblyInfo(IPPacket);
302   if (IPDR) {
303     TI_DbgPrint(DEBUG_IP, ("Continueing assembly.\n"));
304     /* We have a reassembly structure */
305     TcpipAcquireSpinLock(&IPDR->Lock, &OldIrql);
306 
307     /* Reset the timeout since we received a fragment */
308     IPDR->TimeoutCount = 0;
309   } else {
310     TI_DbgPrint(DEBUG_IP, ("Starting new assembly.\n"));
311 
312     /* We don't have a reassembly structure, create one */
313     IPDR = ExAllocateFromNPagedLookasideList(&IPDRList);
314     if (!IPDR)
315       /* We don't have the resources to process this packet, discard it */
316       return;
317 
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);
322     if (!Hole) {
323       /* We don't have the resources to process this packet, discard it */
324       ExFreeToNPagedLookasideList(&IPDRList, IPDR);
325       return;
326     }
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);
335 
336     TcpipInitializeSpinLock(&IPDR->Lock);
337 
338     TcpipAcquireSpinLock(&IPDR->Lock, &OldIrql);
339 
340     /* Update the reassembly list */
341     TcpipInterlockedInsertTailList(
342 	&ReassemblyListHead,
343 	&IPDR->ListEntry,
344 	&ReassemblyListLock);
345   }
346 
347   FragFirst     = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_FRAGOFS_MASK) << 3;
348   FragLast      = FragFirst + WN2H(IPv4Header->TotalLength);
349   MoreFragments = (WN2H(IPv4Header->FlagsFragOfs) & IPv4_MF_MASK) > 0;
350 
351   CurrentEntry = IPDR->HoleListHead.Flink;
352   for (;;) {
353     if (CurrentEntry == &IPDR->HoleListHead)
354         break;
355 
356     Hole = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_HOLE, ListEntry);
357 
358     TI_DbgPrint(DEBUG_IP, ("Comparing Fragment (%d,%d) to Hole (%d,%d).\n",
359       FragFirst, FragLast, Hole->First, Hole->Last));
360 
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 */
365 
366       CurrentEntry = CurrentEntry->Flink;
367       continue;
368     }
369 
370     /* The fragment overlap with the hole, unlink the descriptor */
371     RemoveEntryList(CurrentEntry);
372 
373     if (FragFirst > Hole->First) {
374       NewHole = CreateHoleDescriptor(Hole->First, FragFirst - 1);
375       if (!NewHole) {
376         /* We don't have the resources to process this packet, discard it */
377         ExFreeToNPagedLookasideList(&IPHoleList, Hole);
378         Cleanup(&IPDR->Lock, OldIrql, IPDR);
379         return;
380       }
381 
382       /* Put the new descriptor in the list */
383       InsertTailList(&IPDR->HoleListHead, &NewHole->ListEntry);
384     }
385 
386     if ((FragLast < Hole->Last) && MoreFragments) {
387       NewHole = CreateHoleDescriptor(FragLast + 1, Hole->Last);
388       if (!NewHole) {
389         /* We don't have the resources to process this packet, discard it */
390         ExFreeToNPagedLookasideList(&IPHoleList, Hole);
391         Cleanup(&IPDR->Lock, OldIrql, IPDR);
392         return;
393       }
394 
395       /* Put the new hole descriptor in the list */
396       InsertTailList(&IPDR->HoleListHead, &NewHole->ListEntry);
397     }
398 
399     ExFreeToNPagedLookasideList(&IPHoleList, Hole);
400 
401     /* If this is the first fragment, save the IP header */
402     if (FragFirst == 0) {
403         IPDR->IPv4Header = ExAllocatePoolWithTag(NonPagedPool,
404                                                  IPPacket->HeaderSize,
405                                                  PACKET_BUFFER_TAG);
406         if (!IPDR->IPv4Header)
407         {
408             Cleanup(&IPDR->Lock, OldIrql, IPDR);
409             return;
410         }
411 
412         RtlCopyMemory(IPDR->IPv4Header, IPPacket->Header, IPPacket->HeaderSize);
413         IPDR->HeaderSize = IPPacket->HeaderSize;
414 
415         TI_DbgPrint(DEBUG_IP, ("First fragment found. Header buffer is at (0x%X). "
416                                "Header size is (%d).\n", &IPDR->IPv4Header, IPPacket->HeaderSize));
417 
418     }
419 
420     /* Create a buffer, copy the data into it and put it
421        in the fragment list */
422 
423     Fragment = ExAllocateFromNPagedLookasideList(&IPFragmentList);
424     if (!Fragment) {
425       /* We don't have the resources to process this packet, discard it */
426       Cleanup(&IPDR->Lock, OldIrql, IPDR);
427       return;
428     }
429 
430     TI_DbgPrint(DEBUG_IP, ("Fragment descriptor allocated at (0x%X).\n", Fragment));
431 
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;
437 
438     /* Disassociate the NDIS packet so it isn't freed upon return from IPReceive() */
439     IPPacket->NdisPacket = NULL;
440 
441     /* If this is the last fragment, compute and save the datagram data size */
442     if (!MoreFragments)
443       IPDR->DataSize = FragFirst + Fragment->Size;
444 
445     /* Put the fragment in the list */
446     InsertTailList(&IPDR->FragmentListHead, &Fragment->ListEntry);
447     break;
448   }
449 
450   TI_DbgPrint(DEBUG_IP, ("Done searching for hole descriptor.\n"));
451 
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 */
455 
456     TI_DbgPrint(DEBUG_IP, ("Complete datagram received.\n"));
457 
458     RemoveIPDR(IPDR);
459     TcpipReleaseSpinLock(&IPDR->Lock, OldIrql);
460 
461     /* FIXME: Assumes IPv4 */
462     IPInitializePacket(&Datagram, IP_ADDRESS_V4);
463 
464     Success = ReassembleDatagram(&Datagram, IPDR);
465 
466     FreeIPDR(IPDR);
467 
468     if (!Success)
469       /* Not enough free resources, discard the packet */
470       return;
471 
472     DISPLAY_IP_PACKET(&Datagram);
473 
474     /* Give the packet to the protocol dispatcher */
475     IPDispatchProtocol(IF, &Datagram);
476 
477     /* We're done with this datagram */
478     TI_DbgPrint(MAX_TRACE, ("Freeing datagram at (0x%X).\n", Datagram));
479     Datagram.Free(&Datagram);
480   } else
481     TcpipReleaseSpinLock(&IPDR->Lock, OldIrql);
482 }
483 
484 
IPFreeReassemblyList(VOID)485 VOID IPFreeReassemblyList(
486   VOID)
487 /*
488  * FUNCTION: Frees all IP datagram reassembly structures in the list
489  */
490 {
491   KIRQL OldIrql;
492   PLIST_ENTRY CurrentEntry, NextEntry;
493   PIPDATAGRAM_REASSEMBLY Current;
494 
495   TcpipAcquireSpinLock(&ReassemblyListLock, &OldIrql);
496 
497   CurrentEntry = ReassemblyListHead.Flink;
498   while (CurrentEntry != &ReassemblyListHead) {
499     NextEntry = CurrentEntry->Flink;
500     Current = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
501 
502     /* Unlink it from the list */
503     RemoveEntryList(CurrentEntry);
504 
505     /* And free the descriptor */
506     FreeIPDR(Current);
507 
508     CurrentEntry = NextEntry;
509   }
510 
511   TcpipReleaseSpinLock(&ReassemblyListLock, OldIrql);
512 }
513 
514 
IPDatagramReassemblyTimeout(VOID)515 VOID IPDatagramReassemblyTimeout(
516   VOID)
517 /*
518  * FUNCTION: IP datagram reassembly timeout handler
519  * NOTES:
520  *     This routine is called by IPTimeout to free any resources used
521  *     to hold IP fragments that have taken too long to reassemble
522  */
523 {
524     PLIST_ENTRY CurrentEntry, NextEntry;
525     PIPDATAGRAM_REASSEMBLY CurrentIPDR;
526 
527     TcpipAcquireSpinLockAtDpcLevel(&ReassemblyListLock);
528 
529     CurrentEntry = ReassemblyListHead.Flink;
530     while (CurrentEntry != &ReassemblyListHead)
531     {
532        NextEntry = CurrentEntry->Flink;
533        CurrentIPDR = CONTAINING_RECORD(CurrentEntry, IPDATAGRAM_REASSEMBLY, ListEntry);
534 
535        TcpipAcquireSpinLockAtDpcLevel(&CurrentIPDR->Lock);
536 
537        if (++CurrentIPDR->TimeoutCount == MAX_TIMEOUT_COUNT)
538        {
539            TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR->Lock);
540            RemoveEntryList(CurrentEntry);
541            FreeIPDR(CurrentIPDR);
542        }
543        else
544        {
545            ASSERT(CurrentIPDR->TimeoutCount < MAX_TIMEOUT_COUNT);
546            TcpipReleaseSpinLockFromDpcLevel(&CurrentIPDR->Lock);
547        }
548 
549        CurrentEntry = NextEntry;
550     }
551 
552     TcpipReleaseSpinLockFromDpcLevel(&ReassemblyListLock);
553 }
554 
IPv4Receive(PIP_INTERFACE IF,PIP_PACKET IPPacket)555 VOID IPv4Receive(PIP_INTERFACE IF, PIP_PACKET IPPacket)
556 /*
557  * FUNCTION: Receives an IPv4 datagram (or fragment)
558  * ARGUMENTS:
559  *     Context  = Pointer to context information (IP_INTERFACE)
560  *     IPPacket = Pointer to IP packet
561  */
562 {
563     UCHAR FirstByte;
564     ULONG BytesCopied;
565 
566     TI_DbgPrint(DEBUG_IP, ("Received IPv4 datagram.\n"));
567 
568     /* Read in the first IP header byte for size information */
569     BytesCopied = CopyPacketToBuffer((PCHAR)&FirstByte,
570                                      IPPacket->NdisPacket,
571                                      IPPacket->Position,
572                                      sizeof(UCHAR));
573     if (BytesCopied != sizeof(UCHAR))
574     {
575         TI_DbgPrint(MIN_TRACE, ("Failed to copy in first byte\n"));
576         /* Discard packet */
577         return;
578     }
579 
580     IPPacket->HeaderSize = (FirstByte & 0x0F) << 2;
581     TI_DbgPrint(DEBUG_IP, ("IPPacket->HeaderSize = %d\n", IPPacket->HeaderSize));
582 
583     if (IPPacket->HeaderSize > IPv4_MAX_HEADER_SIZE) {
584         TI_DbgPrint(MIN_TRACE, ("Datagram received with incorrect header size (%d).\n",
585 	      IPPacket->HeaderSize));
586         /* Discard packet */
587         return;
588     }
589 
590     /* This is freed by IPPacket->Free() */
591     IPPacket->Header = ExAllocatePoolWithTag(NonPagedPool,
592                                              IPPacket->HeaderSize,
593                                              PACKET_BUFFER_TAG);
594     if (!IPPacket->Header)
595     {
596         TI_DbgPrint(MIN_TRACE, ("No resources to allocate header\n"));
597         /* Discard packet */
598         return;
599     }
600 
601     IPPacket->MappedHeader = FALSE;
602 
603     BytesCopied = CopyPacketToBuffer((PCHAR)IPPacket->Header,
604                                      IPPacket->NdisPacket,
605                                      IPPacket->Position,
606                                      IPPacket->HeaderSize);
607     if (BytesCopied != IPPacket->HeaderSize)
608     {
609         TI_DbgPrint(MIN_TRACE, ("Failed to copy in header\n"));
610         /* Discard packet */
611         return;
612     }
613 
614     /* Checksum IPv4 header */
615     if (!IPv4CorrectChecksum(IPPacket->Header, IPPacket->HeaderSize)) {
616         TI_DbgPrint(MIN_TRACE, ("Datagram received with bad checksum. Checksum field (0x%X)\n",
617 	      WN2H(((PIPv4_HEADER)IPPacket->Header)->Checksum)));
618         /* Discard packet */
619         return;
620     }
621 
622     IPPacket->TotalSize = WN2H(((PIPv4_HEADER)IPPacket->Header)->TotalLength);
623 
624     AddrInitIPv4(&IPPacket->SrcAddr, ((PIPv4_HEADER)IPPacket->Header)->SrcAddr);
625     AddrInitIPv4(&IPPacket->DstAddr, ((PIPv4_HEADER)IPPacket->Header)->DstAddr);
626 
627     TI_DbgPrint(MID_TRACE,("IPPacket->Position = %d\n",
628                            IPPacket->Position));
629 
630     /* FIXME: Possibly forward packets with multicast addresses */
631 
632     /* FIXME: Should we allow packets to be received on the wrong interface? */
633     /* XXX Find out if this packet is destined for us */
634     ProcessFragment(IF, IPPacket);
635 }
636 
637 
IPReceive(PIP_INTERFACE IF,PIP_PACKET IPPacket)638 VOID IPReceive( PIP_INTERFACE IF, PIP_PACKET IPPacket )
639 /*
640  * FUNCTION: Receives an IP datagram (or fragment)
641  * ARGUMENTS:
642  *     IF       = Interface
643  *     IPPacket = Pointer to IP packet
644  */
645 {
646     UCHAR FirstByte;
647     UINT Version, BytesCopied;
648 
649     /* Read in the first IP header byte for version information */
650     BytesCopied = CopyPacketToBuffer((PCHAR)&FirstByte,
651                                      IPPacket->NdisPacket,
652                                      IPPacket->Position,
653                                      sizeof(UCHAR));
654     if (BytesCopied != sizeof(UCHAR))
655     {
656         TI_DbgPrint(MIN_TRACE, ("Failed to copy in first byte\n"));
657         IPPacket->Free(IPPacket);
658         return;
659     }
660 
661     /* Check that IP header has a supported version */
662     Version = (FirstByte >> 4);
663 
664     switch (Version) {
665         case 4:
666             IPPacket->Type = IP_ADDRESS_V4;
667             IPv4Receive(IF, IPPacket);
668             break;
669         case 6:
670             IPPacket->Type = IP_ADDRESS_V6;
671             TI_DbgPrint(MAX_TRACE, ("Datagram of type IPv6 discarded.\n"));
672             break;
673         default:
674             TI_DbgPrint(MIN_TRACE, ("Datagram has an unsupported IP version %d.\n", Version));
675             break;
676     }
677 
678     IPPacket->Free(IPPacket);
679 }
680 
681 /* EOF */
682