Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6623087
  • 博文数量: 227
  • 博客积分: 10047
  • 博客等级: 上将
  • 技术积分: 6678
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-11 10:33
个人简介

网上的蜘蛛

文章分类

全部博文(227)

文章存档

2010年(19)

2009年(29)

2008年(179)

分类: C/C++

2008-04-10 15:56:00

4.2 接收处理
    接收的时候,由于那个TransferData的曲折过程,使得接收处理要相对复杂一点点,在ProtocolReceive和 ProtocolReceivePacket中的处理不同。但是由于2003 DDK中的PassThru中,没有对数据进行任何处理,所以,它的ProtocolReceive的处理相对来说,简单了一些。

NDIS_STATUS PtReceive(
    IN NDIS_HANDLE ProtocolBindingContext,
    IN NDIS_HANDLE MacReceiveContext,
    IN PVOID HeaderBuffer,
    IN UINT HeaderBufferSize,
    IN PVOID LookAheadBuffer,
    IN UINT LookAheadBufferSize,
    IN UINT PacketSize
    )
{
    // .......


    do
    {
        // 分配一个新的包描述符

        NdisDprAllocatePacket(&Status, &MyPacket, pAdapt->RecvPacketPoolHandle);
        if (Status == NDIS_STATUS_SUCCESS)
        {
            // PassThru维护了一个自己的接收队列,当收到下层的包时,PassThru并不是立刻

            // 调用NdisMIndicateReceive/NdisMXxxIndicateReceive请求NDIS通知上层新数据

            // 的到来,而是在自己的队列中缓冲起来,当自己的队列满了以后,PassThru将一

            // 次性调用NdisMIndicateReceive请求NDIS通知上层数据的到来。

            // 通常最底程的Miniport Driver也有同样的处理机制。

            PtQueueReceivedPacket(pAdapt, MyPacket, TRUE);
            // MyPacket的一个副本被Copy到PassThru中的队列去了,现在可以释放这一个Packet了。

            NdisDprFreePacket(MyPacket);
            break; // 注意这里,跳出了整个do-while了。

        }
       else
       {
            //

            // The miniport below us uses the old-style (not packet)

            // receive indication. Fall through.

            //

       }

        if (Packet != NULL)
        {
            // 当执行到这里,说明前面没有跳出do-while循环,也就是说,包申请失败

            // 进一步说明,PassThru的缓冲队列满了,于是有必要,调用 NdisMIndicateReceive

            // 请求NDIS通知上层数据包的到来了,这将导致上层注册的 ProtocolReceivePacket

            // 被调用.

            PtFlushReceiveQueue(pAdapt);
        }
        
        // ......

        // 把队列中的包通知了给上层接收,但是这一个包,由于分配新的描述符失败

        // 所以并没有通知给上层,因此,在这里,进行单独的处理。

        
        pAdapt->IndicateRcvComplete = TRUE;
        // 以下是进行各种协议相关的处理。

        switch (pAdapt->Medium)
        {
        case NdisMedium802_3:
        case NdisMediumWan:
            NdisMEthIndicateReceive(pAdapt->MiniportHandle,
                                MacReceiveContext,
                                HeaderBuffer,
                                HeaderBufferSize,
                                LookAheadBuffer,
                                LookAheadBufferSize,
                                PacketSize);
        break;

        case NdisMedium802_5:
            NdisMTrIndicateReceive(pAdapt->MiniportHandle,
                                MacReceiveContext,
                                HeaderBuffer,
                                HeaderBufferSize,
                                LookAheadBuffer,
                                LookAheadBufferSize,
                                PacketSize);
        break;
    case NdisMediumFddi:
        NdisMFddiIndicateReceive(pAdapt->MiniportHandle,
                                 MacReceiveContext,
                                 HeaderBuffer,
                                 HeaderBufferSize,
                                 LookAheadBuffer,
                                 LookAheadBufferSize,
                                 PacketSize);
         break;

    default:
         ASSERT(FALSE);
         break;
        }
    } while(FALSE);

    return Status;
}

注意,PassThru 并没有对收到的包进行任何处理,因此,在它的ProtocolReceive中,没有调用NdisTransferData,请求NDIS传送这个包其余的数据,它直接Indicate,把这个工作交给了上层去处理,如果,你的中间层要处理的话,就得在放入队列前面,调用 NdisTransferData,如果返回成功,则在紧接其下进行处理,如果返回 NDIS_STATUS_PENDING 的话,就把处理放到ProtocolTransferDataComplete中处理。所以,你的ProtocolReceive应该看起来是这样的过程:
1.  在把包加入PassThru的队列前,调用NdisTransferData.请求NDIS通知下层传递其余的数据。
    (这里回头走了一段曲折的路了)。
2A. 如果返回成功,则进行处理,如修改数据,重新修正CheckSum,
    在X86平台上可要注意字节顺序的问题了。
2B. 如果返回 NDIS_STATUS_PENDING 就在 ProtocolTransferDataComplete进行2A的处理。
3.  处理完成后,排入PassThru的队列。
    由于,现在网络设备硬件的发展,内存容量的提高,底程的Miniport Driver通常有一个类似PassThru的缓存处理机制,走这条曲折的线路上来,似乎很少见了。

    在搞懂了ProtocolReceive后,rotocolReceivePacket就更简单了。

INT
PtReceivePacket(
    IN NDIS_HANDLE ProtocolBindingContext,
    IN PNDIS_PACKET Packet
    )

{
    // .......

    // 进行你自己的处理,修改包的内容,修正CheckSum等操作,参考前面接收过程

    // 取得Packet中的内容

    Status = NDIS_GET_PACKET_STATUS(Packet);
    if (Status == NDIS_STATUS_RESOURCES)
    {
        // 如果下层设置了NDIS_STATUS_RESOURCES,说明下层由于资源

        // 紧缺等原因,要求上层经快处理,于是产生一个考贝的

        // 包描述符排入队列,马上Indicate,并调用NdisReturnPacket请求

        // NDIS归还下层的资源。这是PassThru的处理方法

        // 事实上,如果,你自己要修改这个包,你已经有一个新的Packet和Buffer

        // 以及相关内容了,只要把你的这个新的包排入队列,并调用 NdisReturnPacket

        // 归还下层的资源,而不立即Indicate上层,也是可以的。

        PtQueueReceivedPacket(pAdapt, Packet, TRUE);
    }
    else
    {
        PtQueueReceivedPacket(pAdapt, MyPacket, FALSE);
    }
    
    if (Status == NDIS_STATUS_RESOURCES)
    {
        NdisDprFreePacket(MyPacket);
    }
    // ......

}

当上层处理完后,调用NdisReturnPacket请求NDIS归还下层的资源时,NDIS调用下层注册的MiniportReturnPacket在这里释放资源后,再调用NdisReturnPackets通知更下层。

 

VOID MPReturnPacket(
    IN NDIS_HANDLE MiniportAdapterContext,
    IN PNDIS_PACKET Packet
    )

{
    // 如果你和我一样,在接收的时候,自己处理不成功的情况下,就把原来的数据往上传

    // 你就可以像我处理发送的方法一样,利用那几个Reserve的字段,在这里判断,

    // 并进行必要的资源释放。

    // ......

    // 通知下层释放资源

    NdisReturnPackets(&Packet, 1);
    // ......

}

注意:对于一个像TCP这样面向连接的发送的数据包来说,你要是改变了它的长度的话,那问题就更复杂了,你在协议栈的下方,上层协议栈不知道这个修改,而你把它发到目标计算机去,目标计算机得到的长度是修改后的,那双方的SEQ,ACK就不能同步了,这样的话,你必须记录下你的改动,并对以后的通信,做相应的修正。不然,你一改的话,接下来的通信就RST了。

五 Ring 0 和 Ring 3的通信问题
    也许你并不只是想修改过往的数据,你也想把这些数据直接传到Ring3上去,或有一些其它的理由需要在你的NDIS中间层驱动和应用程序通信, 2003 DDK中的PassThru也提供了这个功能,它已经创建好了设备对像和符号链接,你只需要修改驱动对象的DispatchTable就行了。假如,你希望Ring3的程序通能Read NDIS中间层截获的数据,你需要维护两个队列,一个用于缓存NDIS收到的数据,另一个存放Ring3的IRP请求队列等等,是的,这个已经没有什么好说的了,这一切对于你来说,也经不是难题了。但是,在兴奋之时,需要注意的是,现在是位于任何协议栈的下方,如果没有任何验证机制的话,别人只要在你的局域网中向你发送链路层的广播,或目标MAC地址是你的网卡的MAC的数据包时,你的程序都要受到影响,也就是说,别人随便发个包,你的系统都要为他操劳了,从安全的角度来说,你的系统应该尽可能减小受外部影响的程度,所以,你在加上这个功能的时候,有必要考虑这一点。

后记:
    也许,和很多人一样,我曾经试图看着DDK中的文档学习NDIS驱动开发,但是总是不顺利,到头来似乎感觉NDIS很难,DDK的文档没用,但是,在动手写起程序来时,发现了自己还是离不开DDK的文档,刚开始试图从看DDK文档中的描述,进而想掌握NDIS,这似乎是不容易的,至少对我来说是这样的,等你真正的写起程序来,遇上一些具体的问题,受到一些挫折后,再带着这些问题来看DDK文档就会有收获了。本文描述了自己学习过程中的一些理解,写出来希望能与大家交流,由于自己水平有限,错误之错在所难免,欢迎诸位指正。

QQ: 22517257. Email: Addylee2004@163.com, MSN: Addylee2004@163.com

参考资料:
    DDK开发文档。
    驱动开发网NDIS板块的讨论。
    http://www.xfocus.net/articles/200307/568.html

基于PassThru的NDIS中间层驱动程序扩展(前部分)

 

阅读(3410) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~