ipv6 over gre/sit: 通过6in4获得ipv6地址

前言

最近用旧笔记本装了个 ESXi 当 NAS 用,其中有一个功能是我期待很久的:离线下载,但是当我挂公网 bt 的时候发现速度实在感人,然而由于某种原因 ESXi 的物理网卡接口上又拿不到 ipv6 但是我可以控制的某台机器有一段 /64,所以就开始琢磨着怎么偷一段回来了。

场景描述

RouterA:

  • 有 ipv4 地址 $routerA_wan_ipv4eth0 上可以连接到 RouterB
  • 有 ipv4 地址 $routerA_lan_ipv4eth1 上可以连接到 NAS
  • 无 ipv6 地址

RouterB:

  • 有 ipv4 地址 $routerB_wan_ipv4eth0 上可以连接到 RouterA
  • 有 ipv6 地址 $routerB_wan_ipv6eth0 上可以连接到 WAN

NAS:

  • 有 ipv4 地址 $NAS_lan_ipv4 可以连接到 RouterA
  • 无 ipv6 地址

隧道两端地址

  • RouterA 端地址 $RouterA_tunnel_ipv4$Router_tunnel_ipv6
  • RouterB 端地址 $RouterB_tunnel_ipv4$Router_tunnel_ipv6

网络拓扑

1
NAS ---<eth1> RouterA <eth0>---<eth0> RouterB <eth0>--- WAN

现在想要实现的目标是在 RouterARouterB 之间打一条隧道让 NAS 能正常接入 ipv6 网络,由于 RouterARouterB 之间只能用 ipv4 交换数据,所以我自然想到了 6in4,所以可选的方案有 ipv6 over greipv6 over sit 两种,其中虽然 sit 可以直接用 luci 配置但是不支持封装 ipv4 协议,而 gre 同时支持 ipv6 和 ipv4 的封装,两者的配置基本是一样的。

建立隧道

RouterA

1
2
3
4
ip tunnel add nasgre mode gre remote $RouterB_wan_ipv4 local $RouterA_wan_ipv4
ip addr add $RouterA_tunnel_ipv4 dev nasgre
ip addr add $RouterA_tunnel_ipv6 dev nasgre
ip link set nasgre up

RouterB

1
2
3
4
ip tunnel add nasgre mode gre remote $RouterA_wan_ipv4 local $RouterB_wan_ipv4
ip addr add $RouterB_tunnel_ipv4 dev nasgre
ip addr add $RouterB_tunnel_ipv6 dev nasgre
ip link set nasgre up

到这里隧道的两端就配好了。

设置路由

RouterA

1
2
3
ip -6 ro del default
ip -6 ro add default via $RouterB_tunnel_ipv6 dev nasgre
ip addr add $RouterA_lan_ipv6 dev eth1

其中 $RouterA_lan_ipv6 应该是 NAS 的默认网关。

RouterB 上不出意外是不用设置额外路由的。

到这里 RouterA 应该能 ping 通 $RouterB_tunnel_ipv6 了(当然也可能不通,见下文),然而我的最终目标是 NAS 能正常使用,但是虽然我给 NAS 设置了一个 ipv6 地址但是并不能正常上网,所以下面就是我踩的坑了。

iptables 放行 gre

这个是最坑的,iptables + tcpdump 了一年才发现 iptables 默认会拦截所有 gre 包即使 policy 是 ACCEPT。

1
2
iptables -I INPUT 1  -p gre -j ACCEPT
iptables -I OUTPUT 1 -p gre -j ACCEPT

这样隧道两端就应该通了。

ipv6 ndp proxy

这个是第二个坑点。

proxy arp

首先我们知道 ipv6 的 ndp 协议替代了 ipv4 的 arp 协议,在 arp 中有个东西叫作 proxy arp,它的定义是

Proxy ARP is a technique by which a device on a given network answers the ARP queries for an IP address that is not on that network. The ARP proxy is aware of the location of the traffic’s destination, and offers its own MAC address as (ostensibly final) destination.[1] The traffic directed to the proxied IP address is then typically routed by the proxy to the intended destination via another interface or via a tunnel.
The process, which results in the node responding with its own MAC address to an ARP request for a different IP address for proxying purposes, is sometimes referred to as ‘publishing’.

也就是说在某个设备知道去往某个网络的路由的时候,如果有其他设备希望知道这个网络的 mac,它会回应自己的 mac 地址。

听起来有点费解,先思考一下设备什么时候会发送 arp 地址?

  • 在有默认网关的情况下,如果目标地址不跟本机在同一个网络,那么直接发给默认网关(当然这里需要用 arp 协议获得默认网关的 mac)
  • 没有默认网关的情况下,对任意地址都需要用 arp 协议转换为 mac 地址。

对于第一种情况,一般情况下同一个子网的机器都是二层互通的所以问题不大,但是假如有如下拓扑

1
2
3
4
5
6
7
8
9
         192.168.1.1/24
Router
eth0 / \ eth1
/ \
/ \
/ \
/ \
DeviceA DeviceB
192.168.1.2/24 192.168.1.3/24

其中路由器上 eth0 和 eth1 没有桥接,在这种情况下当 DeviceA 想和 DeviceB 通信的时候会发送 arp 包询问 DeviceB 的 mac 地址,但是很不幸的是 Router 不会把这个广播包从 eth0 转发到 eth1,在这种情况下 proxy arp 就排上用场了:Router可以在 eth0 上收到询问 DeviceB 的 arp 包的时候回应自己的 mac 地址,配置大概是

1
2
ip neigh add proxy 192.168.1.3 dev eth0
ip neigh add proxy 192.168.1.2 dev eth1

这样 DeviceA 发往 DeviceB 的包都会先发往 Router,这时候由于是三层的包所以 Router 会正常转发到 eth1 上,可以说是“善意的欺骗”。

类似的对于第二种情况,Router 可以设置为对于任意地址的 arp 广播包都返回自己的 mac 地址,这样实质上就是实现了默认网关。

设置 proxy

回到之前的拓扑

1
NAS --- RouterA ---<gre tunnel>--- RouterB --- Gateway --- WAN

现在就有一个问题,RouterB 的默认网关并不能发现隧道后 RouterANAS 上的任意 ipv6 地址,因为 RouterB 在收到来自默认网关的 RS 报文的时候不会转发给 RouterA,所以这里我们需要让 RouterB 的默认网关知道发往 RouterANAS 的包要先通过 RouterB,除了直接桥接隧道 interface 和 eth0 让广播包正常通过隧道以外的方法就是 ipv6 ndp proxy 了,类似之前第一种情况的解决方法,只要在 RouterB 上执行

1
2
ip neigh add proxy $nas_lan_ipv6 dev eth0
ip neigh add proxy $RouterA_lan_ipv6 dev eth0

同样的在 RouterA 上执行

1
2
ip neigh add proxy $RouterA_tunnel_ipv6 dev eth1
ip neigh add proxy $RouterB_tunnel_ipv6 dev eth1

这样隧道后面的 ipv6 地址就可以被正常发现了。

另外这里需要这两行的原因是我在分配隧道 ipv6 的时候没有使用私用地址而且还跟 NAS 的 ipv6 一个段。

小结

到这里就配置好啦,虽然饶了不少弯路,但是教育网 pt 这个速度值呀。

不过反思一下,实际上整体配置还是有一些可以改进的地方。

  • 隧道两端可以用私有 /126 地址
  • 直接桥接 interface 应该也是一个不错的选择

不过反正都跑通了,管他呢(逃

参考资料

ip-neighbour (8) - Linux Man Pages
Breaking a network in two with proxy ARP
Proxy ARP
Linux下建立GRE隧道并获取IPv6地址