1.8.3.2 NPF_BufferedWrite函数
函数把缓冲区
(
发送队列
)
中的原始数据包发送到网络。函数原型如下:
INT NPF_BufferedWrite(IN PIRP Irp,
IN PCHAR UserBuff,
IN ULONG UserBuffSize,
BOOLEAN sync);
参数UserBuff指向待发数据包的缓冲区,参数UserBuffSize为缓冲区的大小。
函数返回值为实际所发送的字节数,如果该值小于Size参数规定的大小,那么发送过程中出现了错误。错误可能由适配器的问题或冲突的/假的数据包缓冲区导致。
该函数作为BIOCSENDPACKETSNOSYNC或BIOCSENDPACKETSSYNC的IOCTL被操作系统调用。缓冲区
UserBuff
作为输入参数,包含任意数量的数据包,每个数据包带一个
sf_pkthdr
结构体。NPF_BufferedWrite扫描分析缓冲区并通过NdisSend函数发送每个数据包。如果Sync为TRUE,数据包一同步方式发送,否则就以能发多快就发多快的方式发送。
IN PIRP Irp,
IN PCHAR UserBuff,
IN ULONG UserBuffSize,
BOOLEAN Sync)
{
POPEN_INSTANCE Open;
PIO_STACK_LOCATION IrpSp;
PNDIS_PACKET pPacket;
UINT i;
NDIS_STATUS Status;
LARGE_INTEGER StartTicks, CurTicks, TargetTicks;
LARGE_INTEGER TimeFreq;
struct timeval BufStartTime;
struct sf_pkthdr *winpcap_hdr;
PMDL TmpMdl;
PCHAR CurPos;
PCHAR EndOfUserBuff = UserBuff + UserBuffSize;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Open=IrpSp->FileObject->FsContext;
if( NPF_StartUsingBinding(Open) == FALSE)
{
//
网络适配器被移出了
return 0;
}
//
对
UserBuff
的合法性进行检查
if(UserBuff == NULL)
{
//
释放对
NdisAdapter
绑定的拥有
NPF_StopUsingBinding(Open);
return 0;
}
//
检查
MaxFrameSize
被正确的初始化
if(Open->MaxFrameSize == 0)
{
NPF_StopUsingBinding(Open);
return 0;
}
//
复位
WriteEvent
事件,用于数据包分配的同步
NdisResetEvent(&Open->WriteEvent);
//
复位挂起的数据包个数
Open->Multiple_Write_Counter = 0;
//
从第一个数据包开始
winpcap_hdr = (struct sf_pkthdr*)UserBuff;
//
获得时间参考
StartTicks = KeQueryPerformanceCounter(&TimeFreq);
BufStartTime.tv_sec = winpcap_hdr->ts.tv_sec;
BufStartTime.tv_usec = winpcap_hdr->ts.tv_usec;
//
检查
UserBuff
的一致性
if( (PCHAR)winpcap_hdr + winpcap_hdr->caplen + sizeof(struct sf_pkthdr) > EndOfUserBuff )
{
NPF_StopUsingBinding(Open);
return -1;
}
//
保存当前的时间戳计数
CurTicks = KeQueryPerformanceCounter(NULL);
/*
主循环,发送缓冲区的数据到网络
*/
while(TRUE)
{
if(winpcap_hdr->caplen ==0 ||
winpcap_hdr->caplen > Open->MaxFrameSize)
{
//
错误的头信息
NPF_StopUsingBinding(Open);
return -1;
}
//
分配一个
MDL
来映射数据包数据
TmpMdl = IoAllocateMdl(
(PCHAR)winpcap_hdr + sizeof(struct sf_pkthdr),
winpcap_hdr->caplen,
FALSE,
FALSE,
NULL);
if (TmpMdl == NULL)
{
NPF_StopUsingBinding(Open);
return -1;
}
MmBuildMdlForNonPagedPool(TmpMdl);
//
分配与初始化一个数据包描述符
NdisAllocatePacket(
&Status, &pPacket, Open->PacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
//
没有足够的空闲空间,等待一段
1000
毫秒,试图再分配
NdisResetEvent(&Open->WriteEvent);
NdisWaitEvent(&Open->WriteEvent, 1000);
NdisAllocatePacket(
&Status, &pPacket, Open->PacketPool);
if (Status != NDIS_STATUS_SUCCESS)
{
IoFreeMdl(TmpMdl);//
释放映射
NPF_StopUsingBinding(Open);
return -1;
}
}
//
如果有要求,为该数据包设置
SkipSentPackets
标志
//
目前,我们只在禁止接受回环数据包时设置该标志,
//
比如,拒收由我们自己发送的数据包
if(Open->SkipSentPackets)
{
NdisSetPacketFlags(
pPacket,g_SendPacketFlags);
}
//
设置
FreeBufAfterWrite
为
TRUE
,
//
以便在
NPF_SendComplete
函数中区别处理方式
RESERVED(pPacket)->FreeBufAfterWrite = TRUE;
TmpMdl->Next = NULL;
//
给
pPacket
附加
MDL
NdisChainBufferAtFront(pPacket, TmpMdl);
//
递增挂起的待发数据包数
InterlockedIncrement(&Open->Multiple_Write_Counter);
//
执行数据的
MAC
层发送
NdisSend( &Status, Open->AdapterHandle, pPacket);
if (Status != NDIS_STATUS_PENDING)
{
//
发送没有被挂起,直接调用完成函数
NPF_SendComplete(
Open,
pPacket,
Status
);
}
//
获得缓冲区中下一个数据包
(PCHAR)winpcap_hdr +=
winpcap_hdr->caplen + sizeof(struct sf_pkthdr);
//
检查是否达到缓冲区的尾部
if( (PCHAR)winpcap_hdr >= EndOfUserBuff )
{
//
等待挂起的发送完成
NPF_WaitEndOfBufferedWrite(Open);
NPF_StopUsingBinding(Open);
return (INT)((PCHAR)winpcap_hdr - UserBuff);
}
-------------------------------------------------------未完待续----------------------------------------