`
hulianwang2014
  • 浏览: 726443 次
文章分类
社区版块
存档分类
最新评论
  • bcworld: 排版成这样,一点看的欲望都没有了
    jfinal

关于bridge-nf-call-iptables的设计问题

 
阅读更多
熟悉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之前这个假设所带来的收益完全失效,记住此点即可。
分享到:
评论

相关推荐

    解决docker安装完成报:bridge-nf-call-iptables is disabled问题

    WARNING: bridge-nf-call-iptables is disabled WARNING: bridge-nf-call-ip6tables is disabled 2)解决方法: 修改系统文件是的机器bridge模式开启 设置机器开机启动的时候执行下面两条命令 编辑vim /etc/rc.d/rc...

    web-server-plus:分析访问日志使用iptables禁止过于频繁的IP(防DDOS,恶意访问,采集器)

    分析访问日志使用iptables禁止过于频繁的IP,可以用来防DDOS,恶意访问,采集 #帮助 ##封禁IP访问80端口 iptables -I INPUT -p tcp --dport 80 -s 192.168.1.101 -j DROP ##查看规则 iptables --list --line-numbers...

    CentOS7安装K8S

    安装Kubernetes(k8s) 4 资料: 4 配置iptables设置 5 创建 CA 根证书 5 安装etcd 5 ...为什么 kubernetes 环境要求开启 bridge-nf-call-iptables ? 11 配置linux net.ipv4.ip_forward 数据包转发: 13

    k8s-base-setup

    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:# ## ...

    k8s部署方案.docx

    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节点和计算节点,设置如下参数: ...

    ubuntu xen

    net.bridge.bridge-nf-call-iptables = 0 net.bridge.bridge-nf-call-arptables = 0 ``` 通过以上步骤,您已经在 Ubuntu 12.04.2 上成功安装并配置了 Xen,创建了一个 Windows 7 虚拟机。您可以根据需要创建更多的...

    k8s 23.10集群部署安装详细手册

    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 ``` 二、安装...

    kubernets+kubeedge的云边协同计算环境部署

    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'> { ...

    一套完整的K8s高可用集群二进制部署(-V1.20)

    * 将桥接的 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 ...

    2-k8部署之环境配置1

    创建`k8s.conf`文件,设置`net.bridge.bridge-nf-call-ip6tables`和`net.bridge.bridge-nf-call-iptables`为1,开启IPv4转发。通过`modprobe`加载`br_netfilter`模块,并应用配置文件,最后确认系统级设置是否生效。...

    Kubeadm安装Kubernetes1.15记录V1.1.docx

    在/etc/sysctl.d/k8s.conf文件中添加必要的内核参数,如`net.bridge.bridge-nf-call-ip6tables`、`net.bridge.bridge-nf-call-iptables`和`net.ipv4.ip_forward`,然后应用这些更改。这使得Kubernetes能够正确处理...

    CentOS 7.5 安装 K8S v1.11.0 集群

    echo 'net.bridge.bridge-nf-call-iptables = 1' | sudo tee -a /etc/sysctl.d/k8s.conf ``` #### 三、正式安装 Kubernetes 完成了上述准备工作后,接下来将正式安装 Kubernetes。 ##### 2.1 初始化相关镜像 ...

    centos7搭建k8s环境.docx

    net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system ``` 时间同步 在安装 Kubernetes 之前,需要同步时间,以免影响安装过程。 ``` yum install ntpdate -y ntpdate time.windows.com ``` Kubernetes ...

    k8s高可用集群搭建教程

    在所有节点上创建 `/etc/sysctl.d/k8s.conf` 文件,设置 `net.bridge.bridge-nf-call-ip6tables` 和 `net.bridge.bridge-nf-call-iptables` 为 1,然后执行 `sysctl --system` 使配置生效,这允许 iptables 处理来自...

    kubernetes1.11.0安装文档(填坑直男)

    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...

    k8s 1.9.2 离线安装,附离线安装包

    最后,配置路由参数(`net.bridge.bridge-nf-call-ip6tables` 和 `net.bridge.bridge-nf-call-iptables`),以防止 kubeadm 报告路由警告。 在部署阶段,首要任务是安装 Docker。在所有节点上执行 `yum install -y ...

    CentOS7安装k8s-v1.13.4.docx

    net.bridge.bridge-nf-call-iptables = 1 vm.overcommit_memory = 1 ``` **4. 安装 Docker:** Docker是k8s的基础,因为它用于运行容器。使用官方仓库安装最新版本的Docker,并启动Docker服务。 **5. 准备部署目录...

    1、系统初始化1

    - `net.bridge.bridge-nf-call-iptables=1`:启用桥接设备的网络过滤功能。 - `net.bridge.bridge-nf-call-ip6tables=1`:启用IPv6下的桥接设备网络过滤功能。 - `net.ipv4.ip_forward=1`:开启IPv4的IP转发功能...

    二进制部署kubernetes多master集群

    net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system # 使配置生效 ``` 7. **同步时间**: - 安装chrony服务:`yum -y install chrony` - 设置开机自启动:`systemctl enable --now chronyd` - 修改...

    搭建部署Kubernetes集群

    这可以通过创建`/etc/sysctl.d/k8s.conf`文件并启用相关参数来实现,如`net.bridge.bridge-nf-call-ip6tables`,`net.bridge.bridge-nf-call-iptables`以及`net.ipv4.ip_forward`。然后,加载这些配置并启动Linux...

Global site tag (gtag.js) - Google Analytics