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

1.8    驅動程序中對應的函數接口

NPF中,提供了NPF_WriteNPF_BufferedWriteNPF_IoControl函數,實現把數據包傳遞給NDIS層,最終調用NdisSend函數把數據包發送出去。

1.8.1    發送單個數據包的接口實現

1.8.1.1             NPF_Write函數

packet.dllPacketSendPacket函數執行WriteFile系統調用時,該函數被調用(響應IRP_MJ_WRITE)。

DriverObject->MajorFunction[IRP_MJ_WRITE]  = NPF_Write;

NPF_Write函數執行數據包的發送,根據Open->Nwrites的值執行發送次數,該數值在NPF_Open()函數中設置爲默認值爲1,通過NPF_IoControl函數可修改該值。

函數NPF_Write原型如下:

NTSTATUS NPF_Write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

參數DeviceObject指向用戶所使用的設備驅動對象,參數Irp指向包含用戶請求的IRP

函數返回操作的狀態。

具體代碼實現如下:

NTSTATUS NPF_Write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

{

    POPEN_INSTANCE       Open;

    PIO_STACK_LOCATION   IrpSp;

    PNDIS_PACKET          pPacket;

    NDIS_STATUS           Status;

    ULONG                 NumSends;

    ULONG                 numSentPackets;

 

/*獲得調用者在給定IRP中的堆棧位置*/

    IrpSp = IoGetCurrentIrpStackLocation(Irp);

/*獲得POPEN_INSTANCE的實例open*/

Open=IrpSp->FileObject->FsContext;

 

    /*獲得重複發送的次數*/

    NumSends = Open->Nwrites;

    /*驗證重複發送次數的有效性,必須大於0,否則函數返回*/

    if (NumSends == 0)

    {

       Irp->IoStatus.Information = 0;

       Irp->IoStatus.Status = STATUS_SUCCESS;

       IoCompleteRequest(Irp, IO_NO_INCREMENT);

      

       return STATUS_SUCCESS;

    }

 

    /*驗證輸入參數的有效性:*/

    //1. 數據包的大小應該大於0

    //2. 小於等於數據鏈路層最大幀的大小,並且

    //3. 最大幀大小不應該爲0

 

//檢查用戶提供的緩衝區不爲空

    if(IrpSp ->Parameters.Write.Length == 0 ||   

// 檢查MaxFrameSize被正確初始化

       Open->MaxFrameSize == 0 ||

       Irp->MdlAddress == NULL ||

//檢查幀大小小於等於MTU

       IrpSp->Parameters.Write.Length > Open->MaxFrameSize)

    {   //輸入參數的有效性檢查失敗,否則函數返回

       Irp->IoStatus.Information = 0;

       Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;

       IoCompleteRequest(Irp, IO_NO_INCREMENT);

      

       return STATUS_UNSUCCESSFUL;

    }

   

    /*如果可能,增加綁定句柄的引用計數*/

    if(NPF_StartUsingBinding(Open) == FALSE)

    {

//適配器沒有被綁定,不能發送數據包,函數返回

       Irp->IoStatus.Information = 0;

       Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;

       IoCompleteRequest(Irp, IO_NO_INCREMENT);

             

       return STATUS_INVALID_DEVICE_REQUEST;

    }

 

/*獲取保護WriteInProgress變量的自旋鎖*/

    NdisAcquireSpinLock(&Open->WriteLock);

    if(Open->WriteInProgress)

    {

// 另一個寫操作當前正在處理中,

//釋放保護WriteInProgress變量的自旋鎖,函數返回

NdisReleaseSpinLock(&Open->WriteLock);

 

       NPF_StopUsingBinding(Open);

 

       Irp->IoStatus.Information = 0;

       Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;

       IoCompleteRequest(Irp, IO_NO_INCREMENT);

 

       return STATUS_UNSUCCESSFUL;

    }

    else

    {  //無正在處理中的寫操作,可開始寫操作,並設置爲正在執行寫操作的狀態

       Open->WriteInProgress = TRUE;

//復位NdisWriteCompleteEvent事件

       NdisResetEvent(&Open->NdisWriteCompleteEvent);

    }

/* 釋放保護WriteInProgress變量的自旋鎖*/

    NdisReleaseSpinLock(&Open->WriteLock);

   

    /*復位掛起在等待SendComplete的數據包個數爲0*/

    Open->TransmitPendingPackets = 0;

/*復位同步多個寫進程的事件WriteEvent*/

    NdisResetEvent(&Open->WriteEvent);

          

    numSentPackets = 0;     //已發數據包次數初始化爲0

   

/*進入重複發送的while循環中*/

    while( numSentPackets < NumSends )

    {  

//分配並初始化一個數據包描述符,

//pPacket返回所分配的數據包描述符

       NdisAllocatePacket( 

           &Status,

           &pPacket,

           Open->PacketPool

           );

 

       if (Status == NDIS_STATUS_SUCCESS)

       {

           //緩衝池中有空閒數據包可用,準備用NdisSend發送該數據包

          

           //如果有要求,爲該數據包設置SkipSentPackets標誌

           //目前,我們只在禁止接受迴環數據包時設置該標誌,

//比如,拒收由我們自己發送的數據包

           if(Open->SkipSentPackets)

           {

              NdisSetPacketFlags(

                  pPacket,

                  g_SendPacketFlags);

           }

 

           //數據包沒有一個緩衝區,不需要每次單個寫操作後執行內存釋放

           RESERVED(pPacket)->FreeBufAfterWrite = FALSE;

           //把寫緩衝區附加給該數據包

           NdisChainBufferAtFront(pPacket,Irp->MdlAddress);

            //遞增掛起待發數據包的數目

            InterlockedIncrement(

&Open->TransmitPendingPackets);

//復位NdisWriteCompleteEvent事件

           NdisResetEvent(&Open->NdisWriteCompleteEvent);

           //向低層的MAC層請求數據包發送

           NdisSend(&Status,Open->AdapterHandle,pPacket);

 

           if (Status != NDIS_STATUS_PENDING)

           {

              //  數據包的發送沒有被掛起,立即調用完成句柄函數

              NPF_SendComplete(Open,pPacket,Status);

           }

           numSentPackets ++; //已發數據包增加1

       }

       else

       {

//傳輸池中沒有空閒數據包可用,需要等待一段時間。

//當至少一半TX數據包緩衝池是可用的時候,

//Open->WriteEvent事件獲得通知,

//NPF_SendComplete完成句柄函數可發送該通知。

           NdisWaitEvent(&Open->WriteEvent,1); 

       }

    }

 

    //當程序運行到此位置時,所有的數據包已在NdisSend中排隊等待發送了,

//我們僅僅需要通過SendComplete

//完成句柄函數等待所有的數據包發送結束

//(如果任何一個NdisSend請求返回STATUS_PENDING)。

    NdisWaitEvent(&Open->NdisWriteCompleteEvent, 0);

   

    //所有的數據包被髮送,釋放適配器的綁定

    NPF_StopUsingBinding(Open);

 

    //沒有寫操作正在處理

    NdisAcquireSpinLock(&Open->WriteLock);

    Open->WriteInProgress = FALSE;

    NdisReleaseSpinLock(&Open->WriteLock);

 

    //完成該Irp並返回成功

    Irp->IoStatus.Status = STATUS_SUCCESS;

    Irp->IoStatus.Information =

           IrpSp->Parameters.Write.Length;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

             

    return STATUS_SUCCESS;

}

NPF_Write函數的主要正常流程如下爲:

首先獲取各個必要參數,檢查輸入參數的有效性;

開始使用適配器的綁定;

檢測是否可執行寫操作;

執行一次或多次數據包發送;

等待發送結束;

停止使用綁定;

設置爲可執行寫操作狀態( Open->WriteInProgress = FALSE;)

函數結束,返回Irp的信息;

本文出自 “千江月” 博客,請務必保留此出處http://eslxf.blog.51cto.com/918801/217015

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