Skip to content

将tinc的流量进行本地卸载,以达到分流VPN的作用

License

Unknown, Unknown licenses found

Licenses found

Unknown
COPYING
Unknown
COPYING.README
Notifications You must be signed in to change notification settings

chk-jxcn/tinc-local-offload

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Next

实际运行多线程后,发现经常decrypt失败,后来了解一下加密的原理才发现瞎忙一场。 咳,考虑不周,因为chacha是带状态的流加密,所以不能拆分成多线程,但是tinc默认就只有一个连接,所以如果不分块或者对连接动手的话,没啥用。 树莓派的AES性能又垃圾到不行,怎么办才好呢。

  1. 在线程中处理包

chacha20-poly1305不能多线程同时对一个流加解密。。所以。。

因为资源大部分都消耗在加密解密上,所以应该把加密解密分离到其他的线程。但是由于tinc本身是单线程设计的,因此其中的数据访问并没有进行多线程的保护。不过sptps协议设计的比较简单,而且本身很容易设计成异步执行,所以我是这样想的:

上半部分,处理sptps协议

  Thread:
  Thread pool: encrypt/decrypt
  dequeue Thread: send a io request to libevent
  sptps_receive_data -> in queue -> decrypt by thread pool -> dequeue
  sptp_send_data -> in queue -> encrypt by thread pool -> dequeue

下半部分,main thread中收发包

  dequeue(io notify)-> (main thread)io watch -> sptps callback

不过要注意的是,握手和其他明文包也需要按序列发送,所以也应该由thread pool来处理,但是协议中间不会发送有更新key什么的消息,我觉得只需要把DATA交给thread pool就行

使用sptps_speed 测试的结果, 比以前还慢了很多,感觉上是用pipe同步callback和dequeue的原因 想了一下,应该是sptps_speed的实现是一收一发,所以没有享受到多线程的增益,因为队列里最多就一个数据等待加密或者解密

-O3
chk@T620:~/tinc-local-offload-thread/src % ./sptps_speed 1
Generating keys for 1 seconds:           4775.39 op/s
Ed25519 sign for 1 seconds:              4687.92 op/s
Ed25519 verify for 1 seconds:            1457.95 op/s
ECDH for 1 seconds:                      1167.75 op/s
SPTPS/TCP authenticate for 1 seconds:     554.28 op/s
SPTPS/TCP transmit for 1 seconds:         637.49 Mbit/s
SPTPS/TCP MP transmit for 1 seconds:         504.10 Mbit/s
SPTPS/UDP authenticate for 1 seconds:     552.58 op/s
SPTPS/UDP transmit for 1 seconds:         642.62 Mbit/s

-O0
chk@T620:~/tinc-local-offload-thread/src % ./sptps_speed 1
Generating keys for 1 seconds:           2258.62 op/s
Ed25519 sign for 1 seconds:              2162.85 op/s
Ed25519 verify for 1 seconds:             824.04 op/s
ECDH for 1 seconds:                       610.23 op/s
SPTPS/TCP authenticate for 1 seconds:     298.93 op/s
SPTPS/TCP transmit for 1 seconds:         205.14 Mbit/s
SPTPS/TCP MP transmit for 1 seconds:         279.38 Mbit/s
SPTPS/UDP authenticate for 1 seconds:     298.59 op/s
SPTPS/UDP transmit for 1 seconds:         205.06 Mbit/s

  1. 为本地路由的包增加一个type

New feature: local route (2020-07)

发送sptps packet的时候,tinc会首先在本地route,寻找目标node,然后把(source, dest)放在压缩的packet前,这样下一跳收到包后就可以不解压直接根据dest id来确定是不是要转发到下一个节点。 如果dest id是自己则再路由一次,路由后可能发给自己或者发给别的节点(如果设置StrictSubnets,每个节点的路由表并非一定一致) 为了解决如下需求, 增加本地路由的feature:

  1. 想要每个节点的接口地址被广播,即不能设置StrictSubnets
  2. 根据目的IP来路由包到不同的节点,比如大陆地址只到大陆的节点,但是不能让tinc的路由表太大,否则遍历路由表会花费太多时间(foreach in splay tree)

具体实现:

  1. 增加route文件夹,里面每个文件用主机名命名,每一行都是一个subnet:x.x.x.x/prefixlen,tinc启动或reload时加载到lctire,成为本地路由表
  2. 在搜索tinc路由表之前,搜索本地路由表,如果找得到,则通过sptps协议发送,不再使用直接发送tcp及udp报文
  3. 节点收到sptps packet后,如果DSTID = myself,则直接发送到接口,不再做一次route

Tinc 本地卸载分流方案

首先,请记住5月31日这个日子,防火墙从这一天开始彻底疯了。。。

对于国内外分流,我知道的方案有iptables,或者添加路由,当然如果是PC客户端的话,也可以用PAC。

上面两种都可以配合chinadns来实现国内外流量分流。

下面是这里描述的另外一种方案

拓扑图

alt text

原理

在tinc中启动两个接口,当tun0中接收到报文时,如果目标地址匹配china subnets的话,转发到另外一个接口,如果设置了NAT地址,则把源地址进行NAT后送出去。

国内流量的方向

eth1下的设备->eth1->tun1->tun0->eth0

国外流量的方向

eth1下的设备->eth1->tun1->VPN 服务器

配合chinaDNS(https://github.com/cherrot/gochinadns) 可以实现国内域名发送到国内地址, 国外域名发送到国外地址

修改的路由及iptables部分(iptables 做NAT,ip rule实现源地址路由):

# $INTERFACE = tun1
# $INTERFACELOCAL = tun0
ip route add default via 192.168.1.1 dev eth0 200
ip route add default dev $INTERFACE scope link table 500

ip rule add from 192.168.200.100 lookup 500
ip rule add from all iif $INTERFACELOCAL lookup 200

iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

修改的tinc代码:

  • 增加tun1设备
  • 使用https://github.com/chuckination/lctrie 来进行目标地址匹配(Intel 3205U测试,单线程匹配5000条国内子网表,大概10Mqps,对于大部分机器来说都不是瓶颈了)
  • 移植freebsd下的libalias(https://wiki.freebsd.org/Libalias) 来执行NAT(不执行NAT也可以,但是iptables似乎无法正确在出口对tun0进来的包进行NAT)

遇到的问题

  • tinc-1.1pre17无法在freebsd链接libz,需要删除__nonull__的定义 (bug of tinc)
  • lctrie 中的ip addr结构与tinc有冲突,用uint32_t 传递IP地址
  • libalias 中的LIST_NEXT等宏在linux中未定义,引入freebsd的queue头文件
  • libalias 中检测头文件是否包含的宏与linux头文件定义不一致
  • libalias 中的ip, tcp, udp等结构与linux中定义不一致,引入宏定义 -DNO_FW_PUNCH -D_BSD_SOURCE -D__BSD_SOURCE -D__FAVOR_BSD
  • chinadns 实现中的upstream DNS server会根据IP来决定是否Trusted,修改为全部Untrusted(因为需要使用内网dns服务器进行缓存)

最后的最后,如果使用源地址路由的话,请修改下面的系统配置:

# 不添加iptables出口NAT不正常
for x in `ls  /proc/sys/net/ipv4/conf/*/accept_source_route`; do echo 1 > $x ; done
# 不添加则系统会丢弃tun0接收到的报文
for x in `ls  /proc/sys/net/ipv4/conf/*/rp_filter`; do echo 0 > $x ; done

NAT性能测试

tun1 是本地卸载端口

tun0 为入口

                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average   |||

      Interface           Traffic               Peak                Total
            ng0  in    197.185 KB/s        204.577 KB/s          100.123 MB
                 out     4.938 MB/s          5.104 MB/s            1.323 GB

           tun1  in    197.118 KB/s        204.297 KB/s           84.025 MB
                 out     5.669 MB/s          5.862 MB/s          654.152 MB

           tun0  in      5.653 MB/s          5.846 MB/s            2.072 GB
                 out   209.493 KB/s        217.079 KB/s          176.669 MB

          ue0.3  in      0.000 KB/s          1.372 KB/s            1.530 GB
                 out     0.000 KB/s          1.243 KB/s            1.447 GB

            ue0  in      5.977 MB/s          6.188 MB/s           24.051 GB
                 out     5.210 MB/s          5.385 MB/s           24.024 GB

            lo0  in      0.000 KB/s          0.000 KB/s            9.403 MB
                 out     0.000 KB/s          0.000 KB/s            9.403 MB

usb用的cpu多是因为树莓派的网口其实是挂在usb下面

基本上50Mbps就是极限了,本身网口也只是100M的

                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average   |||||||

                    /0%  /10  /20  /30  /40  /50  /60  /70  /80  /90  /100
root          tincd XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
root           idle XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
root            usb XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
root           idle XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
root           idle XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
root           idle XXXXXXXXXXXXXXXXXXXXXXXXX
root     rand_harve X
root           intr X

一个月后

基本上可以一直开着,打游戏延迟正常,淘宝优酷正常

ng0是pppoe接口,我一般用pppoe拨号来从PC连接到本地VPN,可以保证易用性和以及在PC上稳定的路由

ue0.3是提供vpn的vlan接口

至于如何让不同的设备来无缝连接VPN,又是另外一个故事了。

另外TAP方案已经放弃了(太过繁琐)

                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average

      Interface           Traffic               Peak                Total
            ng0  in      0.179 KB/s          0.350 KB/s            7.424 MB
                 out     0.140 KB/s          2.591 KB/s           45.293 MB

           tun1  in      0.132 KB/s          0.282 KB/s            4.928 GB
                 out     0.052 KB/s          0.119 KB/s           62.587 GB

           tun0  in      0.140 KB/s          2.591 KB/s          100.533 GB
                 out     0.188 KB/s          0.836 KB/s           59.464 GB

          ue0.3  in      0.000 KB/s          0.416 KB/s           10.125 GB
                 out     0.000 KB/s          0.323 KB/s            9.604 GB

            ue0  in      0.673 KB/s          3.643 KB/s          174.907 GB
                 out     0.703 KB/s          3.599 KB/s          194.651 GB


New idea

将tun0 修改成tap与出口网口进行桥接,内置dhcp client,就不再需要源地址路由了。

透明转发方案

拓扑图

alt text

tap1/tun1	for offload data
tun0		for local access
tap0		for remote access

Process step:
remote data -> tap0 -> if match china -> send to tap1/tun1
                    -> if not match china -> NAT and send outside

Local data -> tun0  -> if match china -> send to tap1/tun1
                    -> if not match china -> send outside
		    
VPN -> tinc -> try de-alias to tap0 -> success -> send to tap0
                                    -> fail -> send to tun0

From china -> tap1/tun1 -> de-alias -> if dest addr = tun0 -> tun0
                                    -> else -> tap0
				    
For example:
LAN = 192.168.200.0/24
tap0 = 192.168.200.2
tun0 = 192.168.192.211
tap1 = 192.168.1.2
tap0 gateway = 192.168.1.1

packet flow:
To VPN

                 tap0                      tinc                    VPN(other node)
request 192.168.200.3->8.8.8.8
                                 192.168.192.210->8.8.8.8
				                              192.168.192.210->8.8.8.8
reply:							      8.8.8.8->192.168.192.210
				 8.8.8.8->192.168.192.210			      
	8.8.8.8->192.168.200.3						      
							      

                 tun0                      tinc                    VPN(other node)
request 192.168.192.211->8.8.8.8
                                 192.168.192.211->8.8.8.8
				                              192.168.192.211->8.8.8.8
reply:							      8.8.8.8->192.168.192.211
				 8.8.8.8->192.168.192.211			      
	8.8.8.8->192.168.192.211

To china
							      
                 tap0                      tinc                    tap1/tun1
request 192.168.200.3->114.114.114.114
                                 192.168.200.3->114.114.114.114
				                              192.168.1.2->114.114.114.114
reply:							      114.114.114.114->192.168.1.2
				 114.114.114.114->192.168.200.3			      
	114.114.114.114->192.168.200.3						      
							      

                 tun0                      tinc                    tap1/tun1
request 192.168.192.211->114.114.114.114
                                 192.168.192.211->114.114.114.114
				                              192.168.1.2->114.114.114.114
reply:							      114.114.114.114->192.168.1.2
				 114.114.114.114->192.168.192.211			      
	114.114.114.114->192.168.192.211

需要处理的问题:

  1. 对tinc内部接口的ICMP的返回
  2. 请求目标地址的arp并存储,但是不需要响应arp查询,系统会自动处理这部分
  3. 是否需要portmap?

更新:

  1. ICMP未处理
  2. 自己实现了arp协议,启动的时候分配子网大小的arp表,有点耗内存,但是一般客户端很少会有特别大的子网
  3. portmap暂时搁置
  4. 没有实现dhcp协议,所以只能设置静态IP
  5. 测试正常

About

将tinc的流量进行本地卸载,以达到分流VPN的作用

Topics

Resources

License

Unknown, Unknown licenses found

Licenses found

Unknown
COPYING
Unknown
COPYING.README

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published