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