深度剖析WinPcap之(九)——数据包的发送过程(13)

本文转自http://eslxf.blog.51cto.com/918801/220998

 

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);
       }
   
-------------------------------------------------------未完待续----------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
标 题: 【原创】基于WinPCap网络协议开发 - 炮王(超级打炮机)发送数据包(03) 作 者: 加百力 时 间: 2009-05-18,16:55:36 链 接: http://bbs.pediy.com/showthread.php?t=89175 【文章标题】: 基于WinPCap网络协议开发 - 炮王(超级打炮机)发送数据包(03) 【文章作者】: 加百力 【软件名称】: WinPCap 【下载地址】: http://www.winpcap.org/ 【内部编号】: MASSADA 0029 【编写语言】: C++ 【使用工具】: VS2005 【操作平台】: Windows 2003 -------------------------------------------------------------------------------- 【详细过程】 在第一篇文章中我们提到WinPCap的基本功能之一就是:  1> 在网络发送原始的数据包发送原始数据包难度不大,只需要应用几个简单的函数就可以了。 我编写炮王程序的主要目的是为了测试WinPCap/libpcap的抓包能力,在不同网速和数据包大小的条件下libpcap的抓包率有多少。 炮王特点: 01、可以发送任意协议的数据包。TCP/IP等等。 02、针对每次发包,会返回状态,可以检测是否成功。在程序中如果失败会自动退出。 03、可以随机修改数据包的:源、目的MAC地址,IP地址,端口、数据内容等属性信息。 04、使用多线程发包,在XP下最多可以同时开2000个线程发包,发包速度快,流量大。在千兆网卡上可以超过15M/s的速度。 05、自动统计发包时间,计算每秒发包数量,流量。 06、可以设置发包的时间间隔,最少到1毫秒。 炮王使用方法: 首先直接运行pw.exe可以看到程序提示的参数信息和显示的当前可用网卡信息。可用网卡信息会保存在CardsInfo.txt文件中。 打开BAT脚本,修改网卡信息,即可。 运行脚本,脚本将数据包文件发送出去。 注意:总的发包数量等于每个线程的发包数量乘以线程总数。 实验的数据包文件都是MSN消息。 具体代码请看附件。 -------------------------------------------------------------------------------- 【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值