11.2.3 网络流量统计的实现
网络流量统计实现的主要函数调用关系如图11-7所示。
图 11-7 网络流量统计实现的主要函数调用关系
pcap_dispatch、pcap_loop、pcap_next_ex与PacketReceivePacket函数的实现参见第10章相关内容。在数据包捕获模式(MODE_CAPT)与网络流量统计模式(MODE_STAT)下,这些函数获得的数据类型并不相同,所以最终会由内核驱动程序NPF的NPF_Read函数根据设置的模式来提供不同的数据,具体的实现代码如下:
NTSTATUS NPF_Read(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
……
if(Open->mode&MODE_STAT)//统计模式的处理
{/该捕获实例处于网络统计模式/
//获得用户空间的缓冲区地址,并且不允许页交换
CurrBuff=(PUCHAR)MmGetSystemAddressForMdlSafe(
Irp->MdlAddress,NormalPagePriority);
if(CurrBuff==NULL)
{
NPF_StopUsingOpenInstance(Open);
EXIT_FAILURE(0);
}
/检查用户空间缓冲区的大小/
if(Open->mode&MODE_DUMP)
{//为内核转储的统计模式
if(IrpSp->Parameters.Read.Length<sizeof(struct bpf_hdr)+24)
{
NPF_StopUsingOpenInstance(Open);
Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
}
else
{//不是内核转储的模式
if(IrpSp->Parameters.Read.Length<sizeof(struct bpf_hdr)+16)
{
NPF_StopUsingOpenInstance(Open);
Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
}
/给用户空间缓冲区填充该数据包的bpf_hdr头/
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;
/复位计数器/
NdisAcquireSpinLock(&Open->CountersLock);
Open->Npackets.QuadPart=0;
Open->Nbytes.QuadPart=0;
NdisReleaseSpinLock(&Open->CountersLock);
NPF_StopUsingOpenInstance(Open);
/成功返回/
Irp->IoStatus.Status=STATUS_SUCCESS;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
……
}
在统计模式(MODE_STAT)下,函数把所接收的数据包数目(Open->Npackets.QuadPart)、数据包字节数(Open->Nbytes.QuadPart)统计信息及头信息(struct bpf_hdr)组成的数据复制到用户空间。这些统计信息在NPF_tap中获得,具体获得方式如下:
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)
{
CpuPrivateData*LocalData;
LocalData=&Open->CpuData[Cpu];
……
if(Open->mode&MODE_STAT)//处于统计模式
{
NdisAcquireSpinLock(&Open->CountersLock);
/更新统计信息/
Open->Npackets.QuadPart++;
if(PacketSize+HeaderBufferSize<60)
Open->Nbytes.QuadPart+=60;//最小长度为60
else
Open->Nbytes.QuadPart+=PacketSize+HeaderBufferSize;
/添加同步码+帧的起始标识+帧校验码(preamble+SFD+FCS)共12字节/
//这些值必须被考虑,因为其并不是来自NDIS的数据包的一部分,但网卡是接收了的
Open->Nbytes.QuadPart+=12;
NdisReleaseSpinLock(&Open->CountersLock);
}
……
}