reactos/reactos/drivers/net/packet/read.c
Robert Dickenson 05a8cab618 Revisted the packet capture driver and updated with fixes in latest 3.0a4 release.
Seems I'll need to do some work on the SCM before this'll be running for me.

svn path=/trunk/; revision=3883
2002-12-21 04:46:09 +00:00

681 lines
18 KiB
C

/*
* Copyright (c) 1999, 2000
* Politecnico di Torino. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the Politecnico
* di Torino, and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifdef _MSC_VER
#include <stdarg.h>
#include <ntddk.h>
#include <ntiologc.h>
#include <ndis.h>
#else
#include <ddk/ntddk.h>
#include <net/ndis.h>
#define NdisMoveMappedMemory(Destination,Source,Length) RtlCopyMemory(Destination,Source,Length)
#define NdisZeroMappedMemory(Destination,Length) RtlZeroMemory(Destination,Length)
#define NdisReinitializePacket(Packet) \
{ \
(Packet)->Private.Head = (PNDIS_BUFFER)NULL; \
(Packet)->Private.ValidCounts = FALSE; \
}
#endif
#include "debug.h"
#include "packet.h"
#include "win_bpf.h"
#include "tme.h"
#include "time_calls.h"
extern struct time_conv G_Start_Time; // from openclos.c
//-------------------------------------------------------------------
UINT GetBuffOccupation(POPEN_INSTANCE Open)
{
UINT Occupation;
NdisAcquireSpinLock( &Open->BufLock );
if(Open->Btail >= Open->Bhead) Occupation = Open->Btail-Open->Bhead;
else Occupation = Open->BLastByte-Open->Bhead+Open->Btail;
NdisReleaseSpinLock( &Open->BufLock );
return Occupation;
}
//-------------------------------------------------------------------
void PacketMoveMem(PVOID Destination, PVOID Source, ULONG Length, UINT *Bhead)
{
ULONG WordLength;
UINT n,i,NBlocks;
WordLength=Length>>2;
NBlocks=WordLength>>8;
for(n=0;n<NBlocks;n++){
for(i=0;i<256;i++){
*((PULONG)Destination)++=*((PULONG)Source)++;
}
*Bhead+=1024;
}
n=WordLength-(NBlocks<<8);
for(i=0;i<n;i++){
*((PULONG)Destination)++=*((PULONG)Source)++;
}
*Bhead+=n<<2;
n=Length-(WordLength<<2);
for(i=0;i<n;i++){
*((PUCHAR)Destination)++=*((PUCHAR)Source)++;
}
*Bhead+=n;
}
//-------------------------------------------------------------------
NTSTATUS
NPF_Read(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
POPEN_INSTANCE Open;
PIO_STACK_LOCATION IrpSp;
PUCHAR packp;
ULONG Input_Buffer_Length;
UINT Thead;
UINT Ttail;
UINT TLastByte;
PUCHAR CurrBuff;
LARGE_INTEGER CapTime;
LARGE_INTEGER TimeFreq;
struct bpf_hdr *header;
KIRQL Irql;
PUCHAR UserPointer;
ULONG bytecopy;
UINT SizeToCopy;
UINT PktLen;
IF_LOUD(DbgPrint("NPF: Read\n");)
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Open=IrpSp->FileObject->FsContext;
if( Open->Bound == FALSE ){
// The Network adapter was removed.
EXIT_FAILURE(0);
}
if( Open->mode & MODE_DUMP && Open->DumpFileHandle == NULL ){
// this instance is in dump mode, but the dump file has still not been opened
EXIT_FAILURE(0);
}
//See if the buffer is full enough to be copied
if( GetBuffOccupation(Open) <= Open->MinToCopy || Open->mode & MODE_DUMP )
{
//wait until some packets arrive or the timeout expires
if(Open->TimeOut.QuadPart != (LONGLONG)IMMEDIATE)
KeWaitForSingleObject(Open->ReadEvent,
UserRequest,
KernelMode,
TRUE,
(Open->TimeOut.QuadPart == (LONGLONG)0)? NULL: &(Open->TimeOut));
KeClearEvent(Open->ReadEvent);
if(Open->mode & MODE_STAT){ //this capture instance is in statistics mode
CurrBuff=(PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress);
//fill the bpf header for this packet
header=(struct bpf_hdr*)CurrBuff;
GET_TIME(&header->bh_tstamp,&G_Start_Time);
if(Open->mode & MODE_DUMP){
*(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr)+16)=Open->DumpOffset.QuadPart;
header->bh_caplen=24;
header->bh_datalen=24;
Irp->IoStatus.Information = 24 + sizeof(struct bpf_hdr);
}
else{
header->bh_caplen=16;
header->bh_datalen=16;
header->bh_hdrlen=sizeof(struct bpf_hdr);
Irp->IoStatus.Information = 16 + sizeof(struct bpf_hdr);
}
*(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr))=Open->Npackets.QuadPart;
*(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr)+8)=Open->Nbytes.QuadPart;
//reset the countetrs
NdisAcquireSpinLock( &Open->CountersLock );
Open->Npackets.QuadPart=0;
Open->Nbytes.QuadPart=0;
NdisReleaseSpinLock( &Open->CountersLock );
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
if(Open->mode==MODE_MON) //this capture instance is in monitor mode
{
PTME_DATA data;
ULONG cnt;
ULONG block_size;
PUCHAR tmp;
UserPointer=MmGetSystemAddressForMdl(Irp->MdlAddress);
if ((!IS_VALIDATED(Open->tme.validated_blocks,Open->tme.active_read))||(IrpSp->Parameters.Read.Length<sizeof(struct bpf_hdr)))
{
EXIT_FAILURE(0);
}
header=(struct bpf_hdr*)UserPointer;
GET_TIME(&header->bh_tstamp,&G_Start_Time);
header->bh_hdrlen=sizeof(struct bpf_hdr);
//moves user memory pointer
UserPointer+=sizeof(struct bpf_hdr);
//calculus of data to be copied
//if the user buffer is smaller than data to be copied,
//only some data will be copied
data=&Open->tme.block_data[Open->tme.active_read];
if (data->last_read.tv_sec!=0)
data->last_read=header->bh_tstamp;
bytecopy=data->block_size*data->filled_blocks;
if ((IrpSp->Parameters.Read.Length-sizeof(struct bpf_hdr))<bytecopy)
bytecopy=(IrpSp->Parameters.Read.Length-sizeof(struct bpf_hdr))/ data->block_size;
else
bytecopy=data->filled_blocks;
tmp=data->shared_memory_base_address;
block_size=data->block_size;
for (cnt=0;cnt<bytecopy;cnt++)
{
NdisAcquireSpinLock(&Open->machine_lock);
RtlCopyMemory(UserPointer,tmp,block_size);
NdisReleaseSpinLock(&Open->machine_lock);
tmp+=block_size;
UserPointer+=block_size;
}
bytecopy*=block_size;
header->bh_caplen=bytecopy;
header->bh_datalen=header->bh_caplen;
EXIT_SUCCESS(bytecopy+sizeof(struct bpf_hdr));
}
if (Open->Bhead == Open->Btail || Open->mode & MODE_DUMP)
// The timeout has expired, but the buffer is still empty (or the packets must be written to file).
// We must awake the application, returning an empty buffer.
{
EXIT_SUCCESS(0);
}
}
//
// The buffer if full enough to be copied,
//
NdisAcquireSpinLock( &Open->BufLock );
Thead = Open->Bhead;
Ttail = Open->Btail;
TLastByte = Open->BLastByte;
//get the address of the buffer
CurrBuff=Open->Buffer;
NdisReleaseSpinLock( &Open->BufLock );
Input_Buffer_Length=IrpSp->Parameters.Read.Length;
packp=(PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress);
//
//fill the application buffer
//
if(Ttail > Thead){ //first of all see if it we can copy all the buffer in one time
if((Ttail-Thead)<Input_Buffer_Length){
KeResetEvent(Open->ReadEvent);
PacketMoveMem(packp,CurrBuff+Thead,Ttail-Thead,&(Open->Bhead));
EXIT_SUCCESS(Ttail-Thead);
}
}
else if((TLastByte - Thead) < Input_Buffer_Length){
PacketMoveMem(packp, CurrBuff+Thead, TLastByte - Thead, &(Open->Bhead));
NdisAcquireSpinLock( &Open->BufLock );
Open->BLastByte = Open->Btail;
Open->Bhead = 0;
NdisReleaseSpinLock( &Open->BufLock );
EXIT_SUCCESS(TLastByte-Thead);
}
//the buffer must be scannned to determine the number of bytes to copy
SizeToCopy = 0;
while(TRUE){
if(Thead + SizeToCopy == Ttail)
break;
if(Thead + SizeToCopy == TLastByte && TLastByte != Ttail){
PacketMoveMem(packp, CurrBuff+Thead, SizeToCopy, &(Open->Bhead));
// Reset the buffer
NdisAcquireSpinLock( &Open->BufLock );
(INT)Open->BLastByte = -1;
Open->Bhead = 0;
NdisReleaseSpinLock( &Open->BufLock );
EXIT_SUCCESS(SizeToCopy);
}
// Get the size of the next packet in the buffer
PktLen = ((struct bpf_hdr*)(CurrBuff + Thead + SizeToCopy))->bh_caplen + sizeof(struct bpf_hdr);
// The length is aligned to 32-bit boundary
PktLen = Packet_WORDALIGN(PktLen);
if(SizeToCopy + PktLen > Input_Buffer_Length)
break;
SizeToCopy += PktLen;
}
PacketMoveMem(packp, CurrBuff+Thead, SizeToCopy, &(Open->Bhead));
EXIT_SUCCESS(SizeToCopy);
}
//-------------------------------------------------------------------
NDIS_STATUS NPF_tap (IN NDIS_HANDLE ProtocolBindingContext,IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer,IN UINT HeaderBufferSize,IN PVOID LookAheadBuffer,
IN UINT LookaheadBufferSize,IN UINT PacketSize)
{
POPEN_INSTANCE Open;
PNDIS_PACKET pPacketb;
ULONG SizeToTransfer;
NDIS_STATUS Status;
UINT BytesTransfered;
ULONG BufferLength;
PMDL pMdl;
LARGE_INTEGER CapTime;
LARGE_INTEGER TimeFreq;
struct bpf_hdr *header;
PUCHAR CurrBuff;
UINT Thead;
UINT Ttail;
UINT TLastByte;
UINT fres;
UINT maxbufspace;
USHORT NPFHdrSize;
UINT BufOccupation;
BOOLEAN ResetBuff = FALSE;
IF_VERY_LOUD(DbgPrint("NPF: tap\n");)
IF_VERY_LOUD(DbgPrint("HeaderBufferSize=%d, LookAheadBuffer=%d, LookaheadBufferSize=%d, PacketSize=%d\n",
HeaderBufferSize,
LookAheadBuffer,
LookaheadBufferSize,
PacketSize);)
Open= (POPEN_INSTANCE)ProtocolBindingContext;
Open->Received++; // Number of packets received by filter ++
BufOccupation = GetBuffOccupation(Open); // Get the full buffer space
if(((Open->mode&MODE_CAPT)||(Open->mode&MODE_DUMP)) && Open->BufSize - BufOccupation < PacketSize+HeaderBufferSize+sizeof(struct bpf_hdr)){
// Heuristic that drops the packet also if it possibly fits in the buffer.
// It allows to avoid filtering in critical situations when CPU is very important.
Open->Dropped++;
return NDIS_STATUS_NOT_ACCEPTED;
}
NdisAcquireSpinLock(&Open->machine_lock);
//
//Check if the lookahead buffer follows the mac header.
//If the data follow the header (i.e. there is only a buffer) a normal bpf_filter() is
//executed on the packet.
//Otherwise if there are 2 separate buffers (this could be the case of LAN emulation or
//things like this) bpf_filter_with_2_buffers() is executed.
//
if((UINT)LookAheadBuffer-(UINT)HeaderBuffer != HeaderBufferSize)
fres=bpf_filter_with_2_buffers((struct bpf_insn*)(Open->bpfprogram),
HeaderBuffer,
LookAheadBuffer,
HeaderBufferSize,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize,
&Open->mem_ex,
&Open->tme,
&G_Start_Time);
else
if(Open->Filter != NULL)
{
if (Open->bpfprogram != NULL)
{
fres=Open->Filter->Function(HeaderBuffer,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize);
// Restore the stack.
// I ignore the reason, but this instruction is needed only at kernel level
#ifndef __GNUC__
_asm add esp,12
#else
asm("add $0x12,%esp;");
#endif
}
else
fres = -1;
}
else
fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram),
HeaderBuffer,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize,
&Open->mem_ex,
&Open->tme,
&G_Start_Time);
NdisReleaseSpinLock(&Open->machine_lock);
if(Open->mode==MODE_MON)
// we are in monitor mode
{
if (fres==1)
KeSetEvent(Open->ReadEvent,0,FALSE);
return NDIS_STATUS_NOT_ACCEPTED;
}
if(fres==0)
// Packet not accepted by the filter, ignore it.
return NDIS_STATUS_NOT_ACCEPTED;
//if the filter returns -1 the whole packet must be accepted
if(fres==-1 || fres > PacketSize+HeaderBufferSize)fres=PacketSize+HeaderBufferSize;
if(Open->mode & MODE_STAT){
// we are in statistics mode
NdisAcquireSpinLock( &Open->CountersLock );
Open->Npackets.QuadPart++;
if(PacketSize+HeaderBufferSize<60)
Open->Nbytes.QuadPart+=60;
else
Open->Nbytes.QuadPart+=PacketSize+HeaderBufferSize;
// add preamble+SFD+FCS to the packet
// these values must be considered because are not part of the packet received from NDIS
Open->Nbytes.QuadPart+=12;
NdisReleaseSpinLock( &Open->CountersLock );
if(!(Open->mode & MODE_DUMP)){
return NDIS_STATUS_NOT_ACCEPTED;
}
}
if(Open->BufSize==0)return NDIS_STATUS_NOT_ACCEPTED;
if(Open->mode & MODE_DUMP && Open->MaxDumpPacks && (UINT)Open->Accepted > Open->MaxDumpPacks){
// Reached the max number of packets to save in the dump file. Discard the packet and stop the dump thread.
Open->DumpLimitReached = TRUE; // This stops the thread
// Awake the dump thread
NdisSetEvent(&Open->DumpEvent);
// Awake the application
KeSetEvent(Open->ReadEvent,0,FALSE);
return NDIS_STATUS_NOT_ACCEPTED;
}
// Calculate the correct size for the header associated with the packet
NPFHdrSize=(Open->mode==MODE_CAPT)? sizeof(struct bpf_hdr): sizeof(struct sf_pkthdr);
NdisAcquireSpinLock( &Open->BufLock );
Thead=Open->Bhead;
Ttail=Open->Btail;
TLastByte = Open->BLastByte;
NdisReleaseSpinLock( &Open->BufLock );
maxbufspace=Packet_WORDALIGN(fres+NPFHdrSize);
if(Ttail+maxbufspace >= Open->BufSize){
if(Thead <= maxbufspace)
{
Open->Dropped++;
return NDIS_STATUS_NOT_ACCEPTED;
}
else{
Ttail=0;
ResetBuff = TRUE;
}
}
if (Thead > Ttail && (Thead-Ttail) <= maxbufspace)
{
Open->Dropped++;
return NDIS_STATUS_NOT_ACCEPTED;
}
CurrBuff=Open->Buffer+Ttail;
if(LookaheadBufferSize != PacketSize || (UINT)LookAheadBuffer-(UINT)HeaderBuffer != HeaderBufferSize)
{
// Allocate an MDL to map the portion of the buffer following the header
pMdl=IoAllocateMdl(CurrBuff+HeaderBufferSize+LookaheadBufferSize+NPFHdrSize,
maxbufspace,
FALSE,
FALSE,
NULL);
if (pMdl == NULL)
{
// Unable to map the memory: packet lost
IF_LOUD(DbgPrint("NPF: Read-Failed to allocate Mdl\n");)
Open->Dropped++;
return NDIS_STATUS_NOT_ACCEPTED;
}
MmBuildMdlForNonPagedPool(pMdl);
//allocate the packet from NDIS
NdisAllocatePacket(&Status, &pPacketb, Open->PacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
IF_LOUD(DbgPrint("NPF: Tap - No free packets\n");)
IoFreeMdl(pMdl);
Open->Dropped++;
return NDIS_STATUS_NOT_ACCEPTED;
}
//link the buffer to the packet
NdisChainBufferAtFront(pPacketb,pMdl);
BufferLength=fres-HeaderBufferSize;
//Find out how much to transfer
SizeToTransfer = (PacketSize < BufferLength) ? PacketSize : BufferLength;
//copy the ethernet header into buffer
NdisMoveMappedMemory((CurrBuff)+NPFHdrSize,HeaderBuffer,HeaderBufferSize);
//Copy the look ahead buffer
if(LookaheadBufferSize)
{
NdisMoveMappedMemory((CurrBuff) + NPFHdrSize + HeaderBufferSize,
LookAheadBuffer,
(SizeToTransfer < LookaheadBufferSize)? SizeToTransfer : LookaheadBufferSize );
SizeToTransfer = (SizeToTransfer > LookaheadBufferSize)?
SizeToTransfer - LookaheadBufferSize : 0;
}
Open->TransferMdl=pMdl;
if(SizeToTransfer)
{
//Call the Mac to transfer the packet
NdisTransferData(&Status,
Open->AdapterHandle,
MacReceiveContext,
LookaheadBufferSize,
SizeToTransfer,
pPacketb,
&BytesTransfered);
}
else{
BytesTransfered = 0;
}
}
else
{
// The whole packet is in the lookahead buffer, we can avoid the call to NdisTransferData.
// This allows us to avoid the allocation of the MDL and the NDIS packet as well
RtlCopyMemory((CurrBuff) + NPFHdrSize,
HeaderBuffer,
HeaderBufferSize + LookaheadBufferSize);
BytesTransfered = 0;
Open->TransferMdl = NULL;
Status = NDIS_STATUS_SUCCESS;
}
if (Status != NDIS_STATUS_FAILURE)
{
Open->Accepted++; // Increase the accepted packets counter
if( fres > (BytesTransfered+HeaderBufferSize+LookaheadBufferSize) )
fres = BytesTransfered+HeaderBufferSize+LookaheadBufferSize;
//
// Build the header
//
header=(struct bpf_hdr*)CurrBuff;
GET_TIME(&header->bh_tstamp,&G_Start_Time);
header->bh_caplen=fres;
header->bh_datalen=PacketSize+HeaderBufferSize;
if(Open->mode==MODE_CAPT){
header->bh_hdrlen=NPFHdrSize;
// Don't align if the packet goes to disk
Ttail+=Packet_WORDALIGN(fres + NPFHdrSize);
}
else
Ttail+=fres+NPFHdrSize;
//update the buffer
NdisAcquireSpinLock( &Open->BufLock );
if(ResetBuff){
Open->BLastByte = Open->Btail;
}
Open->Btail=Ttail;
NdisReleaseSpinLock( &Open->BufLock );
}
if (Status != NDIS_STATUS_PENDING){
if( Open->TransferMdl != NULL)
// Complete the request and free the buffers
NPF_TransferDataComplete(Open,pPacketb,Status,fres);
else{
// Unfreeze the consumer
if(GetBuffOccupation(Open)>Open->MinToCopy){
if(Open->mode & MODE_DUMP){
NdisSetEvent(&Open->DumpEvent);
}
else
KeSetEvent(Open->ReadEvent,0,FALSE);
}
}
}
return NDIS_STATUS_SUCCESS;
}
//-------------------------------------------------------------------
VOID NPF_TransferDataComplete (IN NDIS_HANDLE ProtocolBindingContext,IN PNDIS_PACKET pPacket,
IN NDIS_STATUS Status,IN UINT BytesTransfered)
{
POPEN_INSTANCE Open;
IF_LOUD(DbgPrint("NPF: TransferDataComplete\n");)
Open= (POPEN_INSTANCE)ProtocolBindingContext;
IoFreeMdl(Open->TransferMdl);
//recylcle the packet
NdisReinitializePacket(pPacket);
//Put the packet on the free queue
NdisFreePacket(pPacket);
// Unfreeze the consumer
if(GetBuffOccupation(Open)>Open->MinToCopy){
if(Open->mode & MODE_DUMP){
NdisSetEvent(&Open->DumpEvent);
}
else
KeSetEvent(Open->ReadEvent,0,FALSE);
}
return;
}
//-------------------------------------------------------------------
VOID NPF_ReceiveComplete(IN NDIS_HANDLE ProtocolBindingContext)
{
IF_VERY_LOUD(DbgPrint("NPF: NPF_ReceiveComplete\n");)
return;
}