深度剖析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);
       }
      
       // 设置 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);
       }
   
-------------------------------------------------------未完待续----------------------------------------

你可能感兴趣的:(职场,休闲,winpcap)