1 概述

netfliter框架不仅仅在ipv4中有应用,bridge,ipv4,ipv6,decnet 这四种协议中都有应用,其中ipv4中又分开了arp和ip的两种

其实netfliter是个大的框架,在ipv4中对应的应用层工具是iptables,在bridge中对应的应用层工具是ebtables,在arp中对应的应用层工具是arptables

iptables 中有raw,filter,nat,mangle,security,5个table,

ebtables 中有broute,filter,nat,3个table,

arptables 中有filter,1个table

具体的可以查看源码目录linux/net/目录下的ipv4,ipv6,decnet,bridge目录下的netfilter

2 一些概念

2.1 三层hook函数的优先级

enum nf_ip_hook_priorities {

NF_IP_PRI_FIRST = INT_MIN,

NF_IP_PRI_CONNTRACK_DEFRAG = -400,

NF_IP_PRI_RAW = -300,

NF_IP_PRI_SELINUX_FIRST = -225,

NF_IP_PRI_CONNTRACK = -200,

NF_IP_PRI_MANGLE = -150,

NF_IP_PRI_NAT_DST = -100,

NF_IP_PRI_FILTER = 0,

NF_IP_PRI_SECURITY = 50,

NF_IP_PRI_NAT_SRC = 100,

NF_IP_PRI_SELINUX_LAST = 225,

NF_IP_PRI_CONNTRACK_HELPER = 300,

NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,

NF_IP_PRI_LAST = INT_MAX,

};

2.2 二层hook 函数的优先级

enum nf_br_hook_priorities {

NF_BR_PRI_FIRST = INT_MIN,

NF_BR_PRI_NAT_DST_BRIDGED = -300,

NF_BR_PRI_FILTER_BRIDGED = -200,

NF_BR_PRI_BRNF = 0,

NF_BR_PRI_NAT_DST_OTHER = 100,

NF_BR_PRI_FILTER_OTHER = 200,

NF_BR_PRI_NAT_SRC = 300,

NF_BR_PRI_LAST = INT_MAX,

}

2.3 hook点,hooknum,hook函数

三层(ip)有5个hooknum,分别是pre_routing,local_in,forward,local_out,post_routing

二层(bridge)有6个hooknum,分别是

pre_routing,local_in,forward,local_out,post_routing,brouting,

在头文件./uapi/linux/netfilter_bridge.h ./uapi/linux/netfilter_ipv4.h 可看到

linux/net/netfilter 是整个netfilter框架的代码,不同的协议下面的netfilter是调用的代码

hook函数,就是我们自定义的那些函数,函数优先级,数值越大的,优先级越小

一个hook点是由协议和hooknum两者决定的,nf_hooks[pf][hooknum],因此,协议不一样,hooknum一样也是不一样的hook点的,ipv4的协议是NFPROTO_INET,bridge的协议是NFPROTO_BRIDGE,而只有同一个hook点的函数才会有优先级的问题。因此,在正常情况下,同一个数据包在某一层中只会遍历某一种协议的hook点,是一个水平分层的问题,虽然都注册在netfilter框架下,可是协议决定了这是一个水平的流程。当数据包上到另外一层那就是另外一层的水平。

但是有一些地方在三层的改变会影响二层的结构的,比如像ip-DNAT的,改变了三层的daddr,那么对应的二层的dmac地址也是会跟着改变的,那么这个应该在routing之前还是应该在brigding之前做呢?按道理虽然改的是三层的内容,但是这个应该在brigding之前做的,这样在二层选择出口的时候,才不会错。所以其实二层中有些地方是有穿插三层的hook点的调用的,所以整个结构看起来才会不那么清晰(后面的函数分析会证实这个想法)

hooknum 和pf 决定了hook点,hook点上面有hook函数,根据优先级来进行hook函数的调用。

NF_HOOK 这个宏就是遍历给定的hook点(nf_hooks[pf][hooknum])上面的所有hook函数

在整个网络协议栈(包括二层的)上面的不同位置的NF_HOOK的作用就是遍历不同的hook点上hook函数,这就是netfilter做的事情

3 数据包在网桥的流转

3.1 接收入口函数

netif_rx

netif_receive_skb(skb)-->netif_receive_skb_internal()->__netif_receive_skb()-> __netif_receive_skb_core()

netif_rx 是上层处理函数中最接近驱动层的函数,往queue里面放skb

netif_receive_skb 是最接近上层处理函数的入口函数,在软中断中执行,在queue中取完skb后的处理函数

netif_rx 和netif_receive_skb的关系还没有搞的很明白,两者没有明显的调用关系,在驱动中两者都有调用,

__netif_receive_skb_core 是真正处理skb的函数,到底接着数据包是怎么走的,在这里判断的

对于网桥的数据包,就是rx_handler = br_handle_frame,在调用这个函数之前已经调用了skb_vlan_untag把二层头包含vlan信息的部分去掉,

并且把vlan信息记录在skb->vlan_proto(协议),和skb->vlan_tci(优先级和id)

即bridge的入口函数是br_handle_frame,在br_input.c

br_handle_frame 主要有两个分支有NF_HOOK的调用的,如下:

|---link-local----      NF_HOOK(NFPROTO_BRIDGE,NF_BR_LOCAL_IN,..,br_handle_local_finish) 
|---forward--          NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ...,br_handle_frame_finish)

link-local :dmac是本地链路地址。至于什么是本地链路地址,可以google,只知道在ipv6中(fe80)用得比较多,其他没什么了解

br_handle_frame_finish 这个函数对数据包的dmac进行判断,然后走不同的处理函数.

dmac 的不同的,处理方式不同:

A.bridge it,如果dmac是在网桥的别的端口,复制一份帧到dmac所在的端口                    ---->br_forward

B.flood it over all the forwarding bridge ports,如果dmac地址是网桥不知道的,就泛洪     ---->br_flood_forward

C.pass it to the higher protocol code,如果dmac是网桥的,或者网桥其中一个端口的        ---->br_pass_frame_up

D.ignore it,dmac在进来的端口的这一边的,即dmac能在进来端口的mac地址表中找到             ---->br_forward

3.2 转发

br_forward,通过should_deliver()来进行判断,是否真的需要__br_forward 还是 ignore it,

__br_forward->NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, ... skb->dev,br_forward_finish) ,

__br_forward 函数改变了skb->dev

br_forward_finish->NF_HOOK(NFPROTO_BRIDGE,NF_BR_POST_ROUTING,skb,NULL,skb->dev,br_dev_queue_push_xmit);

br_dev_queue_push_xmit->dev_queue_xmit

br_flood_forward->br_flood(br, skb, skb2, __br_forward, unicast)->__br_forward

same as __br_forward

3.3 local_in

br_pass_frame_up->NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev,NULL,netif_receive_skb)

3.4 发送入口函数

对于二层以上的层,只有网桥这个接口,没有其绑定的ethx了(通过路由表可知),网桥的发送函数是br_dev_xmit

在br_dev_xmit 也会根据dmac判断是进行br_multicast_deliver,br_deliver,

还是br_flood_delver,但是最后调用都是__br_deliver

__br_deliver-> NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL,skb->dev,br_forward_finish)

br_forward_finish->NF_HOOK(NFPROTO_BRIDGE,NF_BR_POST_ROUTING,skb,NULL,skb->dev,br_dev_queue_push_xmit);

br_dev_queue_push_xmit->dev_queue_xmit

3.5 结论

根据上面的分析,通过网桥进来的数据包会经过的hook点跟在三层的是一样的

本地的会经过pre_routing 和local_in, 转发的会经过pre_routing,forward,post_routing ,
而本地出去的会经过local_out,post_routing

4 二层调用三层的hook函数的实现

4.1 NF_HOOK 和NF_HOOK_THRESH的区别

NF_HOOK 封装了NF_HOOK_THRESH ,是特殊的NF_HOOK_THRESH, 是从优先级最高的hook函数开始的

NF_HOOK_THRESH,

static inline int NF_HOOK{

return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN)

4.2 br_netfilter.c分析

二层hook点中调用三层的hook的实现主要在linux/net/bridge/br_netfilter.c ,这个函数注册了7个hook函数,其中5个是NFPROTO_BRIDGE协议的,2个分别是NFPROTO_IPV4,NFPROTO_IPV6的

NFPROTO_BRIDGE的5个函数分别是br_nf_pre_routing,br_nf_local_in,br_nf_forward_ip,

br_nf_forward_arp,br_nf_post_routing的,br_nf_forward_ip 优先级是 -1,其他优先级都是0,

NFPROTO_IPV4/6 的两个都是在pre_routing hook点,优先级是first,hook函数都是ip_sabotage_in,这个函数的作用就是防止多次调用三层pre_routing hook点的hook函数

因此目前看到的在NFPROTO_BRIDGE协议下系统注册了的钩子函数的顺序如下:

pre_routing  ebt_nat_in(dnat)->br_nf_pre_routing

local_in     ebt_in_hook(filter)->br_nf_local_in

forward      ebt_in_hook(filter)->br_nf_forward_ip->br_nf_forward_arp

local_out     ebt_nat_out(dnat_other)->ebt_out_hook(filter_other)

post_routing  ebt_nat_out(snat)->br_nf_post_routing(last)

(1). br_nf_pre_routing->NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev,NULL,br_nf_pre_routing_finish)

br_nf_pre_routing_finish->NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb,skb->dev, NULL,br_handle_frame_finish, 1);

到br_handle_frame_finish 就走完了pre_routing的钩子了,其实NF_HOOK_THRESH 就是为了走完pre_routing 优先级大于1的钩子函数

正常的数据包走br_hadnle_frame 进来调用了一次NF_HOOK ,执行NFPROTO_BRIDGE的pre_routing的hook点中的hook函数,当执行到      br_nf_pre_routing这个钩子函数的时候,会先去调用一次三层的pre_routing的所有hook函数,然后再回到br_nf_pre_routing_finish

因为在br_nf_pre_routing 中返回值是NF_STOLEN,所以在br_handle_frame调用的

NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ...,br_handle_frame_finish),

到br_nf_pre_routing 就结束了,所以会有在br_nf_pre_routing_finish->NF_HOOK_THRESH()的过程,是为了重新接上pre_routing 后面的hook函数

有了这个函数br_nf_pre_routing,就可以对只经过二层的数据包做三层的dnat,

(2).br_nf_local_in->nothing ,

(3).br_nf_forward_ip->NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, in),parent,br_nf_forward_finish),pf=INET/INET6

bf_nf_forward_finish->NF_HOOK_THRESH(NFPROTO_BRIDGE,NF_BR_FORWARD,skb, in,skb->dev, br_forward_finish, 1);

这里主要是经过了3层的forward hook点,就是经过二层走的数据包可以在三层的forward链做过滤,主要是结合physdev模块做indev和outdev的过滤。继续NF_HOOK_THRESH的时候,会走到优先级是1的hook函数那里,跳过了br_nf_forward_arp,因为一个skb->protocol,只能是一种,不可能既是ip,也是arp,既然在br_nf_forward_ip中能走到br_nf_forward_finish就证明这是个ip包了,如果不是ip包,在一开始就会返回NF_ACCEPT,让其继续走原来的遍历顺序

br_nf_forward_arp->NF_HOOK(NFPROTO_ARP,NF_ARP_FORWARD,skb, (struct net_device*)in,(struct net_device *)out,br_nf_forward_finish);

这个就在ARP的forward链上做过滤

注意两个NF_HOOK中传进去的indev和outdev 的区别,不一样的

(4).br_nf_post_routing->NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL,realoutdev,br_nf_dev_queue_xmit)

注意br_nf_post_routing 的优先级是last,

在post_routing中也先判断,数据包是否是经过bridge的了,如果是从

ip/local_out->bridge/local_out,或者直接bridge/local_out的数据包都没有必要再经过一次ip/post_routing,即只有经过bridge转发的包,
才需要经过ip/post_routing

4.3 防止多次调用三层hook点的hook函数

ip_sabotage_in 在NFPROTO_IPV4/6的pre_routing 的first,如果是从网桥上来到三层的数据包,其实三层的pre_routing已经做过了,这个函数

就是控制如果是从网桥上来的数据包就返回NF_STOP ,停止这个hook点的后续hook函数的检查,并且接受数据包(防止两次走过三层的pre_routing),如果不是从网桥上来的包,就返回NF_ACCEP ,继续做这个hook点的hook函数的检查的

根据4.2 可知,在二层只有pre_routing,forward,post_routing 三个hook点会调用到三层对应hook点的hook函数,而只有经过
bridge/pre_routing->bridge/local_in->ip/pre_routing这样路径进来的数据包才需要在ip/pre_routing的位置判断是否是网桥上来的包,如果是网桥上来的就不再需要遍历这个hook点剩下的hook函数了.其他的路径,都不可能同时经过二层和三层的同一个hook点,

所以只需要在ip/pre_routing的first的位置注册ip_sabotage_in,就可以了,ip/forward,ip/post_routing 都不需要

5 brouting hook点

brouting的调用不是通过NF_HOOK 这种传统的方式来进行的,而且系统没有通过nf_register_hooks 这种方式注册对应的hook函数,

只是把一个函数赋值给了一个在br_input.c 中定义的br_should_route_hook_t

*br_should_route_hook 这个变量

然后通过这个变量来进行函数的调用,真正的函数是net/bridge/netfilter/ebtable_broute.c 中的ebt_broute

ebtable 有三个表,分别是

broute:系统没有注册有hook函数,允许注册的hook点只有一个就是brouting

nat:pre_routing(dnat),post_routing(snat),local_out(dnat_other)

filter:local_in,forward,local_out(other)

STP 最小生成树协议的5中状态

#define BR_STATE_DISABLED 0

#define BR_STATE_LISTENING 1

#define BR_STATE_LEARNING 2

#define BR_STATE_FORWARDING 3

#define BR_STATE_BLOCKING 4

DISABLE:   什么功能都没有,只有一个逻辑设备。

LISTENING: 可以接收和发送网络传输的BPDU,包括Configureation BPDU和TCN BPDU,但不能进行数据帧的转发、不能学习。

LEARNING:  可以接收和发送BPDU,可以学习,但是不能进行数据帧的转发。

FORWARDING:可以接收和发送BPDU、可以学习、可以进行数据帧的转发。

BLOCKING:  只能接收BPDU,不能发送BPDU,不能学习,不能转发数据帧。

至于什么是BPDU 这个可以去看看linux-bridge的最小生成树的相关知识

在br_handle_frame函数的forward 标签下,

如果p->state 是FORWARDING的才会调用到brouting的hook点的唯一的hook函数ebt_broute,这个是在pre_routing 的调用之前的,
这里是以调用函数的方式来做ebtable的规则的,而不是遍历hook点上面的hook函数来做ebtables上面的规则的,因此,如果想自定义
hook函数,估计要改源码,即brouting这个hook点,只提供了用户接口,没有提供开发接口

至于p->state (端口状态)是什么时候进行状态转换的?还不清楚

网卡新建为一个网桥的端口的时候状态是BR_STATE_DISABLED,

6 结论

经过二层的数据包会经过的hook点如下:

不知道怎样把大图弄上CU,只能用viso画了,然后截图上去了,有点模糊

还有一篇从ebtables的使用角度分析的文章,个人感觉不错的,也贴在这里了
http://ebtables.netfilter.org/br_fw_ia/br_fw_ia.html

ebtables hook的更多相关文章

  1. ebtables和iptables与linux bridge的交互

    本文为翻译文,不一定是逐字逐句的翻译,而且中间会加上自己的一点见解,如有理解错误的地方,还请大家指出,我定虚心学习.原文见链接 其中斜体字是自己的理解,建议和ebtables手册和iptables手册 ...

  2. iptables,lokkit,ebtables,arptables---logrotate

    iptables,lokkit,ebtables,arptables logrotate  这五个位置也被称为五个钩子函数(hook functions),也叫五个规则链. 1.PREROUTING ...

  3. svnserver hook python

    在使用中可能会遇到的错误排除 :1.Error: svn: 解析"D:\www\test"出错,或svn: E020024: Error resolving case of 'D: ...

  4. Android Hook技术

    原文:http://blog.csdn.net/u011068702/article/details/53208825 附:Android Hook 全面入侵监听器 第一步.先爆项目demo照片,代码 ...

  5. Frida HOOK微信实现骰子作弊

    由于微信摇骰子的功能在本地进行随机后在发送,所以存在可以hook掉判断骰子数的方法进行修改作弊. 1.frida实现hook java层函数1)写个用来测试的demo,当我们点击按钮的时候会弹出窗口显 ...

  6. java的关闭钩子(Shutdown Hook)

    Runtime.getRuntime().addShutdownHook(shutdownHook);    这个方法的含义说明:        这个方法的意思就是在jvm中增加一个关闭的钩子,当jv ...

  7. IDT HOOK思路整理

    IDT(中断描述符表)分为IRQ(真正的硬件中断)和软件中断(又叫异常). HOOK的思路为,替换键盘中断处理的函数地址为自己的函数地址.这样在键盘驱动和过滤驱动之前就可以截获键盘输入. 思路确定之后 ...

  8. Android Hook 借助Xposed

    主要就是使用到了Xposed中的两个比较重要的方法,handleLoadPackage获取包加载时候的回调并拿到其对应的classLoader:findAndHookMethod对指定类的方法进行Ho ...

  9. iOS App 无代码入侵的方法hook

    继续Objective-C runtime的研究 最近公司项目在做用户行为分析 于是App端在某些页面切换,交互操作的时候需要给统计系统发送一条消息 在几十个Controller 的项目里,一个一个地 ...

随机推荐

  1. AngularJS 深入理解 $scope

    $scope 的使用贯穿整个 AngularJS App 应用,它与数据模型相关联,同时也是表达式执行的上下文.有了$scope 就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会立刻更 ...

  2. Kafka报错-as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server

    as it has seen zxid 0x83808 our last zxid is 0x0 client must try another server 停止zookeeper,删除datadi ...

  3. 使用php技术实现无刷新的上传文件

  4. sassCore

    core文件 setting 负责基础变量的文件,如常用的颜色,字体等变量. css3 负责css3属性前缀的文件.参考了bourbon,然后进行一系列的扩展及优化,以使解析出来的代码更加合理. me ...

  5. WEB框架

     WEB框架本质                                                                        一.WEB请求流程 所有的web应用,都 ...

  6. JSON与XML优缺点对比分析

    本文从各个方面向大家对比展示了json和xml的优缺点,十分的全面细致,有需要的小伙伴可以参考下. 1. 定义介绍 1.1 XML定义 扩展标记语言 (Extensible Markup Langua ...

  7. Perl爬取江西失信执行

    #! /usr/bin/perl use strict; use Encode qw(encode decode); binmode(STDIN,":encoding(utf8)" ...

  8. SQL Server 临时表的删除

    --临时表与一般的表不同,它是保存到tempDb表中.临时表的表名与你所建的表名也不一样,因为他要为不同人的相同操作创建不同的临时表. --1.错误的删除操作: --错误的临时表删除操作,因为所在数据 ...

  9. js 动态添加行,删除行,并获得select中值赋予 input

    <html> <head>  <title>Ace Test</title>  <script type="text/javascrip ...

  10. C++ MFC打开文件的流程

    打开文件的步骤如下: 弹出打开文件对话框 -> 获取选择的文件,并将文件显示在视图中. 我们程序中经常需要定制的操作如下: 1. 定制弹出的文件对话框,例如需要修改打开文件的类型或扩展名 2. ...