熟悉ebtables和iptables的都知道,后者提供的选项所能实现的功能要远远多于前者,但这并不是说IP层的功能要比数据链路层的更丰富,因为只要数据包进入网卡,协议栈代码就能“看到”整个数据包,剩下的问题就是如何来解析和过滤的问题了,只要愿意,实际上协议栈完全可以在数据链路层提供和IP层同样的过滤功能,比如ip_conntrack。
然而,协议栈并没有这么实现,因为那样会造成严重的代码冗余,维护成本将会很高。Linux的bridge filter提供了bridge-nf-call-iptables机制来使bridge的Netfilter可以复用IP层的Netfilter代码。Netfilter提供了强大的过滤match,流识别机制,使每一个数据包都可以和一个五元组标示的流关联起来,这样就可以对整个流而不是单独的数据包进行更加人性化的操作,而对流的识别以及之后的过滤作用最大的就是mark机制,注意这个mark并不是数据包本身的,它只在本机协议栈内有效。Netfilter代码可以识别一个流的头包,然后会将一个mark打入该流,接下来的数据包可以直接从流中取出该mark来进行过滤而不必再遍历整个规则链了,类似下面的规则是常用的:
iptables -t mangle -I PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j ACCEPT
iptables -t mangle -A PREROUTING -m state --state ESTABLISHED -j ACCEPT
iptables -t mangle -N mark_Policy"
iptables -t mangle -A mark_Policy $matches1 -j MARK --set-mark 100
iptables -t mangle -A mark_Policy $matches2 -j MARK --set-mark 100
iptables -t mangle -A mark_Policy -m mark ! --mark 0 -j CONNMARK --save-mark类似一种cache机制,只有一个流的第一个数据包才要遍历整个规则链,其余的就可以直接restore出来mark了,接下来协议栈可以根据该mark来进行过滤或者进行Policy Routing。
如果使用bridge-nf-call-iptables的话,能否使bridge层利用上述优势呢?比如抉择哪些数据包需要被本地捕获,哪些数据包需要丢弃,答案当然是模棱两可的,并不绝对。对于上面第二个问题,抉择哪些数据包需要丢弃是可以做到的,因为bridge-nf-call-iptables作用于bridge Netfilter的PREROUTING上,完全可以在FORWARD上做Drop or not的抉择,这没有任何问题,然而对于第一个问题,哪些数据包需要被本地IP层捕获,当前的实现就无能为力,然而只需要修改不多的两行bridge模块的代码,问题便迎刃而解,然而能做如此小的手术解决如此大的问题,确实需要积累很多的常识,我不是自夸,这是实话。
在给出解决办法之前,我首先给出将本应该bridge出去的数据帧捕获到本地IP层会在哪里用到,如果没有实际的需求而去修改代码,那未免太学院派了。一个典型的需求就是透明网桥模式的VPN,VPN的加密和封装需要在IP层进行,因此需要把感兴趣流捕获到IP层,不感兴趣流直接bridge出去,这是一个实际的需求,然而现有的bridge模块的代码却是解决不了,why?听我娓娓道来。
Linux的bridge代码中,bridge-nf-call-iptables体现在br_nf_pre_routing函数中,该函数也是一个Netfilter HOOK函数:
static struct nf_hook_ops br_nf_ops[] __read_mostly = {
{
.hook = br_nf_pre_routing,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_PRE_ROUTING,
.priority = NF_BR_PRI_BRNF,
},
...
}
在该函数的最后:
NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
br_nf_pre_routing_finish);
调用了IP层的Netfilter PREROUTING代码,我希望先调用IP层的Netfilter,在其mangle表中设置好感兴趣流的mark,然后在bridge的nat表中将打上mark的数据帧redirect到本地的IP层,遗憾的是,这是无法做到的,因为优先级的关系,br_nf_pre_routing的优先级是NF_BR_PRI_BRNF,它位于nat的优先级之后:
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
NF_BR_PRI_NAT_DST_BRIDGED = -300, //NAT的优先级
NF_BR_PRI_FILTER_BRIDGED = -200,
NF_BR_PRI_BRNF = 0, //br_nf_pre_routing的优先级
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,
};
因此即使IP层的Netfilter为数据帧打上了mark,该mark也不可能为NAT所用,因此此时已经执行过NAT了...如果此时你说还可以在BROUTING上将数据帧热direct到local IP layer,那你的设备就完全成了一个IP层的设备,虽说还能保持bridge的语义(比如放过arp数据帧),然而这种设计会让你的产品文档很令人费解,你的心理预期也将和最终所想的谬之千里。
最后,我们来看看应该怎么修改代码来解决这个问题。最本质的,那就是修改br_nf_pre_routing这个HOOK函数的优先级,使之执行于bridge的NAT之后,这比较好办,修改br_netfilter.c代码:
static struct nf_hook_ops br_nf_ops[] __read_mostly = {
{
.hook = br_nf_pre_routing,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_PRE_ROUTING,
#ifdef IP_FILTER_BEFORE_NAT
/**
* 2013/03/06 by 赵亚
* 使iptables的PREROUTING在ebtables的DNAT之前进行,
* 因为网桥的DNAT要使用iptables设置的mark
*/
.priority = NF_BR_PRI_NAT_DST_BRIDGED-1,
#else
.priority = NF_BR_PRI_BRNF,
#endif
...
另一处修改是br_nf_pre_routing_finish,问题涉及到执行完IP Netfilter之后,需要从哪里继续的问题,修改该函数的最后:
#ifdef IP_FILTER_BEFORE_NAT
/**
* 2013/03/06 by 赵亚
* 重新开始NF_BR_PRI_BRNF
*/
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, NF_BR_PRI_NAT_DST_BRIDGED);
#else
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, 1);
#endif
NF_BR_PRI_BRNF被定义成了0,如果按照标准的现有2.6.32内核的实现,应该从优先级1开始执行,然而我们的修改版上,由于此时还没有执行NAT,因此需要从NAT开始执行,而我们的br_nf_pre_routing优先级被设置成了NAT的优先级减去1,那么接下来应该从NAT开始。
这个修改也不是说没有副作用的,它使得标准的实现,即NAT位于IP Netfilter之前这个假设所带来的收益完全失效,记住此点即可。
分享到:
相关推荐
WARNING: bridge-nf-call-iptables is disabled WARNING: bridge-nf-call-ip6tables is disabled 2)解决方法: 修改系统文件是的机器bridge模式开启 设置机器开机启动的时候执行下面两条命令 编辑vim /etc/rc.d/rc...
分析访问日志使用iptables禁止过于频繁的IP,可以用来防DDOS,恶意访问,采集 #帮助 ##封禁IP访问80端口 iptables -I INPUT -p tcp --dport 80 -s 192.168.1.101 -j DROP ##查看规则 iptables --list --line-numbers...
安装Kubernetes(k8s) 4 资料: 4 配置iptables设置 5 创建 CA 根证书 5 安装etcd 5 ...为什么 kubernetes 环境要求开启 bridge-nf-call-iptables ? 11 配置linux net.ipv4.ip_forward 数据包转发: 13
EOF | sudo tee /etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1EOFsudo sysctl --system安装泊坞窗# (Install Docker CE)# # Set up the repository:# ## ...
net.bridge.bridge-nf-call-iptables=1 net.ipv4.ip_local_port_range=10000 65000 fs.file-max=2000000 net.ipv4.ip_forward=1 vm.swappiness=0 EOF ``` - 对于其他Master节点和计算节点,设置如下参数: ...
net.bridge.bridge-nf-call-iptables = 0 net.bridge.bridge-nf-call-arptables = 0 ``` 通过以上步骤,您已经在 Ubuntu 12.04.2 上成功安装并配置了 Xen,创建了一个 Windows 7 虚拟机。您可以根据需要创建更多的...
net.bridge.bridge-nf-call-iptables = 1 net.ipv4.conf.default.rp_filter = 0 net.ipv4.conf.all.rp_filter = 0 net.ipv4.ip_forward = 1 ``` 然后,重新加载sysctl配置文件: ``` sysctl --system ``` 二、安装...
net.bridge.bridge-nf-call-iptables = 1 $sysctl -p # 从配置文件“/etc/sysctl.conf”加载内核参数设置 修改 Docker 镜像源 $sudo mkdir -p /etc/docker $sudo tee /etc/docker/daemon.conf <<-'EOF'> { ...
* 将桥接的 IPv4 流量传递到 iptables 的链:cat > /etc/sysctl.d/k8s.conf 、net.bridge.bridge-nf-call-ip6tables = 1、net.bridge.bridge-nf-call-iptables = 1、EOF、sysctl --system * 时间同步:yum install ...
创建`k8s.conf`文件,设置`net.bridge.bridge-nf-call-ip6tables`和`net.bridge.bridge-nf-call-iptables`为1,开启IPv4转发。通过`modprobe`加载`br_netfilter`模块,并应用配置文件,最后确认系统级设置是否生效。...
在/etc/sysctl.d/k8s.conf文件中添加必要的内核参数,如`net.bridge.bridge-nf-call-ip6tables`、`net.bridge.bridge-nf-call-iptables`和`net.ipv4.ip_forward`,然后应用这些更改。这使得Kubernetes能够正确处理...
echo 'net.bridge.bridge-nf-call-iptables = 1' | sudo tee -a /etc/sysctl.d/k8s.conf ``` #### 三、正式安装 Kubernetes 完成了上述准备工作后,接下来将正式安装 Kubernetes。 ##### 2.1 初始化相关镜像 ...
net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system ``` 时间同步 在安装 Kubernetes 之前,需要同步时间,以免影响安装过程。 ``` yum install ntpdate -y ntpdate time.windows.com ``` Kubernetes ...
在所有节点上创建 `/etc/sysctl.d/k8s.conf` 文件,设置 `net.bridge.bridge-nf-call-ip6tables` 和 `net.bridge.bridge-nf-call-iptables` 为 1,然后执行 `sysctl --system` 使配置生效,这允许 iptables 处理来自...
net.bridge.bridge-nf-call-iptables=1 EOF ``` - 加载其他必要的内核模块: ```bash modprobe -- ip_vs modprobe -- ip_vs_rr modprobe -- ip_vs_wrr modprobe -- ip_vs_sh modprobe -- nf_conntrack_ipv4...
最后,配置路由参数(`net.bridge.bridge-nf-call-ip6tables` 和 `net.bridge.bridge-nf-call-iptables`),以防止 kubeadm 报告路由警告。 在部署阶段,首要任务是安装 Docker。在所有节点上执行 `yum install -y ...
net.bridge.bridge-nf-call-iptables = 1 vm.overcommit_memory = 1 ``` **4. 安装 Docker:** Docker是k8s的基础,因为它用于运行容器。使用官方仓库安装最新版本的Docker,并启动Docker服务。 **5. 准备部署目录...
- `net.bridge.bridge-nf-call-iptables=1`:启用桥接设备的网络过滤功能。 - `net.bridge.bridge-nf-call-ip6tables=1`:启用IPv6下的桥接设备网络过滤功能。 - `net.ipv4.ip_forward=1`:开启IPv4的IP转发功能...
net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system # 使配置生效 ``` 7. **同步时间**: - 安装chrony服务:`yum -y install chrony` - 设置开机自启动:`systemctl enable --now chronyd` - 修改...
这可以通过创建`/etc/sysctl.d/k8s.conf`文件并启用相关参数来实现,如`net.bridge.bridge-nf-call-ip6tables`,`net.bridge.bridge-nf-call-iptables`以及`net.ipv4.ip_forward`。然后,加载这些配置并启动Linux...