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