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