Ubuntu使用KCPRAW伪装UDP请求,防止运营商拦截

kcptun

kcptun

-

原因及原理

因为KCP多倍发包原理,有些地方运营商会拦截多倍的UDP请求,使用KCPRAW可以把UDP伪装成TCP流量,防止运营商拦截

快速设定

客户端、服务器分别下载对应平台的预编译版本,并解压,通过下面的命令启动端口转发。

1
2
KCP客户端: ./client_darwin_amd64 -r "KCP服务器IP地址:4000" -l ":8388" -mode fast2
KCP服务器: ./server_linux_amd64 -t "目标服务器IP地址:8388" -l ":4000" -mode fast2

以上命令可以实现8388/tcp端口的转发(通过4000/udp端口),即:

Application -> KCP客户端(8388/tcp) -> KCP服务器(4000/udp) -> Server(8388/tcp)

从源码安装

1
2
$go get -u github.com/xtaci/kcptun/client
$go get -u github.com/xtaci/kcptun/server

注意: 如果出现错误提示,请确保依赖库能正确访问到。

Release中的所有二进制版本,是通过 build-release.sh 脚本生成并优化。

速度对比

fast.com

使用方法

在Mac OS X El Capitan下的帮助输出,注意默认值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
$ ./client_darwin_amd64 -h
NAME:
kcptun - client(with SMUX)

USAGE:
client_darwin_amd64 [global options] command [command options] [arguments...]

VERSION:
20170120

COMMANDS:
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--localaddr value, -l value local listen address (default: ":12948")
--remoteaddr value, -r value kcp server address (default: "vps:29900")
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none (default: "aes")
--mode value profiles: fast3, fast2, fast, normal (default: "fast")
--conn value set num of UDP connections to server (default: 1)
--autoexpire value set auto expiration time(in seconds) for a single UDP connection, 0 to disable (default: 0)
--mtu value set maximum transmission unit for UDP packets (default: 1350)
--sndwnd value set send window size(num of packets) (default: 128)
--rcvwnd value set receive window size(num of packets) (default: 512)
--datashard value, --ds value set reed-solomon erasure coding - datashard (default: 10)
--parityshard value, --ps value set reed-solomon erasure coding - parityshard (default: 3)
--dscp value set DSCP(6bit) (default: 0)
--nocomp disable compression
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
--snmpperiod value snmp collect period, in seconds (default: 60)
--log value specify a log file to output, default goes to stderr
-c value config from json file, which will override the command from shell
--help, -h show help
--version, -v print the version

$ ./server_darwin_amd64 -h
NAME:
kcptun - server(with SMUX)

USAGE:
server_darwin_amd64 [global options] command [command options] [arguments...]

VERSION:
20170120

COMMANDS:
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--listen value, -l value kcp server listen address (default: ":29900")
--target value, -t value target server address (default: "127.0.0.1:12948")
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none (default: "aes")
--mode value profiles: fast3, fast2, fast, normal (default: "fast")
--mtu value set maximum transmission unit for UDP packets (default: 1350)
--sndwnd value set send window size(num of packets) (default: 1024)
--rcvwnd value set receive window size(num of packets) (default: 1024)
--datashard value, --ds value set reed-solomon erasure coding - datashard (default: 10)
--parityshard value, --ps value set reed-solomon erasure coding - parityshard (default: 3)
--dscp value set DSCP(6bit) (default: 0)
--nocomp disable compression
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
--snmpperiod value snmp collect period, in seconds (default: 60)
--log value specify a log file to output, default goes to stderr
-c value config from json file, which will override the command from shell
--help, -h show help
--version, -v print the version

分层参数图

params

两端参数必须一致的有:

  • datashard –前向纠错
  • parityshard –前向纠错
  • nocomp –压缩
  • key –密钥
  • crypt –加密算法

其余为两边可独立设定的参数

内置模式

响应速度:
fast3 > fast2 > [fast] > normal > default
有效载荷比:
default > normal > [fast] > fast2 > fast3
中间-mode参数比较均衡,总之就是越快,包重传越激进。
更高级的 手动档 需要理解KCP协议,并通过 隐藏参数 调整,例如:

1
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20

  • 搭配1. fast + FEC(5,5)
  • 搭配2. fast2 + FEC(10,3)
  • 搭配3. fast2 + FEC(0,0)

默认profile参考: https://github.com/xtaci/kcptun/blob/master/client/main.go#L248

前向纠错

前向纠错采用Reed Solomon纠删码, 它的基本原理如下: 给定n个数据块d1, d2,…, dn,n和一个正整数m, RS根据n个数据块生成m个校验块, c1, c2,…, cm。 对于任意的n和m, 从n个原始数据块和m 个校验块中任取n块就能解码出原始数据, 即RS最多容忍m个数据块或者校验块同时丢失

reed-solomon

通过参数

n -parityshard m``` 在两端同时设定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

数据包发送顺序严格遵循: n个datashard紧接m个parityshard,重复。

注意:为了发挥FEC最佳效果,设置 parityshard/(parity+datashard) > packet loss,比如5/(5+5) > 30%

### 窗口调整
**简易窗口自我调优方法**:

> 第一步:同时在两端逐步增大client rcvwnd和server sndwnd;
> 第二步:尝试下载,观察如果带宽利用率(服务器+客户端两端都要观察)到达预期则停止,否则跳转到第一步。

**注意:产生大量重传时,一定是窗口偏大了**

### 安全

无论你上层如何加密,如果```-crypt none```,那么**协议头部**都是**明文**的,建议至少采用```-crypt aes-128```加密,并修改密码。

密码可以通过`-key`指定,也可以通过环境变量`KCPTUN_KEY`指定。

注意: ```-crypt xor``` 也是不安全的,除非你知道你在做什么。

附加密速度Benchmark:

BenchmarkAES128-4 200000 11182 ns/op
BenchmarkAES192-4 200000 12699 ns/op
BenchmarkAES256-4 100000 13757 ns/op
BenchmarkTEA-4 50000 26441 ns/op
BenchmarkSimpleXOR-4 3000000 441 ns/op
BenchmarkBlowfish-4 30000 48036 ns/op
BenchmarkNone-4 20000000 106 ns/op
BenchmarkCast5-4 20000 60222 ns/op
BenchmarkTripleDES-4 2000 878759 ns/op
BenchmarkTwofish-4 20000 68501 ns/op
BenchmarkXTEA-4 20000 77417 ns/op
BenchmarkSalsa20-4 300000 4998 ns/op

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

### 内存控制

路由器,手机等嵌入式设备通常对**内存用量敏感**,通过调节环境变量GOGC(例如GOGC=20)后启动client,可以降低内存使用。
参考:https://blog.golang.org/go15gc


### DSCP

DSCP差分服务代码点(Differentiated Services Code Point),IETF于1998年12月发布了Diff-Serv(Differentiated Service)的QoS分类标准。它在每个数据包IP头部的服务类别TOS标识字节中,利用已使用的**6比特**和未使用的2比特,通过编码值来区分优先级。
常用DSCP值可以参考[Wikipedia DSCP](https://en.wikipedia.org/wiki/Differentiated_services#Commonly_used_DSCP_values),至于有没有用,完全取决于数据包经过的设备。

通过 ```-dscp ``` 参数指定dscp值,两端可分别设定。

注意:设置dscp不一定会更好,需要尝试。

### Snappy数据流压缩

> Snappy is a compression/decompression library. It does not aim for maximum
> compression, or compatibility with any other compression library; instead,
> it aims for very high speeds and reasonable compression. For instance,
> compared to the fastest mode of zlib, Snappy is an order of magnitude faster
> for most inputs, but the resulting compressed files are anywhere from 20% to
> 100% bigger.

> Reference: http://google.github.io/snappy/

压缩对于非加密,非压缩的数据能降低传输数据量,比如点对点的HTTP数据转发。

通过参数 ```-nocomp``` 在两端同时设定以关闭压缩。
> 提示: 关闭压缩可能会降低延迟。

### 流量控制

**必要性: 针对流量敏感的服务器,做双保险。**

> 基本原则: SERVER的发送速率不能超过ADSL下行带宽,否则只会浪费您的服务器带宽。

在server通过linux tc,可以限制服务器发送带宽。
举例: 用linux tc限制server发送带宽为32mbit/s:

root@kcptun:~# cat tc.sh
tc qdisc del dev eth0 root
tc qdisc add dev eth0 root handle 1: htb
tc class add dev eth0 parent 1: classid 1:1 htb rate 32mbit
tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 10 fw flowid 1:1
iptables -t mangle -A POSTROUTING -o eth0 -j MARK –set-mark 10
root@kcptun:~#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
其中eth0为网卡,有些服务器为ens3,有些为p2p1,通过ifconfig查询修改。

### SNMP

```go
// Snmp defines network statistics indicator
type Snmp struct {
BytesSent uint64 // raw bytes sent
BytesReceived uint64
MaxConn uint64
ActiveOpens uint64
PassiveOpens uint64
CurrEstab uint64 // count of connections for now
InErrs uint64 // udp read errors
InCsumErrors uint64 // checksum errors from CRC32
KCPInErrors uint64 // packet iput errors from kcp
InSegs uint64
OutSegs uint64
InBytes uint64 // udp bytes received
OutBytes uint64 // udp bytes sent
RetransSegs uint64
FastRetransSegs uint64
EarlyRetransSegs uint64
LostSegs uint64 // number of segs infered as lost
RepeatSegs uint64 // number of segs duplicated
FECRecovered uint64 // correct packets recovered from FEC
FECErrs uint64 // incorrect packets recovered from FEC
FECSegs uint64 // FEC segments received
FECShortShards uint64 // number of data shards that's not enough for recovery
}

使用

-SIGUSR1 pid``` 可以在控制台打印出SNMP信息,通常用于精细调整**当前链路的有效载荷比**。
1
2
3
观察```RetransSegs,FastRetransSegs,LostSegs,OutSegs```这几者的数值比例,用于参考调整```-mode manual,fec```的参数。    

#### 带宽计算公式

在不丢包的情况下,有最大-rcvwnd 个数据包在网络上正在向你传输,以平均数据包大小avgsize计算,在任意时刻,有:

network_cap = rcvwnd*avgsize

数据流向你,这个值再除以ping值(rtt),等于最大带宽使用量。

max_bandwidth = network_cap/rtt = rcvwnd*avgsize/rtt

举例,设rcvwnd = 1024, avgsize = 1KB, rtt = 400ms,则:

max_bandwidth = 1024 * 1KB / 400ms = 2.5MB/s ~= 25Mbps

(注:以上计算不包括前向纠错的数据量)

前向纠错是最大带宽量的一个固定比例增加:

max_bandwidth_fec = max_bandwidth*(datashard+parityshard)/datashard

举例,设datashard = 10 , partiyshard = 3,则:

max_bandwidth_fec = max_bandwidth * (10 + 3) /10 = 1.3*max_bandwidth = 1.3 * 25Mbps = 32.5Mbps

`

故障排除

Q: 客户端和服务器端皆无 stream opened信息。
A: 连接客户端程序的端口设置错误。

Q: 客户端有 stream opened信息,服务器端没有。
A: 连接服务器的端口设置错误,或者被防火墙拦截。

Q: 客户端服务器皆有 stream opened信息,但无法通信。
A: 上层软件的设定错误。

免责申明

用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。

对免责声明的解释、修改及更新权均属于作者本人所有。

特别鸣谢

GITHUB上的各位大佬,就不打名字了

好人一生平安!