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

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

 

 

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的信息;

  • 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、付费专栏及课程。

余额充值