11.2.3 网络流量统计的实现

网络流量统计实现的主要函数调用关系如图11-7所示。

11.2.3 网络流量统计的实现 - 图1

图 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);

}

……

}