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);
}
-------------------------------------------------------未完待续----------------------------------------
本文出自 “千江月” 博客,请务必保留此出处http://eslxf.blog.51cto.com/918801/220998