rp_filter 和 network ingress filtering
最近碰到的一个问题是,在一台服务器上配了 VLAN,VLAN 外的其他机器联不通这台服务器的 VLAN里的 IP。各种改路由无果后,搜到一个帖子,提到需要将 rp_filter 设为 0。修改了 {all,interface}.rp_filter=0 后,按照预期的连通了。
rp_filter 是用于设置网络入口过滤 (Network ingress filtering) 策略的。网络入口过滤可以有效过滤伪造 IP 地址的 DoS 流量,保证流量是可追踪的。RFC 3074 中提到了 5 种网络入口过滤的实现,Linux 实现了其中的严格反向路径转发 (Strict Reverse Path Forwarding) 和宽松的反向路径转发 (Loose Reverse Path Forwarding) 。
rp_filter 为 0 时,关闭入口流量过滤;为 1 时,使用严格反向路径转发策略;为 2 时,使用宽松的反向路径转发策略。
严格反向路径转发策略下,收到包后,内核去 Forwarding Information Base (FIB) 查找源地址,如果收到这个包的接口就是用于转发 src 的流量,那么通过检查,否则直接将包丢弃。
宽松的反向路径转发下,收到包后,内核检查对于这个 src 是否有一条可用路由(包括default),有的话,通过检查,反之丢弃。
如果网络中的各个节点都配置了完备的入口流量过滤,那么伪造的流量会在刚进入网络是就被丢弃。RFC 3074 也建议在 ISP 和网络边界节点的地方总是使用严格反向路径转发。Linux 默认是关闭入口流量过滤的(虽然文档中提到 RFC 3704 推荐使用严格反向路径转发策略)。Gentoo 团队很好的遵循了 RFC 3074 的建议,把 rp_filter 的默认值设为了 1 ...
对于每一个新建的 interface,Linux 会自动将 net.ipv4.conf.<interface>.rp_filter 设为 net.ipv4.conf.default.rp_filter 的值。但实际这个 interface 的网络入口过滤策略是 net.ipv4.conf.{all,interface}.rp_filter 中的最大值。因此,想要关闭入口流量过滤的话,需要将 all 和对应 interface 的 rp_filter 都设为 0。当然,如果使用宽松的反向路径转发,只需要将 interface 的 rp_filter 改为 2 就好。
在我们的场景下,服务器在两个网段内,访问 VLAN 内 IP 的流量实际从 bond1 进入,由 bond1.1 处理,流量到达 bond1.1 后无法通过严格反向路径转发的检查,直接被丢弃。
除了 VLAN 的场景,LVS-TUN 的 real server 也是一样,收到 ipip 包的接口是 bond1,实际处理包的接口是 tunl0,解开 IPIP 包后,实际 IP 包的 src 为 client IP,在 tunl0 上,这个 src 无法通过严格的反向路径转发检查。
目前网上的帖子绝大多数都是说关闭入口流量过滤,但宽松的反向路径转发策略9年前就被加入内核,按照文档的建议,这类场景下应该使用宽松的反向路径检查而不是直接关闭入口流量过滤。
Write a more generic note on the ingress filtering mechanisms than this memo, after the taxonomy and the details or the mechanisms (points above) have been fleshed out.
---- RFC 3074
拖延症博客没弄起来,就先扔日记里了。
实际写下来还是觉得很多地方不清不楚,不过至少比再读一遍 RFC,翻内核文档好= =