Recently I tried using iptables marks together with ip rule on my software router to route requests to a particular ip set through a particular routing table. After the initial configuration, iptables and ip rule never took effect. I tried countless approaches along the way, at one point creating a routing table with tens of thousands of entries to achieve this.
Eventually, I found that the cause was these two settings, fwmark_reflect and rp_filter:
- fwmark_reflect defaults to 0 and should be set to 1;
- rp_filter defaults to 0 and should be set to 2.
After changing these two settings, the configuration worked properly. I have not fully understood the principles behind these settings because of time constraints. Interested readers can look into them.
The configuration and comments are below:
iptables -t mangle -N YOUR_CHAIN_NAME
# 来自指定 ip 的包不走这套规则,可以加你需要指定的其他 ip
iptables -t mangle -A YOUR_CHAIN_NAME -s 192.168.1.1/32 -j RETURN
iptables -t mangle -A YOUR_CHAIN_NAME -s 192.168.1.xxx/32 -j RETURN
# 目标 ip 匹配上指定 ip set 的包,不走指定的特殊路由表(也就是走默认路由表)
iptables -t mangle -A YOUR_CHAIN_NAME -m set --match-set your_ip_set_name dst -j RETURN
iptables -t mangle -A YOUR_CHAIN_NAME -j MARK --set-mark 100
iptables -t mangle -A PREROUTING -s 192.168.1.0/24 -j YOUR_CHAIN_NAME
ip -4 rule add fwmark 100 table 100
sysctl -w net.ipv4.conf.all.src_valid_mark=1
# 如果基于 fwmark 的路由规则不能正常工作,可以加这几个配置
sysctl -w net.ipv4.fwmark_reflect=1
sysctl -w net.ipv4.conf.all.rp_filter=2
# 涉及到的 interface 都配置下
sysctl -w net.ipv4.conf.your_interface_device.rp_filter=2