ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ebtables

2021-01-16 17:36:50  阅读:431  来源: 互联网

标签:ebtables NF hook routing br skb forward


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函数

标签:ebtables,NF,hook,routing,br,skb,forward
来源: https://www.cnblogs.com/alix-1988/p/14286661.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有