2 * Copyright (c) 1999, 2000
3 * Politecnico di Torino. All rights reserved.
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
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.
28 #include <ddk/ntddk.h>
31 #define NdisMoveMappedMemory(Destination,Source,Length) RtlCopyMemory(Destination,Source,Length)
32 #define NdisZeroMappedMemory(Destination,Length) RtlZeroMemory(Destination,Length)
33 #define NdisReinitializePacket(Packet) \
35 (Packet)->Private.Head = (PNDIS_BUFFER)NULL; \
36 (Packet)->Private.ValidCounts = FALSE; \
47 #include "time_calls.h"
49 extern struct time_conv G_Start_Time; // from openclos.c
51 //-------------------------------------------------------------------
53 UINT GetBuffOccupation(POPEN_INSTANCE Open)
57 NdisAcquireSpinLock( &Open->BufLock );
59 if(Open->Btail >= Open->Bhead) Occupation = Open->Btail-Open->Bhead;
60 else Occupation = Open->BLastByte-Open->Bhead+Open->Btail;
62 NdisReleaseSpinLock( &Open->BufLock );
67 //-------------------------------------------------------------------
69 void PacketMoveMem(PVOID Destination, PVOID Source, ULONG Length, UINT *Bhead)
75 NBlocks=WordLength>>8;
77 for(n=0;n<NBlocks;n++){
79 *((PULONG)Destination)++=*((PULONG)Source)++;
84 n=WordLength-(NBlocks<<8);
86 *((PULONG)Destination)++=*((PULONG)Source)++;
90 n=Length-(WordLength<<2);
92 *((PUCHAR)Destination)++=*((PUCHAR)Source)++;
97 //-------------------------------------------------------------------
100 NPF_Read(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
103 PIO_STACK_LOCATION IrpSp;
105 ULONG Input_Buffer_Length;
110 LARGE_INTEGER CapTime;
111 LARGE_INTEGER TimeFreq;
112 struct bpf_hdr *header;
119 IF_LOUD(DbgPrint("NPF: Read\n");)
121 IrpSp = IoGetCurrentIrpStackLocation(Irp);
122 Open=IrpSp->FileObject->FsContext;
124 if( Open->Bound == FALSE ){
125 // The Network adapter was removed.
129 if( Open->mode & MODE_DUMP && Open->DumpFileHandle == NULL ){
130 // this instance is in dump mode, but the dump file has still not been opened
134 //See if the buffer is full enough to be copied
135 if( GetBuffOccupation(Open) <= Open->MinToCopy || Open->mode & MODE_DUMP )
137 //wait until some packets arrive or the timeout expires
138 if(Open->TimeOut.QuadPart != (LONGLONG)IMMEDIATE)
139 KeWaitForSingleObject(Open->ReadEvent,
143 (Open->TimeOut.QuadPart == (LONGLONG)0)? NULL: &(Open->TimeOut));
145 KeClearEvent(Open->ReadEvent);
147 if(Open->mode & MODE_STAT){ //this capture instance is in statistics mode
148 CurrBuff=(PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress);
150 //fill the bpf header for this packet
151 header=(struct bpf_hdr*)CurrBuff;
152 GET_TIME(&header->bh_tstamp,&G_Start_Time);
154 if(Open->mode & MODE_DUMP){
155 *(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr)+16)=Open->DumpOffset.QuadPart;
156 header->bh_caplen=24;
157 header->bh_datalen=24;
158 Irp->IoStatus.Information = 24 + sizeof(struct bpf_hdr);
161 header->bh_caplen=16;
162 header->bh_datalen=16;
163 header->bh_hdrlen=sizeof(struct bpf_hdr);
164 Irp->IoStatus.Information = 16 + sizeof(struct bpf_hdr);
167 *(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr))=Open->Npackets.QuadPart;
168 *(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr)+8)=Open->Nbytes.QuadPart;
170 //reset the countetrs
171 NdisAcquireSpinLock( &Open->CountersLock );
172 Open->Npackets.QuadPart=0;
173 Open->Nbytes.QuadPart=0;
174 NdisReleaseSpinLock( &Open->CountersLock );
176 Irp->IoStatus.Status = STATUS_SUCCESS;
177 IoCompleteRequest(Irp, IO_NO_INCREMENT);
179 return STATUS_SUCCESS;
182 if(Open->mode==MODE_MON) //this capture instance is in monitor mode
189 UserPointer=MmGetSystemAddressForMdl(Irp->MdlAddress);
191 if ((!IS_VALIDATED(Open->tme.validated_blocks,Open->tme.active_read))||(IrpSp->Parameters.Read.Length<sizeof(struct bpf_hdr)))
196 header=(struct bpf_hdr*)UserPointer;
198 GET_TIME(&header->bh_tstamp,&G_Start_Time);
201 header->bh_hdrlen=sizeof(struct bpf_hdr);
204 //moves user memory pointer
205 UserPointer+=sizeof(struct bpf_hdr);
207 //calculus of data to be copied
208 //if the user buffer is smaller than data to be copied,
209 //only some data will be copied
210 data=&Open->tme.block_data[Open->tme.active_read];
212 if (data->last_read.tv_sec!=0)
213 data->last_read=header->bh_tstamp;
216 bytecopy=data->block_size*data->filled_blocks;
218 if ((IrpSp->Parameters.Read.Length-sizeof(struct bpf_hdr))<bytecopy)
219 bytecopy=(IrpSp->Parameters.Read.Length-sizeof(struct bpf_hdr))/ data->block_size;
221 bytecopy=data->filled_blocks;
223 tmp=data->shared_memory_base_address;
224 block_size=data->block_size;
226 for (cnt=0;cnt<bytecopy;cnt++)
228 NdisAcquireSpinLock(&Open->machine_lock);
229 RtlCopyMemory(UserPointer,tmp,block_size);
230 NdisReleaseSpinLock(&Open->machine_lock);
232 UserPointer+=block_size;
235 bytecopy*=block_size;
237 header->bh_caplen=bytecopy;
238 header->bh_datalen=header->bh_caplen;
240 EXIT_SUCCESS(bytecopy+sizeof(struct bpf_hdr));
243 if (Open->Bhead == Open->Btail || Open->mode & MODE_DUMP)
244 // The timeout has expired, but the buffer is still empty (or the packets must be written to file).
245 // We must awake the application, returning an empty buffer.
253 // The buffer if full enough to be copied,
255 NdisAcquireSpinLock( &Open->BufLock );
259 TLastByte = Open->BLastByte;
261 //get the address of the buffer
262 CurrBuff=Open->Buffer;
264 NdisReleaseSpinLock( &Open->BufLock );
266 Input_Buffer_Length=IrpSp->Parameters.Read.Length;
267 packp=(PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress);
271 //fill the application buffer
273 if(Ttail > Thead){ //first of all see if it we can copy all the buffer in one time
274 if((Ttail-Thead)<Input_Buffer_Length){
275 KeResetEvent(Open->ReadEvent);
277 PacketMoveMem(packp,CurrBuff+Thead,Ttail-Thead,&(Open->Bhead));
278 EXIT_SUCCESS(Ttail-Thead);
281 else if((TLastByte - Thead) < Input_Buffer_Length){
282 PacketMoveMem(packp, CurrBuff+Thead, TLastByte - Thead, &(Open->Bhead));
284 NdisAcquireSpinLock( &Open->BufLock );
286 Open->BLastByte = Open->Btail;
289 NdisReleaseSpinLock( &Open->BufLock );
291 EXIT_SUCCESS(TLastByte-Thead);
294 //the buffer must be scannned to determine the number of bytes to copy
297 if(Thead + SizeToCopy == Ttail)
300 if(Thead + SizeToCopy == TLastByte && TLastByte != Ttail){
302 PacketMoveMem(packp, CurrBuff+Thead, SizeToCopy, &(Open->Bhead));
304 NdisAcquireSpinLock( &Open->BufLock );
305 (INT)Open->BLastByte = -1;
307 NdisReleaseSpinLock( &Open->BufLock );
309 EXIT_SUCCESS(SizeToCopy);
312 // Get the size of the next packet in the buffer
313 PktLen = ((struct bpf_hdr*)(CurrBuff + Thead + SizeToCopy))->bh_caplen + sizeof(struct bpf_hdr);
315 // The length is aligned to 32-bit boundary
316 PktLen = Packet_WORDALIGN(PktLen);
318 if(SizeToCopy + PktLen > Input_Buffer_Length)
321 SizeToCopy += PktLen;
324 PacketMoveMem(packp, CurrBuff+Thead, SizeToCopy, &(Open->Bhead));
325 EXIT_SUCCESS(SizeToCopy);
329 //-------------------------------------------------------------------
331 NDIS_STATUS NPF_tap (IN NDIS_HANDLE ProtocolBindingContext,IN NDIS_HANDLE MacReceiveContext,
332 IN PVOID HeaderBuffer,IN UINT HeaderBufferSize,IN PVOID LookAheadBuffer,
333 IN UINT LookaheadBufferSize,IN UINT PacketSize)
336 PNDIS_PACKET pPacketb;
337 ULONG SizeToTransfer;
339 UINT BytesTransfered;
342 LARGE_INTEGER CapTime;
343 LARGE_INTEGER TimeFreq;
344 struct bpf_hdr *header;
353 BOOLEAN ResetBuff = FALSE;
355 IF_VERY_LOUD(DbgPrint("NPF: tap\n");)
356 IF_VERY_LOUD(DbgPrint("HeaderBufferSize=%d, LookAheadBuffer=%d, LookaheadBufferSize=%d, PacketSize=%d\n",
362 Open= (POPEN_INSTANCE)ProtocolBindingContext;
364 Open->Received++; // Number of packets received by filter ++
366 BufOccupation = GetBuffOccupation(Open); // Get the full buffer space
368 if(((Open->mode&MODE_CAPT)||(Open->mode&MODE_DUMP)) && Open->BufSize - BufOccupation < PacketSize+HeaderBufferSize+sizeof(struct bpf_hdr)){
369 // Heuristic that drops the packet also if it possibly fits in the buffer.
370 // It allows to avoid filtering in critical situations when CPU is very important.
372 return NDIS_STATUS_NOT_ACCEPTED;
375 NdisAcquireSpinLock(&Open->machine_lock);
378 //Check if the lookahead buffer follows the mac header.
379 //If the data follow the header (i.e. there is only a buffer) a normal bpf_filter() is
380 //executed on the packet.
381 //Otherwise if there are 2 separate buffers (this could be the case of LAN emulation or
382 //things like this) bpf_filter_with_2_buffers() is executed.
384 if((UINT)LookAheadBuffer-(UINT)HeaderBuffer != HeaderBufferSize)
385 fres=bpf_filter_with_2_buffers((struct bpf_insn*)(Open->bpfprogram),
389 PacketSize+HeaderBufferSize,
390 LookaheadBufferSize+HeaderBufferSize,
397 if(Open->Filter != NULL)
399 if (Open->bpfprogram != NULL)
401 fres=Open->Filter->Function(HeaderBuffer,
402 PacketSize+HeaderBufferSize,
403 LookaheadBufferSize+HeaderBufferSize);
405 // Restore the stack.
406 // I ignore the reason, but this instruction is needed only at kernel level
410 asm("add $0x12,%esp;");
417 fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram),
419 PacketSize+HeaderBufferSize,
420 LookaheadBufferSize+HeaderBufferSize,
425 NdisReleaseSpinLock(&Open->machine_lock);
427 if(Open->mode==MODE_MON)
428 // we are in monitor mode
431 KeSetEvent(Open->ReadEvent,0,FALSE);
432 return NDIS_STATUS_NOT_ACCEPTED;
437 // Packet not accepted by the filter, ignore it.
438 return NDIS_STATUS_NOT_ACCEPTED;
440 //if the filter returns -1 the whole packet must be accepted
441 if(fres==-1 || fres > PacketSize+HeaderBufferSize)fres=PacketSize+HeaderBufferSize;
443 if(Open->mode & MODE_STAT){
444 // we are in statistics mode
445 NdisAcquireSpinLock( &Open->CountersLock );
447 Open->Npackets.QuadPart++;
449 if(PacketSize+HeaderBufferSize<60)
450 Open->Nbytes.QuadPart+=60;
452 Open->Nbytes.QuadPart+=PacketSize+HeaderBufferSize;
453 // add preamble+SFD+FCS to the packet
454 // these values must be considered because are not part of the packet received from NDIS
455 Open->Nbytes.QuadPart+=12;
457 NdisReleaseSpinLock( &Open->CountersLock );
459 if(!(Open->mode & MODE_DUMP)){
460 return NDIS_STATUS_NOT_ACCEPTED;
464 if(Open->BufSize==0)return NDIS_STATUS_NOT_ACCEPTED;
466 if(Open->mode & MODE_DUMP && Open->MaxDumpPacks && (UINT)Open->Accepted > Open->MaxDumpPacks){
467 // Reached the max number of packets to save in the dump file. Discard the packet and stop the dump thread.
468 Open->DumpLimitReached = TRUE; // This stops the thread
469 // Awake the dump thread
470 NdisSetEvent(&Open->DumpEvent);
472 // Awake the application
473 KeSetEvent(Open->ReadEvent,0,FALSE);
475 return NDIS_STATUS_NOT_ACCEPTED;
478 // Calculate the correct size for the header associated with the packet
479 NPFHdrSize=(Open->mode==MODE_CAPT)? sizeof(struct bpf_hdr): sizeof(struct sf_pkthdr);
481 NdisAcquireSpinLock( &Open->BufLock );
485 TLastByte = Open->BLastByte;
487 NdisReleaseSpinLock( &Open->BufLock );
489 maxbufspace=Packet_WORDALIGN(fres+NPFHdrSize);
491 if(Ttail+maxbufspace >= Open->BufSize){
492 if(Thead <= maxbufspace)
495 return NDIS_STATUS_NOT_ACCEPTED;
503 if (Thead > Ttail && (Thead-Ttail) <= maxbufspace)
506 return NDIS_STATUS_NOT_ACCEPTED;
509 CurrBuff=Open->Buffer+Ttail;
511 if(LookaheadBufferSize != PacketSize || (UINT)LookAheadBuffer-(UINT)HeaderBuffer != HeaderBufferSize)
513 // Allocate an MDL to map the portion of the buffer following the header
514 pMdl=IoAllocateMdl(CurrBuff+HeaderBufferSize+LookaheadBufferSize+NPFHdrSize,
522 // Unable to map the memory: packet lost
523 IF_LOUD(DbgPrint("NPF: Read-Failed to allocate Mdl\n");)
525 return NDIS_STATUS_NOT_ACCEPTED;
527 MmBuildMdlForNonPagedPool(pMdl);
529 //allocate the packet from NDIS
530 NdisAllocatePacket(&Status, &pPacketb, Open->PacketPool);
531 if (Status != NDIS_STATUS_SUCCESS)
533 IF_LOUD(DbgPrint("NPF: Tap - No free packets\n");)
536 return NDIS_STATUS_NOT_ACCEPTED;
538 //link the buffer to the packet
539 NdisChainBufferAtFront(pPacketb,pMdl);
541 BufferLength=fres-HeaderBufferSize;
542 //Find out how much to transfer
543 SizeToTransfer = (PacketSize < BufferLength) ? PacketSize : BufferLength;
545 //copy the ethernet header into buffer
546 NdisMoveMappedMemory((CurrBuff)+NPFHdrSize,HeaderBuffer,HeaderBufferSize);
548 //Copy the look ahead buffer
549 if(LookaheadBufferSize)
551 NdisMoveMappedMemory((CurrBuff) + NPFHdrSize + HeaderBufferSize,
553 (SizeToTransfer < LookaheadBufferSize)? SizeToTransfer : LookaheadBufferSize );
555 SizeToTransfer = (SizeToTransfer > LookaheadBufferSize)?
556 SizeToTransfer - LookaheadBufferSize : 0;
559 Open->TransferMdl=pMdl;
563 //Call the Mac to transfer the packet
564 NdisTransferData(&Status,
579 // The whole packet is in the lookahead buffer, we can avoid the call to NdisTransferData.
580 // This allows us to avoid the allocation of the MDL and the NDIS packet as well
581 RtlCopyMemory((CurrBuff) + NPFHdrSize,
583 HeaderBufferSize + LookaheadBufferSize);
587 Open->TransferMdl = NULL;
588 Status = NDIS_STATUS_SUCCESS;
591 if (Status != NDIS_STATUS_FAILURE)
594 Open->Accepted++; // Increase the accepted packets counter
596 if( fres > (BytesTransfered+HeaderBufferSize+LookaheadBufferSize) )
597 fres = BytesTransfered+HeaderBufferSize+LookaheadBufferSize;
602 header=(struct bpf_hdr*)CurrBuff;
603 GET_TIME(&header->bh_tstamp,&G_Start_Time);
604 header->bh_caplen=fres;
605 header->bh_datalen=PacketSize+HeaderBufferSize;
606 if(Open->mode==MODE_CAPT){
607 header->bh_hdrlen=NPFHdrSize;
608 // Don't align if the packet goes to disk
609 Ttail+=Packet_WORDALIGN(fres + NPFHdrSize);
612 Ttail+=fres+NPFHdrSize;
615 NdisAcquireSpinLock( &Open->BufLock );
618 Open->BLastByte = Open->Btail;
622 NdisReleaseSpinLock( &Open->BufLock );
625 if (Status != NDIS_STATUS_PENDING){
627 if( Open->TransferMdl != NULL)
628 // Complete the request and free the buffers
629 NPF_TransferDataComplete(Open,pPacketb,Status,fres);
631 // Unfreeze the consumer
632 if(GetBuffOccupation(Open)>Open->MinToCopy){
633 if(Open->mode & MODE_DUMP){
634 NdisSetEvent(&Open->DumpEvent);
637 KeSetEvent(Open->ReadEvent,0,FALSE);
643 return NDIS_STATUS_SUCCESS;
647 //-------------------------------------------------------------------
649 VOID NPF_TransferDataComplete (IN NDIS_HANDLE ProtocolBindingContext,IN PNDIS_PACKET pPacket,
650 IN NDIS_STATUS Status,IN UINT BytesTransfered)
654 IF_LOUD(DbgPrint("NPF: TransferDataComplete\n");)
656 Open= (POPEN_INSTANCE)ProtocolBindingContext;
658 IoFreeMdl(Open->TransferMdl);
659 //recylcle the packet
660 NdisReinitializePacket(pPacket);
661 //Put the packet on the free queue
662 NdisFreePacket(pPacket);
663 // Unfreeze the consumer
664 if(GetBuffOccupation(Open)>Open->MinToCopy){
665 if(Open->mode & MODE_DUMP){
666 NdisSetEvent(&Open->DumpEvent);
669 KeSetEvent(Open->ReadEvent,0,FALSE);
674 //-------------------------------------------------------------------
676 VOID NPF_ReceiveComplete(IN NDIS_HANDLE ProtocolBindingContext)
678 IF_VERY_LOUD(DbgPrint("NPF: NPF_ReceiveComplete\n");)