一个简单的tcp filter的例子

一个简单的tcp filter的例子

来源:华中白云黄鹤BBS
作者:huyuguang


    这两天版面日渐萧条,我只好硬着头皮把我自己的一个尚在开发,非常不成熟
的东西拿出来了。
    前面我曾经就网络加密这个议题讨论过,那个时候我提出的方法是imd,
9x下就是利用hook_device_service,用这些方法的一个好处就是,接收数据
比较低层,直接得到的就是mac数据,而且可以知道是哪块网卡上来的,而且,
可以加密各种数据帧,ipx,ip什么的都不在话下。因为我所遇到的加密环境和要求,
几乎都是要求对tcp加密,在这种情况下,做在imd上的话,显然会降低ipx或者icmp
之类的非加密数据包的效率。因此以前我就考虑在tcp 上做一个filter,直接加密tcp
的数据。
    正好在mark的站点上新出来一个小工具,叫做tdimon,工具很好,可以看到各个进程
的通讯状态。可惜的是1.没有源代码,2.free版本不能得到数据内容。有这两点限制,这个
工具就只能当作玩具。正好那个时候我比较闲,就花了点时间做了一个类似的工具。
关于FILTER,art baker的书里说得很详细,我做的时候基本上也就是根据这本书上来的,
做tcp的filter,和做别的设备的filter,没什么区别,但是不同是由于不知道tcp是如何
和别的设备(tdi clinet)通讯的,所以必须仔细看tdi的规范。
    我先描述一下2000/nt下的tcp/ip协议的一些情况。2000/nt下,ip,tcp,udp是在
一个驱动程序里实现的,叫做tcp.sys,这个驱动程序创建了3个设备,就是ip,tcp,udp。
    首先描述一下driverentry。首先当然是iocreatedevice,用file_device_unknown,
因为tcp设备就是用的这个参数。代码如下:
    RtlInitUnicodeString( &usDeviceName, FILTER_NAME );
    status = IoCreateDevice( DriverObject,
                            sizeof(DEVICE_EXTENSION),
                            &usDeviceName,
                            FILE_DEVICE_UNKNOWN,
                            0,
                            FALSE,
                            &pDevObj );
然后就调用iogetdeviceobjectpointer来得到tcp设备的指针。
代码如下:
    RtlInitUnicodeString( &usTargetName, TARGET_NAME );
    status = IoGetDeviceObjectPointer( &usTargetName,
                                      FILE_ALL_ACCESS,
                                      &pTargetFileObj,
                                      &pTargetDevObj );
注意TARGET_NAME是大小写区分的,我是用#define TARGET_NAME L"//Device//Tcp",
不能写成#define TARGET_NAME L"//Device//tcp"。
然后我们就开始调用IoAttachDeviceToDevieStatck插入到tcp设备。
调用完成后,我们要让我们的设备表现的和tcp一样,于是把它的所有
特性都从tcpobj复制(pdevobj->xxx=ptcpobj->xxx)。
再扫描他tcpdriverobject的majorfunction,我们的driver必须都
支持。最后设置driverunload,因为为了方便调试,我们必须写一个
unload。

前面已经讲过driverentry了,经过driverentry,所有的原来应该发送到
tcp设备的irp现在都发送到我们的设备的处理函数了,如果什么事情都不做,
那么可以简单的调用iocalldriver把这个irp发到tcp设备去让它处理。
当然,我们是要做些事情的,于是代码如下:
    UCHAR MajorFunction,MinorFunction;
    PDEVICE_EXTENSION  pDevExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( Irp );
    MajorFunction = pIrpStack->MajorFunction;
    MinorFunction = pIrpStack->MinorFunction;
    file://DBGPRINT( ...)
    ParseIrp(Irp);
    IoCopyCurrentIrpStackLocationToNext( Irp );

    IoSetCompletionRoutine( Irp,
                            CompletionRoutine,
                            NULL,   // context
                            TRUE,   // InvokeOnSuccess
                            TRUE,   // InvokeOnError
                            TRUE ); // InvokeOnCancel
   
    return IoCallDriver( pDevExt->TargetDevObj, Irp);

代码很简单,除了parseirp之外,其他都是例行公事。对于tcp设备的前期处理
就在这个函数里。后期处理的函数可以放在completionroution里。

好了,我们已经得到了发向tcp设备的所有irp了,这个时候我们的任务就是要了解
tcp设备到底是如何工作的.


我们的任务是要得到本机发送出去的tcp数据和接受的tcp数据。当然前提是发送数据
通过tcp设备,如果某些应用程序用packet.sys之类的协议driver,直接发送tcp数据,
这种情况下数据不经过tcp设备,filter也就无从得到了。
为了了解如何从发向tcp设备的irp中得到信息,首先我先描述一下tdi client是如何
与tcp通讯以及tdi client一般是如何工作的。
driverstdio里面有好几个例子都是关于tdi client的,但是这些例子都是基于它自己
的类库的,不过这些例子功能都很强大,其中一个usb+web的温度计的创意简直让我目瞪口呆。
因为我也做过pci温度计的driver,但是我从来就没有想到还可以加上一个web server在里面。
因为driverstdio里的例子太复杂,我在这里简单描述一下,给一个小例子。
因为发送过程较为简单,先描述发送。
首先调用pIrp = TdiBuildInternalDeviceControlIrp (
                     TDI_SEND_DATAGRAM,                              // sub function
                     pDeviceObject,                                  // pointer to device object
                     pTransportObject,                               // pointer to udp object
                     NULL,                                           // pointer to event
                     NULL );                                         // pointer to return buffer
分配一块irp,然后调用
        TdiBuildSendDatagram (
                           pIrp,                                     // pointer to irp
                           pDeviceObject,                            // pointer to device object
                           pTransportObject,                         // pointer to file object
                           NULL,                                     // completion routine
                           NULL,                                     // completion context
                           pMdl,                                     // pointer to data
                           dBufferSize,                              // size of data
                           pConnectInfo );                           // connection information
不用我说也应该知道,数据是放在一个buf里面,调用这个函数之前,要先构件一个mdl,把这个buf
放进去。
然后...irp已经好了,只要IoCallDriver(pUDPObject,PIrp)就行了...

上面用的是UDP的例子(Datagram),但是tcp也是一样,虽然函数有点差别,但是也是大同小异(TdiBuildSend)。

为了搞清楚上面的两个函数到底做了些什么(到底构建的irp是什么样子),没有必要去跟踪,实际上,tdibuildxxx
都不过是一个宏,你可以在tdikrnl.h里找到。打开tdikrnl.h看看,发现每个宏里都有这么一句:
        _IRPSP->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;             
于是我们知道tdi client就是通过majorfunction=IRP_MJ_INTERNAL_DEVICE_CONTROL,minorfunction=send/recv/...
和tcp通讯的。这就使得我们确信,这种方法是可行的。(不是直接调用tdi函数,而是发irp)

接收的过程较为复杂(tdi client的选择较多,因此要考虑各种情况)
开始的时候,我在ddk document里看到这么一个宏:tdibuildreceive,我想ok,
和send一样,我当时想的很简单,以为tdi client要接受数据了,它就向tcp发一个
irp,然后返回pending,当有数据来的时候,tcp处理完这个irp,然后tdi client只要在这个irp的
irp_complete里处理得到的数据就行了。这的确是tdi client的一个选择,但是
当我试验的时候,我发现至少wsock不是这样做的,因为我打开了一个ie,浏览了一个网页,
但是我发现我发出的数据都很好的捕获到了,但是接收的数据一个也没有,而且事实上,
tcp似乎就没有受到minorfunction=tdi_receive的irp。我因为这件事苦恼了一段时间,
后来我仔细的读了读ddk document,发现tdi client还有第2种选择,首先向tcp发送一个
irp,minorfunction=tdi_sethandler,设置一些回调函数,然后当事情发生的时候,tcp
就会调用这些回调函数,这些函数名字是clientEventxxx。这个过程可以仔细看ddk document,
看TdiBuildSetEventHandler,就知道哪些过程可以用这个方法。receive也是其中的一个,于是
我们的方法得到了。首先我们得到了irp,如果是set_eventhandler,那么就修改tdi client
向tcp设置的回调函数的入口,把它指向我们自己写的一个函数,同时保留原来的入口,
然后在我们自己写的函数里里调用它。
代码如下:
       PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( Irp );
    MajorFunction = pIrpStack->MajorFunction;
    MinorFunction = pIrpStack->MinorFunction;
    FileObject = pIrpStack->FileObject ;
       ....
    switch(MajorFunction)
    {
        ...
        case IRP_MJ_INTERNAL_DEVICE_CONTROL  :
        {
            switch(MinorFunction)
            {
                ...
                case TDI_SET_EVENT_HANDLER:
                pRequestSetEvent = (PTDI_REQUEST_KERNEL_SET_EVENT)(&(pIrpStack->                        Parameters.DeviceIoControl)) ;
                EventType=pRequestSetEvent->EventType;
                EventHandler=pRequestSetEvent->EventHandler;
                   EventContext=pRequestSetEvent->EventContext;
                ...   
                switch(EventType)
                {
                    ...   
                    case TDI_EVENT_RECEIVE :
                        pRequestSetEvent->EventHandler = OurClientEventReceive;
                        g_EventReceive = EventHandler;
                        break;
                    ...
值得注意的是,就算是set_eventhandler,也不一定就是tdi_event_receive里接受数据,
这个tdi_client的选择很多,还有ClientEventChainedReceive 什么的,详情可看ddk,
winsock似乎都是没有用过这个。不过这个不影响,我们可以把ddk里面所有可能的情况
全部列出来,一一判断,然后分别处理。对于clienteventreceive,这里还有几句话要说
清楚,并不是每次调用这个函数就得到了数据,这还要根据ReceiveFlags参数,tdi client
要根据这个参数决定是否要发tdi_receive irp得到数据。于是我们的处理函数就要判断这个
参数,以便做出相应的处理,我自己的代码在实际的环境中运行了一段时间,似乎没有问题,但是
我也不敢说,对于各种情况我都考虑到了。

 

 


 

<think>我们正在讨论网络设备配置,特别是关于分组过滤规则(packet-filter)的配置。用户询问如何配置"packet-filter 3006 inbound"规则。根据引用,我们知道packet-filter可以在物理接口或VLAN接口下配置,用于过滤入站(inbound)或出站(outbound)方向的流量。 配置步骤通常包括: 1. 创建ACL(访问控制列表)规则,定义过滤条件(例如源IP、目的IP、协议类型等)。ACL的编号需要与packet-filter命令中引用的编号一致(这里是3006,属于高级ACL,范围3000-3999)。 2. 在接口视图下应用该ACL,指定方向(inbound或outbound)。 由于用户只给出了命令"packet-filter 3006 inbound",但没有给出ACL 3006的具体内容,我们需要假设ACL 3006已经配置好。如果还没有配置,需要先配置ACL 3006。 配置ACL 3006的示例步骤: a. 创建ACL 3006 b. 定义规则(rule),例如拒绝或允许特定流量。 然后,在接口上应用: - 进入接口视图(可能是物理接口如GigabitEthernet0/0,也可能是VLAN接口如Vlan-interface10) - 执行命令:packet-filter 3006 inbound 根据引用[2]的例子,在VLAN接口10下配置入方向包过滤: [H3C] interface Vlan-interface 10 [H3C-Vlan-interface10] packet-filter 3000 inbound # 这里用户要配置的是3006,所以应该是packet-filter 3006 inbound 注意:引用[4]提到packet-filter和traffic-filter的异同,但用户没有具体询问,我们暂时不展开。 因此,回答用户的问题: 1. 首先确保ACL 3006已经配置好,如果没有,需要先配置ACL 3006。 2. 然后在需要应用过滤的接口上(物理接口或VLAN接口)使用命令`packet-filter 3006 inbound`。 为了更具体,我们可以给出一个配置ACL 3006的示例,然后应用它。 示例:配置一个ACL 3006,拒绝来自特定源IP(比如192.168.1.100)的流量,并允许其他所有流量。 注意:ACL规则是按顺序匹配的,一旦匹配就执行动作,并且停止继续匹配。默认情况下,ACL末尾有一条拒绝所有的规则(根据ACL的类型,高级ACL默认是拒绝所有)。 配置ACL 3006: ``` [H3C] acl advanced 3006 # 进入高级ACL 3006的视图 [H3C-acl-ipv4-adv-3006] rule 0 deny ip source 192.168.1.100 0 # 拒绝源IP为192.168.1.100的IP流量(0表示精确匹配,即32位掩码) [H3C-acl-ipv4-adv-3006] rule 5 permit ip # 允许其他所有IP流量(规则号5,比0大,所以排在后面) [H3C-acl-ipv4-adv-3006] quit ``` 然后在接口上应用(假设应用在Vlan-interface 10的入方向): ``` [H3C] interface Vlan-interface 10 [H3C-Vlan-interface10] packet-filter 3006 inbound [H3C-Vlan-interface10] quit ``` 这样,当有流量进入Vlan-interface 10时,就会根据ACL 3006的规则进行过滤:来自192.168.1.100的IP流量会被拒绝,其他流量允许通过。 注意:实际配置时,请根据网络需求调整ACL规则。 根据用户问题,我们只给出了配置步骤,但用户可能还需要理解: - 如何验证配置? - 如何查看ACL的匹配统计? 因此,我们可以生成相关问题,并简要回答。 另外,引用[1]提到,当物理接口很多且存在VLAN间转发时,在VLAN接口下配置包过滤更高效。因此,我们建议在VLAN接口下配置。 总结回答: 配置"packet-filter 3006 inbound"规则需要两步:配置ACL 3006,然后在接口视图下应用该ACL到入方向。示例配置如上。 生成相关问题: 1. 如何查看ACL 3006的配置和匹配统计? 2. 如何删除已配置的packet-filter? 3. 除了VLAN接口,还能在哪些接口下配置packet-filter? 4. 如何配置更复杂的过滤条件(如基于目的IP、端口号等)?</think>### 配置 packet-filter 3006 inbound 规则指南 #### 步骤 1:创建 ACL 3006 规则 首先需定义 ACL 3006 的过滤规则(高级 ACL 范围 3000-3999): ```bash [H3C] acl advanced 3006 [H3C-acl-ipv4-adv-3006] rule 5 deny icmp # 示例:拒绝所有ICMP流量 [H3C-acl-ipv4-adv-3006] rule 10 permit tcp destination-port eq 80 # 示例:放行HTTP流量 [H3C-acl-ipv4-adv-3006] quit ``` **说明**: - 规则按序号从小到大匹配,`deny`表示拒绝,`permit`表示放行 - 支持匹配源/目的IP、协议类型、端口号等(如`source 192.168.1.0 0.0.0.255`)[^1] --- #### 步骤 2:在目标接口应用规则 在物理接口或 VLAN 接口下绑定 ACL: ```bash [H3C] interface GigabitEthernet 1/0/1 # 进入物理接口视图 [H3C-GigabitEthernet1/0/1] packet-filter 3006 inbound # 应用入方向过滤 # 或在VLAN接口应用(推荐用于跨VLAN过滤) [H3C] interface Vlan-interface 10 [H3C-Vlan-interface10] packet-filter 3006 inbound ``` --- #### 关键配置原则 1. **接口选择**: - 物理接口:过滤直接进入该端口的流量 - VLAN接口:过滤跨VLAN转发的流量(适合多VLAN环境)[^1] 2. **方向控制**: - `inbound`:过滤进入接口的流量 - `outbound`:过滤从接口发出的流量 3. **优先级**: - 物理接口和VLAN接口的过滤规则独立生效 - VLAN接口规则对三层转发流量生效[^2] --- #### 验证与维护 ```bash # 查看ACL配置 [H3C] display acl 3006 # 检查接口过滤状态 [H3C] display packet-filter interface Vlan-interface 10 # 清除统计计数 [H3C] reset acl counter 3006 ``` > ⚠️ **注意事项** > - 未匹配规则的流量默认**拒绝通过**(ACL隐含`deny any`) > - 修改ACL后需重新绑定到接口才会生效 > - 物理接口过滤在二层生效,VLAN接口过滤在三层生效[^4] --- ### 相关问题 1. **如何为不同 VLAN 配置差异化的过滤规则?** > 需在每个 VLAN 接口下单独绑定 ACL,例如: > ```bash > interface Vlan-interface 10 > packet-filter 3010 inbound > interface Vlan-interface 20 > packet-filter 3020 inbound > ``` 2. **`packet-filter` 与 `traffic-filter` 有何区别?** > `packet-filter` 基于ACL实现基础过滤,`traffic-filter` 支持QoS策略(如限速、重标记),两者可组合使用[^4]。 3. **如何放行特定 IP 的 SSH 流量?** > 在 ACL 中添加规则: > ```bash > rule permit tcp source 192.168.1.5 0 destination-port eq 22 > ``` 4. **配置后流量异常如何排查?** > 检查顺序: > ① ACL规则顺序(小序号优先) > ② 接口方向(`inbound/outbound`) > ③ 计数器`display packet-filter statistics`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值