如何从网卡读取数据包:
Winpcap从网卡获取数据是从NPF_tap开始,NPF_tap是一个回调函数,当网卡有数据包到来时,NDis调用该函数接收数据包,该函数中首先进行了过滤,源码如下;
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)
{
/*参数说明其中,参数ProtocolBindingContext为Ndis的上下文,指向一个OPEN_INSTANCE结构体,标识数据包去往的NPF实例;参数MacReceiveContext为标识生成请求的底层NIC驱动句柄,当使用NdisTransferData函数从NIC驱动传输数据包时该参数必须被使用;参数HeaderBuffer指向NIC驱动中包含数据包头的内存;参数HeaderBufferSize为数据包头的字节长度;参数LookAheadBuffer指向NIC驱动中包含进入的对NPF有用的数据包的内存(前视缓冲区)。该值并不一定需要与数据包的实际大小一致,既然此时只有一部分可能是有用的。余下部分可以通过NDIS库的NdisTransferData函数获取;参数LookaheadBufferSize为前视缓冲区(中可用数据)的字节长度;参数PacketSize为进入的数据包总的大小,除了数据包头外。HeaderBuffer为包头缓冲,为以太网头,LookaheadBuffer为IP头开始的数据缓冲区:*/
POPEN_INSTANCE Open;
PNDIS_PACKET pPacket;
ULONG SizeToTransfer;
NDIS_STATUS Status;
UINT BytesTransfered;
ULONG BufferLength;
PMDL pMdl1,pMdl2;
LARGE_INTEGER CapTime;
LARGE_INTEGER TimeFreq;
UINT fres;
USHORT NPFHdrSize;
CpuPrivateData *LocalData;
ULONG Cpu;
struct PacketHeader *Header;
ULONG ToCopy;
ULONG increment;
ULONG i;
BOOLEAN ShouldReleaseBufferLock;
IF_VERY_LOUD(DbgPrint("NPF: tap\n");)
IF_VERY_LOUD(DbgPrint("HeaderBufferSize=%u, LookAheadBuffer=%p, LookaheadBufferSize=%u, PacketSize=%u\n",
HeaderBufferSize,
LookaheadBuffer,
LookaheadBufferSize,
PacketSize);)
Open= (POPEN_INSTANCE)ProtocolBindingContext; //获取绑定上下文
Cpu = KeGetCurrentProcessorNumber(); //获取cpu数
LocalData = &Open->CpuData[Cpu];
LocalData->Received++;
IF_LOUD(DbgPrint("Received on CPU %d \t%d\n",Cpu,LocalData->Received);)
// Open->Received++; // Number of packets received by filter ++
NdisAcquireSpinLock(&Open->MachineLock); //获取自旋锁
//
//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.
/*
/*检查前视缓冲区是否紧接着MAC头
*如果数据紧接着该头(比如,只有一个缓冲区),
*在该数据包上执行一个正常的bpf_filte函数。
*否则,如果有两个分开的缓冲区(这可能在LAN仿真或类似的情况下出现),
*就需要执行bpf_filter_with_2_buffers函数。
*/
*/
if((UINT)((PUCHAR)LookaheadBuffer-(PUCHAR)HeaderBuffer) != HeaderBufferSize)
{
#ifdef HAVE_BUGGY_TME_SUPPORT
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 // HAVE_BUGGY_TME_SUPPORT
fres=bpf_filter_with_2_buffers((struct bpf_insn*)(Open->bpfprogram),
HeaderBuffer,
LookaheadBuffer,
HeaderBufferSize,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize);
#endif // HAVE_BUGGY_TME_SUPPORT
}
else
//
// the jit filter is available on x86 (32 bit) only(一个缓冲区的情况, JIT编译器生成的过滤器只在x86(32位)的平台上有效)
//
#ifdef _X86_
if(Open->Filter != NULL)
{
if (Open->bpfprogram != NULL)
{
fres=Open->Filter->Function(HeaderBuffer,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize);
NPF_tap函数中Open->Filter->Function实际上调用的就是BPFtoX86函数,执行过滤操作。
}
else
fres = -1;
}
else
#endif //_X86_
//无JIT编译器生成的过滤器,则调用BPF过滤器
#ifdef HAVE_BUGGY_TME_SUPPORT
fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram),
HeaderBuffer,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize,
&Open->mem_ex,
&Open->tme,
&G_Start_Time);
#else //HAVE_BUGGY_TME_SUPPORT
fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram),
HeaderBuffer,
PacketSize+HeaderBufferSize,
LookaheadBufferSize+HeaderBufferSize);
#endif //HAVE_BUGGY_TME_SUPPORT
NdisReleaseSpinLock(&Open->MachineLock); //释放锁
//
// The MONITOR_MODE (aka TME extensions) is not supported on
// 64 bit architectures
//
#ifdef HAVE_BUGGY_TME_SUPPORT
if(Open->mode==MODE_MON)
// we are in monitor mode
{
if (fres==1)
{
if (Open->ReadEvent != NULL)
{
KeSetEvent(Open->ReadEvent,0,FALSE);
}
}
return NDIS_STATUS_NOT_ACCEPTED;
}
#endif //HAVE_BUGGY_TME_SUPPORT
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->Size == 0)
{
LocalData->Dropped++;
return NDIS_STATUS_NOT_ACCEPTED;
}
if(Open->mode & MODE_DUMP && Open->MaxDumpPacks) //其中MaxDumpPacks为最大保存文件的大小
{
ULONG Accepted=0;
for(i=0;i<g_NCpu;i++)
Accepted+=Open->CpuData[i].Accepted;
if( 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
if (Open->ReadEvent != NULL)
KeSetEvent(Open->ReadEvent,0,FALSE); // ReadEvent唤醒应用程序,让应用程序去读数据包
return NDIS_STATUS_NOT_ACCEPTED;
}
}
//COPIA.C//77
ShouldReleaseBufferLock = TRUE;
NdisDprAcquireSpinLock(&LocalData->BufferLock);
do
{
if (fres + sizeof(struct PacketHeader) > LocalData->Free)
{
LocalData->Dropped++; //缓冲区不够,丢弃
break;
}
if (LocalData->TransferMdl1 != NULL)
{
//
//if TransferMdl is not NULL, there is some TransferData pending (i.e. not having called TransferDataComplete, yet)
//in order to avoid buffer corruption, we drop the packet
//
LocalData->Dropped++; //调用时为避免数据覆盖,丢弃该包
break;
}
if (LookaheadBufferSize + HeaderBufferSize >= fres)
{
//
// we do not need to call NdisTransferData, either because we need only the HeaderBuffer, or because the LookaheadBuffer
// contains what we need
//
Header = (struct PacketHeader*)(LocalData->Buffer + LocalData->P);
LocalData->Accepted++;
GET_TIME(&Header->header.bh_tstamp,&G_Start_Time);
Header->SN = InterlockedIncrement(&Open->WriterSN) - 1;
Header->header.bh_caplen = fres;
Header->header.bh_datalen = PacketSize + HeaderBufferSize;
Header->header.bh_hdrlen=sizeof(struct bpf_hdr);
LocalData->P +=sizeof(struct PacketHeader);
if (LocalData->P == Open->Size)
LocalData->P = 0;
if ( fres <= HeaderBufferSize || (UINT)( (PUCHAR)LookaheadBuffer - (PUCHAR)HeaderBuffer ) == HeaderBufferSize )
{
//
//we can consider the buffer contiguous, either because we use only the data
//present in the HeaderBuffer, or because HeaderBuffer and LookaheadBuffer are contiguous
// ;-))))))
//
if (Open->Size - LocalData->P < fres)
{
//the packet will be fragmented in the buffer (aka, it will skip the buffer boundary)
//two copies!!
ToCopy = Open->Size - LocalData->P;
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P,HeaderBuffer, ToCopy);
NdisMoveMappedMemory(LocalData->Buffer + 0 , (PUCHAR)HeaderBuffer + ToCopy, fres - ToCopy);
LocalData->P = fres-ToCopy;
}
else
{
//the packet does not need to be fragmented in the buffer (aka, it doesn't skip the buffer boundary)
// ;-)))))) only ONE copy
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, fres);
LocalData->P += fres;
}
}
else
{
//HeaderBuffer and LookAhead buffer are NOT contiguous,
//AND, we need some bytes from the LookaheadBuffer, too
if (Open->Size - LocalData->P < fres)
{
//the packet will be fragmented in the buffer (aka, it will skip the buffer boundary)
if (Open->Size - LocalData->P >= HeaderBufferSize)
{
//HeaderBuffer is NOT fragmented
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, HeaderBufferSize);
LocalData->P += HeaderBufferSize;
if (LocalData->P == Open->Size)
{
//the fragmentation of the packet in the buffer is the same fragmentation
//in HeaderBuffer+LookaheadBuffer
LocalData->P=0;
NdisMoveMappedMemory(LocalData->Buffer + 0, LookaheadBuffer, fres - HeaderBufferSize);
LocalData->P += (fres - HeaderBufferSize);
}
else
{
//LookAheadBuffer is fragmented, two copies
ToCopy = Open->Size - LocalData->P;
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, LookaheadBuffer, ToCopy);
LocalData->P=0;
NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)LookaheadBuffer+ ToCopy, fres - HeaderBufferSize - ToCopy);
LocalData->P = fres - HeaderBufferSize - ToCopy;
}
}
else
{
//HeaderBuffer is fragmented in the buffer (aka, it will skip the buffer boundary)
//two copies to copy the HeaderBuffer
ToCopy = Open->Size - LocalData->P;
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, ToCopy);
LocalData->P = 0;
NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)HeaderBuffer + ToCopy, HeaderBufferSize - ToCopy);
LocalData->P = HeaderBufferSize - ToCopy;
//only one copy to copy the LookaheadBuffer
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, LookaheadBuffer, fres- HeaderBufferSize);
LocalData->P += (fres - HeaderBufferSize);
}
}
else
{
//the packet won't be fragmented in the destination buffer (aka, it won't skip the buffer boundary)
//two copies, the former to copy the HeaderBuffer, the latter to copy the LookaheadBuffer
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, HeaderBuffer, HeaderBufferSize);
LocalData->P += HeaderBufferSize;
NdisMoveMappedMemory(LocalData->Buffer + LocalData->P, LookaheadBuffer, fres - HeaderBufferSize);
LocalData->P += (fres - HeaderBufferSize);
}
}
increment = fres + sizeof(struct PacketHeader);
if (Open->Size - LocalData->P < sizeof(struct PacketHeader)) //we check that the available, AND contiguous, space in the buffer will fit
{ //the NewHeader structure, at least, otherwise we skip the producer
increment += Open->Size-LocalData->P; //at the beginning of the buffer (p = 0), and decrement the free bytes appropriately
LocalData->P = 0;
}
InterlockedExchangeAdd(&LocalData->Free, (ULONG)(-(LONG)increment));
if(Open->Size - LocalData->Free >= Open->MinToCopy)
{
if(Open->mode & MODE_DUMP)
NdisSetEvent(&Open->DumpEvent);
else
{
if (Open->ReadEvent != NULL)
{
KeSetEvent(Open->ReadEvent,0,FALSE);
}
}
}
break;
}
Else //读取数据
{
IF_LOUD(DbgPrint("TransferData!!\n");)
//ndisTransferData required
LocalData->NewP = LocalData->P;
LocalData->NewP +=sizeof(struct PacketHeader);
if (LocalData->NewP == Open->Size)
LocalData->NewP = 0;
//first of all, surely the header must be copied
if (Open->Size-LocalData->NewP >= HeaderBufferSize)
{
//1 copy!
NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, HeaderBuffer, HeaderBufferSize);
LocalData->NewP += HeaderBufferSize;
if (LocalData->NewP == Open->Size)
LocalData->NewP = 0;
}
else
{
ToCopy = Open->Size - LocalData->NewP;
NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, HeaderBuffer, ToCopy);
NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)HeaderBuffer + ToCopy, HeaderBufferSize - ToCopy);
LocalData->NewP = HeaderBufferSize - ToCopy;
}
//then we copy the Lookahead buffer
if (Open->Size-LocalData->NewP >= LookaheadBufferSize)
{
//1 copy!
NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, LookaheadBuffer, LookaheadBufferSize);
LocalData->NewP += LookaheadBufferSize;
if (LocalData->NewP == Open->Size)
LocalData->NewP = 0;
}
else
{
ToCopy = Open->Size - LocalData->NewP;
NdisMoveMappedMemory(LocalData->Buffer + LocalData->NewP, LookaheadBuffer, ToCopy);
NdisMoveMappedMemory(LocalData->Buffer + 0, (PUCHAR)LookaheadBuffer + ToCopy, LookaheadBufferSize - ToCopy);
LocalData->NewP = LookaheadBufferSize - ToCopy;
}
//Now we must prepare the buffer(s) for the NdisTransferData
if ((Open->Size - LocalData->NewP) >= (fres - HeaderBufferSize - LookaheadBufferSize))
{
//only 1 buffer
pMdl1 = IoAllocateMdl(
LocalData->Buffer + LocalData->NewP,
fres - HeaderBufferSize - LookaheadBufferSize,
FALSE,
FALSE,
NULL);
if (pMdl1 == NULL)
{
IF_LOUD(DbgPrint("Error allocating Mdl1\n");)
LocalData->Dropped++;
break;
}
MmBuildMdlForNonPagedPool(pMdl1);
pMdl2=NULL;
LocalData->NewP += fres - HeaderBufferSize - LookaheadBufferSize;
}
else
{
//2 buffers
pMdl1 = IoAllocateMdl(
LocalData->Buffer + LocalData->NewP,
Open->Size - LocalData->NewP,
FALSE,
FALSE,
NULL);
if (pMdl1 == NULL)
{
IF_LOUD(DbgPrint("Error allocating Mdl1\n");)
LocalData->Dropped++;
break;
}
pMdl2 = IoAllocateMdl(
LocalData->Buffer + 0,
fres - HeaderBufferSize - LookaheadBufferSize - (Open->Size - LocalData->NewP),
FALSE,
FALSE,
NULL);
if (pMdl2 == NULL)
{
IF_LOUD(DbgPrint("Error allocating Mdl2\n");)
IoFreeMdl(pMdl1);
LocalData->Dropped++;
break;
}
LocalData->NewP = fres - HeaderBufferSize - LookaheadBufferSize - (Open->Size - LocalData->NewP);
MmBuildMdlForNonPagedPool(pMdl1);
MmBuildMdlForNonPagedPool(pMdl2);
}
NdisAllocatePacket(&Status, &pPacket, Open->PacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
IF_LOUD(DbgPrint("NPF: Tap - No free packets\n");)
IoFreeMdl(pMdl1);
if (pMdl2 != NULL)
IoFreeMdl(pMdl2);
LocalData->Dropped++;
break;
}
if (pMdl2 != NULL)
NdisChainBufferAtFront(pPacket,pMdl2);
NdisChainBufferAtFront(pPacket,pMdl1);
RESERVED(pPacket)->Cpu = Cpu;
LocalData->TransferMdl1 = pMdl1;
LocalData->TransferMdl2 = pMdl2;
Header = (struct PacketHeader*)(LocalData->Buffer + LocalData->P);
Header->header.bh_caplen = fres;
Header->header.bh_datalen = PacketSize + HeaderBufferSize;
Header->header.bh_hdrlen=sizeof(struct bpf_hdr);
//从网卡读取数据
NdisTransferData(
&Status,
Open->AdapterHandle,
MacReceiveContext,
LookaheadBufferSize,
fres - HeaderBufferSize - LookaheadBufferSize,
pPacket,
&BytesTransfered);
if (Status != NDIS_STATUS_PENDING)
{
IF_LOUD(DbgPrint("NdisTransferData, not pending!\n");)
LocalData->TransferMdl1 = NULL;
LocalData->TransferMdl2 = NULL;
IoFreeMdl(pMdl1);
if ( pMdl2 != NULL )
IoFreeMdl(pMdl2);
NdisReinitializePacket(pPacket);
// Put the packet on the free queue
NdisFreePacket(pPacket);
LocalData->P = LocalData->NewP;
LocalData->Accepted++;
GET_TIME(&Header->header.bh_tstamp,&G_Start_Time);
Header->SN = InterlockedIncrement(&Open->WriterSN) - 1;
increment = fres + sizeof(struct PacketHeader);
if (Open->Size - LocalData->P < sizeof(struct PacketHeader))
{
increment += Open->Size-LocalData->P;
LocalData->P = 0;
}
InterlockedExchangeAdd(&LocalData->Free, (ULONG)(-(LONG)increment));
if(Open->Size - LocalData->Free >= Open->MinToCopy)
{
if(Open->mode & MODE_DUMP)
NdisSetEvent(&Open->DumpEvent);
else
{
if (Open->ReadEvent != NULL)
{
KeSetEvent(Open->ReadEvent,0,FALSE);
}
}
}
break;
}
else
{
IF_LOUD(DbgPrint("NdisTransferData, pending!\n");)
ShouldReleaseBufferLock = FALSE;
}
}
}
while(FALSE);
if (ShouldReleaseBufferLock)
{
NdisDprReleaseSpinLock(&LocalData->BufferLock);
}
return NDIS_STATUS_NOT_ACCEPTED;
}
从winpcap源码中可以看到,内核通过NpF_tap从网卡获取数据包,同时进行过滤,当数据包的大小大于mintocopy,
KeSetEvent(Open->ReadEvent,0,FALSE); ,这样ReadEvent处于有信号状态,这样应用程序就可以通过ReadFile读取数据包了。