文章

配置vpnc:不添加默认路由避免影响上网

问题

vpnc 是一个 Linux 下的 Cisco VPN 客户端,它可以让我们连接到 Cisco VPN 服务器。但是,vpnc 会添加一个默认路由,这样我们的所有网络请求都会通过 VPN 服务器转发,这样会导致我们无法正常上网。

例如,以下是普通情况的路由表:

1
2
3
4
5
$ ip route
default via 172.16.170.254 dev enp3s0 proto dhcp metric 100
10.42.0.0/24 dev wlp4s0 proto kernel scope link src 10.42.0.1 metric 600
172.16.170.0/24 dev enp3s0 proto kernel scope link src 172.16.170.159 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown

在启动 vpnc 之后,路由表变成了这样:

1
2
3
4
5
6
7
8
$ sudo vpnc
$ ip route
default dev tun0 scope link
default via 172.16.170.254 dev enp3s0 proto dhcp metric 100
10.42.0.0/24 dev wlp4s0 proto kernel scope link src 10.42.0.1 metric 600
172.16.170.0/24 dev enp3s0 proto kernel scope link src 172.16.170.159 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.17.72.0/23 dev tun0 scope link

可以看到,多出了一个 default dev tun0 scope link,这个路由会导致所有网络请求都通过 VPN 服务器转发,从而影响我们正常上网。

解决

vpnc 对路由表的变动实际上是通过一个外部的脚本来实现的,默认情况下这个脚本是 /usr/share/vpnc-scripts/vpnc-script,而这个脚本是通过调用 ip routeroute 命令来实现对路由比的修改的。

我们可以通过 --script 选项或修改配置文件中的 Script /path/to/our/script 来指定一个自定义的脚本,例如:

1
2
3
4
5
6
$ sudo vpnc --script /dev/null
$ ip route
default via 172.16.170.254 dev enp3s0 proto dhcp metric 100
10.42.0.0/24 dev wlp4s0 proto kernel scope link src 10.42.0.1 metric 600
172.16.170.0/24 dev enp3s0 proto kernel scope link src 172.16.170.159 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown

可以看到,这次路由表就没有发生任何变化,但是这也意味着 VPN 并不能正常工作。我们可以在自定义的脚本中添加一些手动路由规则,来实现 VPN 和正常上网的共存,这个脚本的内容比较复杂,建议复制 /usr/share/vpnc-scripts/vpnc-script,然后基于它来修改。

我们需要找到它有关默认路由设置的部分,然后将它们改为我们自己需要的路由规则,例如:

1
2
3
4
5
6
7
	set_default_route() {
		$IPROUTE route | grep '^default' | fix_ip_get_output > "$DEFAULT_ROUTE_FILE"
		# $IPROUTE route replace default dev "$TUNDEV"
    # 改为
    $IPROUTE route add 172.16.0.0/16 dev "$TUNDEV"
		$IPROUTE route flush cache 2>/dev/null
	}
1
2
3
4
5
6
7
	set_ipv6_default_route() {
		# We don't save/restore IPv6 default route; just add a higher-priority one.
		# $IPROUTE -6 route add default dev "$TUNDEV" metric 1
    # 改为
		$IPROUTE -6 route add 172.16.0.0/16 dev "$TUNDEV" metric 1
		$IPROUTE -6 route flush cache 2>/dev/null
	}

这样,就只有 172.16.0.0/16 这个网段的数据包会被发送到 VPN 服务器,其他的数据包会按照原来的路由表进行转发,就能实现 VPN 和正常上网的共存了。

补充学习:ip route 输出的含义

  • default dev tun0 scope link

    这段信息应该这么拆开来看:

    • default

      默认路由,即当没有匹配的路由时,数据包将被发送到这个路由。这个地方也可以是一个 IP 网段,例如下面的 10.42.0.0/24 那样。

    • dev tun0

      数据包将通过设备 tun0 发送。这个设备文件一般可能出现在两个地方:

      1. /dev/tun0
      2. /sys/class/net/tun0

      后者往往是一个到 /sys/devices/pci... 的一个软链接,在系统启动时自动创建。

    • scope link:表示这是一个直连路由(回想 IP 协议里直接交付和间接交付的概念)。

  • default via 172.16.170.254 dev enp3s0 proto dhcp metric 100

    只介绍和之前不一样的:

    • via 172.16.170.254

      表示数据包将被转发到这个 IP 地址,再在路由表中查询这个 IP 地址,可知数据包会被发送到 enp3s0 这个设备。

    • proto dhcp

      这个路由是通过 DHCP 协议获取的。

    • metric 100

      路由的优先级,数值越小,优先级越高。

  • 10.42.0.0/24 dev wlp4s0 proto kernel scope link src 10.42.0.1 metric 600

    • 10.42.0.0/24

      default 在一样的位置,只是这次表示只有特定网段的数据包会被发送到这个路由了。

    • src 10.42.0.1

      这个地址会被填在 IP 数据包的源地址字段里。

本文由作者按照 CC BY 4.0 进行授权