深度剖析WinPcap之(九)——數據包的發送過程(13)

1.8.3.2             NPF_BufferedWrite函數

函數把緩衝區(發送隊列)中的原始數據包發送到網絡。函數原型如下:

INT NPF_BufferedWrite(IN PIRP Irp,

                     IN PCHAR UserBuff,

                     IN ULONG UserBuffSize,

                     BOOLEAN sync);

參數UserBuff指向待發數據包的緩衝區,參數UserBuffSize爲緩衝區的大小。 

函數返回值爲實際所發送的字節數,如果該值小於Size參數規定的大小,那麼發送過程中出現了錯誤。錯誤可能由適配器的問題或衝突的/假的數據包緩衝區導致。

該函數作爲BIOCSENDPACKETSNOSYNCBIOCSENDPACKETSSYNCIOCTL被操作系統調用。緩衝區UserBuff作爲輸入參數,包含任意數量的數據包,每個數據包帶一個sf_pkthdr結構體。NPF_BufferedWrite掃描分析緩衝區並通過NdisSend函數發送每個數據包。如果SyncTRUE,數據包一同步方式發送,否則就以能發多快就發多快的方式發送。
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);

       }

      

       //設置FreeBufAfterWriteTRUE

//以便在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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章