update for HEAD-2003021201
[reactos.git] / drivers / net / npf / dump.c
1 /*
2  * Copyright (c) 1999, 2000
3  *  Politecnico di Torino.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the Politecnico
13  * di Torino, and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21
22 #ifdef _MSC_VER
23 #include "stdarg.h"
24 #include "ntddk.h"
25 #include "ntiologc.h"
26 #include "ndis.h"
27 #else
28 #include <ddk/ntddk.h>
29 #include <net/ndis.h>
30 //#define PsGetCurrentProcess() IoGetCurrentProcess()
31 #define PsGetCurrentThread() ((PETHREAD) (KeGetCurrentThread()))
32 #endif
33
34 #include "debug.h"
35 #include "packet.h"
36 #include "win_bpf.h"
37
38 //-------------------------------------------------------------------
39
40 NTSTATUS
41 NPF_OpenDumpFile(POPEN_INSTANCE Open , PUNICODE_STRING fileName, BOOLEAN Append)
42 {
43     NTSTATUS ntStatus;
44     IO_STATUS_BLOCK IoStatus;
45     OBJECT_ATTRIBUTES ObjectAttributes;
46     PWCHAR PathPrefix;
47     USHORT PathLen;
48     UNICODE_STRING FullFileName;
49     ULONG FullFileNameLength;
50     PDEVICE_OBJECT fsdDevice;
51
52     FILE_STANDARD_INFORMATION StandardInfo;
53     
54     IF_LOUD(DbgPrint("NPF: OpenDumpFile.\n");)
55
56     if(fileName->Buffer[0] == L'\\' &&
57         fileName->Buffer[1] == L'?' &&
58         fileName->Buffer[2] == L'?' &&
59         fileName->Buffer[3] == L'\\'
60     ){
61         PathLen = 0;
62     }
63     else{
64         PathPrefix = L"\\??\\";
65         PathLen = 8;
66     }
67     
68     // Insert the correct path prefix.
69     FullFileNameLength = PathLen + fileName->MaximumLength;
70     
71 #define NPF_TAG_FILENAME  TAG('0', 'D', 'W', 'A')
72     FullFileName.Buffer = ExAllocatePoolWithTag(NonPagedPool, 
73         FullFileNameLength,
74         NPF_TAG_FILENAME);
75     
76     if (FullFileName.Buffer == NULL) {
77         ntStatus = STATUS_INSUFFICIENT_RESOURCES;
78         return ntStatus;
79     }
80     
81     FullFileName.Length = PathLen;
82     FullFileName.MaximumLength = (USHORT)FullFileNameLength;
83     
84     if(PathLen)
85         RtlMoveMemory (FullFileName.Buffer, PathPrefix, PathLen);
86     
87     RtlAppendUnicodeStringToString (&FullFileName, fileName);
88     
89     IF_LOUD(DbgPrint( "Packet: Attempting to open %wZ\n", &FullFileName);)
90     
91     InitializeObjectAttributes ( &ObjectAttributes,
92         &FullFileName,
93         OBJ_CASE_INSENSITIVE,
94         NULL,
95         NULL );
96     
97     // Create the dump file
98     ntStatus = ZwCreateFile( &Open->DumpFileHandle,
99         SYNCHRONIZE | FILE_WRITE_DATA,
100         &ObjectAttributes,
101         &IoStatus,
102         NULL,
103         FILE_ATTRIBUTE_NORMAL,
104         FILE_SHARE_READ,
105         (Append)?FILE_OPEN_IF:FILE_SUPERSEDE,
106         FILE_SYNCHRONOUS_IO_NONALERT,
107         NULL,
108         0 );
109
110     if ( !NT_SUCCESS( ntStatus ) )
111     {
112         IF_LOUD(DbgPrint("NPF: Error opening file %x\n", ntStatus);)
113         
114         ExFreePool(FullFileName.Buffer);
115         Open->DumpFileHandle=NULL;
116         ntStatus = STATUS_NO_SUCH_FILE;
117         return ntStatus;
118     }
119     
120     ExFreePool(FullFileName.Buffer);
121     
122     ntStatus = ObReferenceObjectByHandle(Open->DumpFileHandle,
123         FILE_WRITE_ACCESS,
124 #ifndef __GNUC__
125         *IoFileObjectType,
126 #else
127         IoFileObjectType,
128 #endif
129         KernelMode,
130         (PVOID)&Open->DumpFileObject,
131         0);
132     
133     if ( !NT_SUCCESS( ntStatus ) )
134     {
135         IF_LOUD(DbgPrint("NPF: Error creating file, status=%x\n", ntStatus);)
136             
137         ZwClose( Open->DumpFileHandle );
138         Open->DumpFileHandle=NULL;
139         
140         ntStatus = STATUS_NO_SUCH_FILE;
141         return ntStatus;
142     }
143     
144     fsdDevice = IoGetRelatedDeviceObject(Open->DumpFileObject);
145
146     IF_LOUD(DbgPrint("NPF: Dump: write file created succesfully, status=%d \n",ntStatus);)
147
148     return ntStatus;
149 }   
150
151 //-------------------------------------------------------------------
152
153 NTSTATUS
154 NPF_StartDump(POPEN_INSTANCE Open)
155 {
156     NTSTATUS ntStatus;
157     struct packet_file_header hdr;
158     IO_STATUS_BLOCK IoStatus;
159     NDIS_REQUEST pRequest;
160     ULONG MediaType;
161     OBJECT_ATTRIBUTES ObjectAttributes;
162
163     IF_LOUD(DbgPrint("NPF: StartDump.\n");)
164
165     // Init the file header
166     hdr.magic = TCPDUMP_MAGIC;
167     hdr.version_major = PCAP_VERSION_MAJOR;
168     hdr.version_minor = PCAP_VERSION_MINOR;
169     hdr.thiszone = 0; /*Currently not set*/
170     hdr.snaplen = 1514;
171     hdr.sigfigs = 0;
172
173     // Detect the medium type
174     switch (Open->Medium){
175         
176     case NdisMediumWan:
177         hdr.linktype = DLT_EN10MB;
178         break;
179         
180     case NdisMedium802_3:
181         hdr.linktype = DLT_EN10MB;
182         break;
183         
184     case NdisMediumFddi:
185         hdr.linktype = DLT_FDDI;
186         break;
187         
188     case NdisMedium802_5:           
189         hdr.linktype = DLT_IEEE802; 
190         break;
191         
192     case NdisMediumArcnet878_2:
193         hdr.linktype = DLT_ARCNET;
194         break;
195         
196     case NdisMediumAtm:
197         hdr.linktype = DLT_ATM_RFC1483;
198         break;
199         
200     default:
201         hdr.linktype = DLT_EN10MB;
202     }
203
204     // Write the header.
205     // We can use ZwWriteFile because we are in the context of the application
206     ntStatus = ZwWriteFile(Open->DumpFileHandle,
207         NULL,
208         NULL,
209         NULL,
210         &IoStatus,
211         &hdr,
212         sizeof(hdr),
213         NULL,
214         NULL );
215
216     
217     if ( !NT_SUCCESS( ntStatus ) )
218     {
219         IF_LOUD(DbgPrint("NPF: Error dumping file %x\n", ntStatus);)
220         
221         ZwClose( Open->DumpFileHandle );
222         Open->DumpFileHandle=NULL;
223         
224         ntStatus = STATUS_NO_SUCH_FILE;
225         return ntStatus;
226     }
227
228     Open->DumpOffset.QuadPart=24;
229             
230     ntStatus = PsCreateSystemThread(&Open->DumpThreadHandle,
231         THREAD_ALL_ACCESS,
232         (ACCESS_MASK)0L,
233         0,
234         0,
235         (PKSTART_ROUTINE)NPF_DumpThread,
236         Open);
237     
238     if ( !NT_SUCCESS( ntStatus ) )
239     {
240         IF_LOUD(DbgPrint("NPF: Error creating dump thread, status=%x\n", ntStatus);)
241         
242         ZwClose( Open->DumpFileHandle );
243         Open->DumpFileHandle=NULL;
244
245         return ntStatus;
246     }  
247     ntStatus = ObReferenceObjectByHandle(Open->DumpThreadHandle,
248         THREAD_ALL_ACCESS,
249         NULL,
250         KernelMode,
251         (PVOID*)&Open->DumpThreadObject,
252         0);
253     if ( !NT_SUCCESS( ntStatus ) )
254     {
255         IF_LOUD(DbgPrint("NPF: Error creating dump thread, status=%x\n", ntStatus);)
256         
257         ObDereferenceObject(Open->DumpFileObject);
258         ZwClose( Open->DumpFileHandle );
259         Open->DumpFileHandle=NULL;
260
261         return ntStatus;
262     }  
263   
264     return ntStatus;
265     
266 }
267
268 //-------------------------------------------------------------------
269 // Dump Thread
270 //-------------------------------------------------------------------
271
272 VOID NPF_DumpThread(POPEN_INSTANCE Open)
273 {
274     ULONG       FrozenNic;
275
276     IF_LOUD(DbgPrint("NPF: In the work routine.  Parameter = 0x%0x\n",Open);)
277
278     while(TRUE){
279
280         // Wait until some packets arrive or the timeout expires
281         NdisWaitEvent(&Open->DumpEvent, 5000);  
282
283         IF_LOUD(DbgPrint("NPF: Worker Thread - event signalled\n");)
284             
285         if(Open->DumpLimitReached ||
286             Open->BufSize==0){      // BufSize=0 means that this instance was closed, or that the buffer is too
287                                     // small for any capture. In both cases it is better to end the dump
288
289             IF_LOUD(DbgPrint("NPF: Worker Thread - Exiting happily\n");)
290             IF_LOUD(DbgPrint("Thread: Dumpoffset=%I64d\n",Open->DumpOffset.QuadPart);)
291
292             PsTerminateSystemThread(STATUS_SUCCESS);
293             return;
294         }
295         
296         NdisResetEvent(&Open->DumpEvent);
297
298         // Write the content of the buffer to the file
299         if(NPF_SaveCurrentBuffer(Open) != STATUS_SUCCESS){
300             PsTerminateSystemThread(STATUS_SUCCESS);
301             return;
302         }
303     
304     }
305     
306 }
307
308 //-------------------------------------------------------------------
309
310 NTSTATUS NPF_SaveCurrentBuffer(POPEN_INSTANCE Open)
311 {
312     UINT        Thead;
313     UINT        Ttail;
314     UINT        TLastByte;
315     PUCHAR      CurrBuff;
316     NTSTATUS    ntStatus;
317     IO_STATUS_BLOCK IoStatus;
318     PMDL        lMdl;
319     UINT        SizeToDump;
320
321     
322     Thead=Open->Bhead;
323     Ttail=Open->Btail;
324     TLastByte=Open->BLastByte;
325     
326     IF_LOUD(DbgPrint("NPF: NPF_SaveCurrentBuffer.\n");)
327
328     // Get the address of the buffer
329     CurrBuff=Open->Buffer;
330     //
331     // Fill the application buffer
332     //
333     if( Ttail < Thead )
334     {
335         if(Open->MaxDumpBytes &&
336             (UINT)Open->DumpOffset.QuadPart + GetBuffOccupation(Open) > Open->MaxDumpBytes)
337         {
338             // Size limit reached
339             UINT PktLen;
340             
341             SizeToDump = 0;
342             
343             // Scan the buffer to detect the exact amount of data to save
344             while(TRUE){
345                 PktLen = ((struct sf_pkthdr*)(CurrBuff + Thead + SizeToDump))->caplen + sizeof(struct sf_pkthdr);
346                 
347                 if((UINT)Open->DumpOffset.QuadPart + SizeToDump + PktLen > Open->MaxDumpBytes)
348                     break;
349                 
350                 SizeToDump += PktLen;
351             }
352             
353         }
354         else
355             SizeToDump = TLastByte-Thead;
356         
357         lMdl=IoAllocateMdl(CurrBuff+Thead, SizeToDump, FALSE, FALSE, NULL);
358         if (lMdl == NULL)
359         {
360             // No memory: stop dump
361             IF_LOUD(DbgPrint("NPF: dump thread: Failed to allocate Mdl\n");)
362             return STATUS_UNSUCCESSFUL;
363         }
364         
365         MmBuildMdlForNonPagedPool(lMdl);
366         
367         // Write to disk
368         NPF_WriteDumpFile(Open->DumpFileObject,
369             &Open->DumpOffset,
370             SizeToDump,
371             lMdl,
372             &IoStatus);
373         
374         IoFreeMdl(lMdl);
375         
376         if(!NT_SUCCESS(IoStatus.Status)){
377             // Error
378             return STATUS_UNSUCCESSFUL;
379         }
380         
381         if(SizeToDump != TLastByte-Thead){
382             // Size limit reached.
383             Open->DumpLimitReached = TRUE;
384     
385             // Awake the application
386             KeSetEvent(Open->ReadEvent,0,FALSE);
387
388             return STATUS_UNSUCCESSFUL;
389         }
390         
391         // Update the packet buffer
392         Open->DumpOffset.QuadPart+=(TLastByte-Thead);
393         Open->BLastByte=Ttail;
394         Open->Bhead=0;
395     }
396
397     if( Ttail > Thead ){
398         
399         if(Open->MaxDumpBytes &&
400             (UINT)Open->DumpOffset.QuadPart + GetBuffOccupation(Open) > Open->MaxDumpBytes)
401         {
402             // Size limit reached
403             UINT PktLen;
404                         
405             SizeToDump = 0;
406             
407             // Scan the buffer to detect the exact amount of data to save
408             while(Thead + SizeToDump < Ttail){
409
410                 PktLen = ((struct sf_pkthdr*)(CurrBuff + Thead + SizeToDump))->caplen + sizeof(struct sf_pkthdr);
411                 
412                 if((UINT)Open->DumpOffset.QuadPart + SizeToDump + PktLen > Open->MaxDumpBytes)
413                     break;
414                 
415                 SizeToDump += PktLen;
416             }
417             
418         }
419         else
420             SizeToDump = Ttail-Thead;
421                 
422         lMdl=IoAllocateMdl(CurrBuff+Thead, SizeToDump, FALSE, FALSE, NULL);
423         if (lMdl == NULL)
424         {
425             // No memory: stop dump
426             IF_LOUD(DbgPrint("NPF: dump thread: Failed to allocate Mdl\n");)
427             return STATUS_UNSUCCESSFUL;
428         }
429         
430         MmBuildMdlForNonPagedPool(lMdl);
431         
432         // Write to disk
433         NPF_WriteDumpFile(Open->DumpFileObject,
434             &Open->DumpOffset,
435             SizeToDump,
436             lMdl,
437             &IoStatus);
438         
439         IoFreeMdl(lMdl);
440         
441         if(!NT_SUCCESS(IoStatus.Status)){
442             // Error
443             return STATUS_UNSUCCESSFUL;
444         }
445         
446         if(SizeToDump != Ttail-Thead){
447             // Size limit reached.
448             Open->DumpLimitReached = TRUE;
449
450             // Awake the application
451             KeSetEvent(Open->ReadEvent,0,FALSE);
452             
453             return STATUS_UNSUCCESSFUL;
454         }
455         
456         // Update the packet buffer
457         Open->DumpOffset.QuadPart+=(Ttail-Thead);           
458         Open->Bhead=Ttail;
459         
460     }
461
462     return STATUS_SUCCESS;
463 }
464
465 //-------------------------------------------------------------------
466
467 NTSTATUS NPF_CloseDumpFile(POPEN_INSTANCE Open){
468     NTSTATUS    ntStatus;
469     IO_STATUS_BLOCK IoStatus;
470     PMDL        WriteMdl;
471     PUCHAR      VMBuff;
472     UINT        VMBufLen;
473
474
475     IF_LOUD(DbgPrint("NPF: NPF_CloseDumpFile.\n");)
476     IF_LOUD(DbgPrint("Dumpoffset=%d\n",Open->DumpOffset.QuadPart);)
477
478 DbgPrint("1\n");
479     // Consistency check
480     if(Open->DumpFileHandle == NULL)
481         return STATUS_UNSUCCESSFUL;
482
483 DbgPrint("2\n");
484     ZwClose( Open->DumpFileHandle );
485
486     ObDereferenceObject(Open->DumpFileObject);
487 /*
488     if(Open->DumpLimitReached == TRUE)
489         // Limit already reached: don't save the rest of the buffer.
490         return STATUS_SUCCESS;
491 */
492 DbgPrint("3\n");
493
494     NPF_OpenDumpFile(Open,&Open->DumpFileName, TRUE);
495
496     // Flush the buffer to file 
497     NPF_SaveCurrentBuffer(Open);
498
499     // Close The file
500     ObDereferenceObject(Open->DumpFileObject);
501     ZwClose( Open->DumpFileHandle );
502     
503     Open->DumpFileHandle = NULL;
504
505     ObDereferenceObject(Open->DumpFileObject);
506
507     return STATUS_SUCCESS;
508 }
509
510 //-------------------------------------------------------------------
511
512 #ifndef __GNUC__
513 static NTSTATUS 
514 #else
515 NTSTATUS STDCALL
516 #endif
517 PacketDumpCompletion(PDEVICE_OBJECT DeviceObject,
518                                 PIRP Irp,
519                                 PVOID Context)
520 {
521
522     // Copy the status information back into the "user" IOSB
523     *Irp->UserIosb = Irp->IoStatus;
524     
525     // Wake up the mainline code
526     KeSetEvent(Irp->UserEvent, 0, FALSE);
527           
528     return STATUS_MORE_PROCESSING_REQUIRED;
529 }
530
531 //-------------------------------------------------------------------
532
533 VOID NPF_WriteDumpFile(PFILE_OBJECT FileObject,
534                                 PLARGE_INTEGER Offset,
535                                 ULONG Length,
536                                 PMDL Mdl,
537                                 PIO_STATUS_BLOCK IoStatusBlock)
538 {
539     PIRP irp;
540     KEVENT event;
541     PIO_STACK_LOCATION ioStackLocation;
542     PDEVICE_OBJECT fsdDevice = IoGetRelatedDeviceObject(FileObject);
543     NTSTATUS Status;
544  
545     // Set up the event we'll use
546     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
547     
548     // Allocate and build the IRP we'll be sending to the FSD
549     irp = IoAllocateIrp(fsdDevice->StackSize, FALSE);
550
551     if (!irp) {
552         // Allocation failed, presumably due to memory allocation failure
553         IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
554         IoStatusBlock->Information = 0;
555
556         return;
557     }
558
559     irp->MdlAddress = Mdl;
560     irp->UserEvent = &event;
561     irp->UserIosb = IoStatusBlock;
562     irp->Tail.Overlay.Thread = PsGetCurrentThread();
563     irp->Tail.Overlay.OriginalFileObject= FileObject;
564     irp->RequestorMode = KernelMode;
565
566     // Indicate that this is a WRITE operation
567     irp->Flags = IRP_WRITE_OPERATION;
568
569     // Set up the next I/O stack location
570     ioStackLocation = IoGetNextIrpStackLocation(irp);
571     ioStackLocation->MajorFunction = IRP_MJ_WRITE;
572     ioStackLocation->MinorFunction = 0;
573     ioStackLocation->DeviceObject = fsdDevice;
574     ioStackLocation->FileObject = FileObject;
575     IoSetCompletionRoutine(irp, PacketDumpCompletion, 0, TRUE, TRUE, TRUE);
576     ioStackLocation->Parameters.Write.Length = Length;
577     ioStackLocation->Parameters.Write.ByteOffset = *Offset;
578
579
580     // Send it on.  Ignore the return code
581     (void) IoCallDriver(fsdDevice, irp);
582
583     // Wait for the I/O to complete.
584     KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);
585
586     // Free the IRP now that we are done with it
587     IoFreeIrp(irp);
588
589     return;
590 }