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,數據包一同步方式發送,否則就以能發多快就發多快的方式發送。
INT NPF_BufferedWrite(
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