惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
J
Java Code Geeks
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
腾讯CDC
GbyAI
GbyAI
I
InfoQ
博客园 - Franky
G
Google Developers Blog
Last Week in AI
Last Week in AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Visual Studio Blog
Vercel News
Vercel News
博客园_首页
MyScale Blog
MyScale Blog
Martin Fowler
Martin Fowler
N
Netflix TechBlog - Medium
V
V2EX
T
The Blog of Author Tim Ferriss
M
MIT News - Artificial intelligence
雷峰网
雷峰网
H
Hackread – Cybersecurity News, Data Breaches, AI and More
大猫的无限游戏
大猫的无限游戏
The GitHub Blog
The GitHub Blog

K.I.S.S

身在 Kimi 的 800 天 写在 Kimi K2 发布之后:再也不仅仅是 ChatBot macOS 按域名切换 DNS 解析 | K.I.S.S 为什么说 GPT 是无损压缩 | K.I.S.S 握着你的手训一个类GPT语言模型 (二) | K.I.S.S 握着你的手训一个类GPT语言模型 (一) | K.I.S.S Rosetta in Linux Virtual Machine on Apple Silicon Fit an Overfit with MegEngine 把 CUPS 扔进 docker 里 ASUS Chromebook Flip | K.I.S.S GPG 与 SSH Agent 转发 YubiKey 4 简介与配置 | K.I.S.S 定制GH60机械键盘 | K.I.S.S Debian 网络安装内核参数 | K.I.S.S 我的 Vim 配置 | K.I.S.S Libinput 与 Udev | K.I.S.S 给妹子看的 Arch Linux 桌面日常安装 | K.I.S.S 修复GTK3 CSD外观 | K.I.S.S 握着你的手教你画哀女王 | K.I.S.S R.I.P Google Reader | K.I.S.S Richard Stallman仍然是对的 | K.I.S.S Scala 自定义控制结构 | K.I.S.S 入手Wacom Bamboo CTL470 | K.I.S.S 转个型? | K.I.S.S Wish List | K.I.S.S pulseaudio音量问题 | K.I.S.S 入手yubikey,一点小心得 | K.I.S.S 迁移到Octopress | K.I.S.S 自动更新DNSPod记录 | K.I.S.S Linux 用作 IPv6 网关 | K.I.S.S
使用 dnsmasq 和 ipset 的策略路由
2016-02-20 · via K.I.S.S

今天试了一把 dnsmasq 的 ipset 功能,配合 iptables 和 iproute2 即可实现基于域名的策略路由。 网上已有一些文章介绍了这种方法,但多是面向 OpenWrt 在路由器上做, 我直接拿来则发现在本地跑其实有坑。

基本需求和思路

我平时一直开着 VPN,配合 chnroute 实现策略路由。但是在学校里访问各类学术数据库需要用学校 IP,过去的做法是把几个常用数据库的 IP 加进路由表里,然而最近一些数据库开始使用 CDN 了,IP 三天两头的变化…… 所以基于域名实现策略路由势在必行。

Ipset 是一个可以和 iptables 配合使用的工具,可以把一系列 ip 放进一个集合中,iptables 则根据集合名进行规则匹配。Dnsmasq 从 2.66 版本之后就支持将一些域名的查询结果放进 ipset 中,这样就可以对这些域名对应的 IP 使用 iptables 处理。集合中的数据包在 iptables mangle 表打上 mark,再使用 iproute2 的规则(rule),对该 mark 的数据包查询一个单独的路由表,从而实现策略路由。

配置流程

VPN?

这个不用说了。

IPSET

首先创建 ipset

ipset create bypass_vpn hash:ip

这个每次重启就会丢失,可以 ipset save 导出配置文件,Arch Linux 的 ipset 包还带了 systemd service,所以

sudo ipset save | sudo tee /etc/ipset.conf
sudo systemctl enable ipset.service

即可。

DNSMASQ

配置 dnsmasq,这里推荐一下 @felixonmars 的 dnsmasq-china-list,把国内 常见网站都涵盖了,自己再把常见学术数据库的域名加进去。但是这份配置中不包含 ipset 的配置,这个简单,简单的全文替换就好。

格式如下:

server=/cn/114.114.114.114  # 这里指定域名的上级 DNS 
ipset=/cn/bypass_vpn        # 这里指定查询结果要放入的 ipset
server=/sciencedirect.com/166.111.8.28
ipset=/sciencedirect.com/bypass_vpn

我自己是将server=ipset=开头的配置分别放在了 china.confchina-ipset.conf 两个文件中,/etc/dnsmasq.conf 中再使用 conf-dir=/etc/dnsmasq.d 配置。

路由表,IPTABLES, IP RULE

首先创建路由表(重启仍有效)

echo "200 bypass_vpn" | sudo tee -a /etc/iproute2/rt_tables   # 这里数字在 1 到 252 之间,不要重复

再加 iptables 规则

sudo iptables -t mangle -N fwmark
sudo iptables -t mangle -A PREROUTING -j fwmark  # 对转发数据包有效
sudo iptables -t mangle -A OUTPUT -j fwmark      # 对从本地发出的数据包有效
sudo iptables -t mangle -A fwmark -m set --match-set bypass_vpn dst -j MARK --set-mark 1  # 给目标地址在 bypass_vpn 中的数据包打上mark 1

在 VPN 启动后,执行一些操作:

  • 把默认物理路由设为 bypass_vpn 表的默认路由
  • 让 mark=1 的数据包查询 bypass_vpn 表

我是在 VPN 的 post-up 脚本中加了这么几句:

OLDGW=$(ip route show 1/0 | head -n1 | sed -e 's/^default//')

gwdev=$(echo $OLDGW|awk '{print $4}')   # 物理设备
ip route show dev $gwdev | while read gwroute; do
	ip route add $gwroute dev $gwdev table bypass_vpn
done

ip rule add fwmark 1 table bypass_vpn

填坑

按理说到这里就该好了,但是实际是跑不起来的,抓包发现,虽然 iptables 的 mark 打上了,路由查询也是对的,但是发出去的包源IP竟然是我的 VPN 网卡地址。

然后 @shankerwangmiao 和 @hexchain 帮我找到了下面这张图还有这份文档。 大致意思就是,Linux 给数据包设置的源 IP 是通过查路由表决定的,而 Routing Decision 的过程发生在进入 iptables 之前,所以这时候根据规则,查询的是默认路由表,策略 路由没有起作用。在经过 mangle OUTPUT chain 之后,有一次 reroute check 过程,如果被打上了 mark,则再决定是不是要根据别的路由表发包,但是这时源地址中央已经 决定了。

解决的办法也比较简单,通过 NAT 把源地址改了就好

sudo iptables -t nat -A POSTROUTING -m mark --mark 0x1 -j MASQUERADE
# 或者使用
sudo iptables -t nat -A POSTROUTING -m mark --mark 0x1 -j SNAT --to-source <your ip>

这时 tcpdump 发现 ICMP reply 已经收到了,但是 ping 仍然没反应。这是因为 Arch Linux 默认的 rp_filter 策略是严格路由匹配,即我收到 ICMP 包的网卡(物理网卡)并不是 最佳路径(默认路由表中VPN才是最佳路径)所以就丢掉了。所以把 rp_filter 改成宽松模式(路由可达即认为数据包合法)

sudo sysctl net.ipv4.conf.{all,enp2s0}.rp_filter=2  # 你的网卡不一定叫 enp2s0

其实默认改成宽松也行,编辑 /etc/sysctl.d/60-rp_filter.conf

net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.rp_filter = 2

以上。