基于内核4.4.60版本分析。
BRIDGE_NETFILTER
依赖于NET [=y] && BRIDGE [=y] && NETFILTER [=n] && INET [=y] && NETFILTER_ADVANCED
NF_TABLES_BRIDGE
依赖于BRIDGE && NETFILTER && NF_TABLES
BRIDGE_NF_EBTABLES
依赖于NET [=y] && BRIDGE [=y] && NETFILTER [=y] && NETFILTER_XTABLES [=m]
BRIDGE_EBT_802_3
BRIDGE_EBT_AMONG
BRIDGE_EBT_ARP
BRIDGE_EBT_ARPREPLY
BRIDGE_EBT_BROUTE
BRIDGE_EBT_DNAT
BRIDGE_EBT_IP
BRIDGE_EBT_IP6
BRIDGE_EBT_LIMIT
BRIDGE_EBT_LOG
BRIDGE_EBT_MARK
BRIDGE_EBT_MARK_T
BRIDGE_EBT_NFLOG
BRIDGE_EBT_PKTTYPE
BRIDGE_EBT_REDIRECT
BRIDGE_EBT_SNAT
BRIDGE_EBT_STP
BRIDGE_EBT_T_FILTER
BRIDGE_EBT_T_NAT
BRIDGE_EBT_VLAN
bridge-$(subst m,y,$(CONFIG_BRIDGE_NETFILTER)) += br_nf_core.o
br_netfilter-y := br_netfilter_hooks.o
br_netfilter-$(subst m,y,$(CONFIG_IPV6)) += br_netfilter_ipv6.o
obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
obj-$(CONFIG_NETFILTER) += netfilter/
每个配置项对应一个模块
obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o
obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o
...
有3个主要的配置
CONFIG_BRIDGE_NETFILTER
CONFIG_NF_TABLES_BRIDGE
CONFIG_BRIDGE_NF_EBTABLES
下面将一一分析。
netfilter的bridge family,定义在<linux/netfilter.h>,NFPROTO_BRIDGE。桥里面共有5个hook,和ipv4一样的5个hook对应。这5个hook定义在<linux/netfilter_bridge.h>,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* Bridge Hooks */ /* After promisc drops, checksum checks. */ #define NF_BR_PRE_ROUTING 0 /* If the packet is destined for this box. */ #define NF_BR_LOCAL_IN 1 /* If the packet is destined for another interface. */ #define NF_BR_FORWARD 2 /* Packets coming from a local process. */ #define NF_BR_LOCAL_OUT 3 /* Packets about to hit the wire. */ #define NF_BR_POST_ROUTING 4 /* Not really a hook, but used for the ebtables broute table */ #define NF_BR_BROUTING 5 #define NF_BR_NUMHOOKS 6 |
再来看这5个hook是在桥的哪些部分执行的:
NF_BR_PRE_ROUTING | net/bridge/br_input.c | br_handle_frame() |
NF_BR_LOCAL_IN | net/bridge/br_input.c | br_pass_frame_up() br_handle_frame() (link local) |
NF_BR_FORWARD | net/bridge/br_forward.c | __br_forward() |
NF_BR_LOCAL_OUT | net/bridge/br_forward.c net/bridge/br_multicast.c | __br_deliver() __br_multicast_send_query() |
NF_BR_POST_ROUTING | net/bridge/br_forward.c | br_forward_finish() |
注意:这5个hook的执行是hard code在桥处理代码中的。只要开启了CONFIG_NETFILTER,那么就可以注册桥的hook,和CONFIG_BRIDGE_NETFILTER无关。那么CONFIG_BRIDGE_NETFILTER这宏是做啥用途的呢,接下来分析。
这个配置项,是可以让iptables,ip6tables,arptables能勾到桥中的数据包。源代码:net/bridge/br_netfilter_hooks.c。它在NFPROTO_BRIDGE的5个hook点都注册钩子,比如
static struct nf_hook_ops br_nf_ops[] __read_mostly = {
{
.hook = br_nf_pre_routing,
.pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_PRE_ROUTING,
.priority = NF_BR_PRI_BRNF,
},
在br_nf_pre_routing() hook函数中,判断是IPV4包,就执行
NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->net, state->sk, skb,
skb->dev, NULL,
br_nf_pre_routing_finish);
sysctl里面,还有几个配置,配置是否启用对应的hook
$ ls -1 /proc/sys/net/bridge/
bridge-nf-call-arptables
bridge-nf-call-ip6tables
bridge-nf-call-iptables
bridge-nf-filter-pppoe-tagged
bridge-nf-filter-vlan-tagged
bridge-nf-pass-vlan-input-dev
对应编译源码:net/bridge/netfilter/nf_tables_bridge.c
它是新的nftables框架中,支持NFPROTO_BRIDGE。
ebtables的支持内核代码。
target/linux/ar71xx/patches-3.3/670-openwrt-bridge_optimize_netfilter_hooks.patch
将NF_HOOK,修改为BR_HOOK,在BR_HOOK里面判断是否走NF_HOOK。增加了一个
/proc/sys/net/bridge/bridge-nf-call-custom选项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bool br_netfilter_run_hooks( void ) { return brnf_call_iptables | brnf_call_ip6tables | brnf_call_arptables | brnf_call_ebtables | brnf_call_custom; } static inline int BR_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb, struct net_device *in, struct net_device *out, int (*okfn)( struct net *, struct sock *, struct sk_buff *)) { if (!br_netfilter_run_hooks()) return okfn(net, sk, skb); return NF_HOOK(pf, hook, net, sk, skb, in, out, okfn); } |
用宏CONFIG_BRIDGE_NETFILTER控制,因此需要开启CONFIG_BRIDGE_NETFILTER才能执行5个写死的hook点。这和内核产生了差异,且更改了CONFIG_BRIDGE_NETFILTER的本意。
参考:
https://unix.stackexchange.com/questions/499756/how-does-iptable-work-with-linux-bridge