Compare commits

..
84 Commits
Author SHA1 Message Date
xtaci b3c4ec9483 update 2017-03-12 21:12:03 +08:00
xtaci 91335c3db6 listen to all 6060 2017-03-12 21:03:44 +08:00
xtaci 7df2c447db add -pprof option for debuging purpose 2017-03-12 20:53:53 +08:00
xtaci ad8683d46c use io.CopyBuffer instead of io.Copy for memory recycle 2017-03-11 13:20:28 +08:00
xtaci ca4800f298 remove hard limit for tcp socket buffer,let kernel autoscale 2017-03-10 12:53:14 +08:00
xtaci a25c9eb3a9 fix possible CLOSE_WAIT in client 2017-03-08 16:33:10 +08:00
xtaci a0e8b2592e use keepalive in smux alone directly 2017-03-07 20:33:52 +08:00
xtaci dc6b61ded3 remove a deprecated func 2017-03-07 10:49:06 +08:00
xtaci 298b4a0795 adjusta default parameters 2017-03-02 11:04:01 +08:00
xtaci 1334ce5a9a Revert "adjust defaults"
This reverts commit 6efb6008e5.
2017-03-02 00:08:34 +08:00
xtaci 6efb6008e5 adjust defaults 2017-03-01 16:27:33 +08:00
xtaci 53d4bbfe4c change default parameters 2017-03-01 11:06:20 +08:00
xtaciandGitHub 2f0783f81d Update README.md 2017-02-14 15:53:42 +08:00
xtaciandGitHub 64e141430e Update README.md 2017-02-14 13:00:03 +08:00
xtaci bf543cfd63 add scavengettl option 2017-02-12 12:26:34 +08:00
xtaciandGitHub 751fb5f8cd Update README-CN.md 2017-02-08 11:36:06 +08:00
xtaci e074076db5 do not quit when NIC is disabled 2017-02-06 16:45:03 +08:00
higher-syandxtaci 417c602cc5 Update build-release.sh again
是我想多了,请多多指教 😄
2017-02-04 14:04:39 +08:00
higher-syandxtaci 23e3782dc4 Update build-release.sh
Use sha1sum. But on sometimes it's shasum (e.g. macos),so......
2017-02-04 14:04:39 +08:00
higher-syandxtaci 532d9e56ce Update build-release.sh
change md5 to sha256
2017-02-04 14:04:39 +08:00
xtaciandGitHub 203a626528 Update README-CN.md 2017-01-26 22:00:11 +08:00
xtaciandGitHub 338bc5befc Update README-CN.md 2017-01-26 12:03:30 +08:00
xtaciandGitHub 4051938931 Update README.md 2017-01-24 21:54:56 +08:00
xtaciandGitHub c2489808cc Update README.md 2017-01-24 21:54:20 +08:00
xtaciandGitHub a9ea222f24 Update README-CN.md 2017-01-24 21:51:46 +08:00
xtaciandGitHub aa85dc9ada Update README-CN.md 2017-01-24 21:51:19 +08:00
xtaciandGitHub ec5cb958b7 Update README-CN.md 2017-01-24 21:50:43 +08:00
xtaciandGitHub 490c40eb44 Update README-CN.md 2017-01-24 21:47:39 +08:00
xtaciandGitHub 76e7818b45 Update README-CN.md 2017-01-24 21:46:31 +08:00
xtaciandGitHub f2363702bc Update README-CN.md 2017-01-24 21:45:25 +08:00
xtaciandGitHub beddab04fe Update README.md 2017-01-24 17:53:34 +08:00
xtaciandGitHub 12a39bebb8 Update README.md 2017-01-24 17:35:48 +08:00
xtaciandGitHub c0d6104594 Update README.md 2017-01-24 17:32:44 +08:00
xtaciandGitHub 2fe88a1c46 Update README.md 2017-01-24 17:32:10 +08:00
xtaciandGitHub 22c2314eee Update README.md 2017-01-24 17:28:53 +08:00
xtaciandGitHub e7ee96cb1c Update README.md 2017-01-24 17:24:36 +08:00
xtaciandGitHub a5f5d64fd9 Update README.md 2017-01-24 17:14:24 +08:00
xtaciandGitHub ac0d3aa954 Update README.md 2017-01-24 17:10:20 +08:00
xtaciandGitHub 16c8c2daa5 Update README.md 2017-01-24 17:07:12 +08:00
xtaciandGitHub 69b1ee3e30 Update README-CN.md 2017-01-24 14:18:34 +08:00
xtaciandGitHub 87ee41833b Update README-CN.md 2017-01-24 14:16:14 +08:00
xtaciandGitHub d3d2cd271d Update README-CN.md 2017-01-24 14:11:48 +08:00
xtaciandGitHub 1a2c3559a2 Update README-CN.md 2017-01-24 14:03:59 +08:00
xtaciandGitHub 844dc17ab7 Update README.md 2017-01-24 13:58:10 +08:00
xtaciandGitHub 7eee949682 Update README-CN.md 2017-01-24 13:57:09 +08:00
xtaci 06b63b6fdd upd 2017-01-24 13:55:47 +08:00
xtaciandGitHub 5f950feed8 Update README.md 2017-01-24 13:09:29 +08:00
xtaciandGitHub 8ee3fce2ff Add files via upload 2017-01-24 13:08:52 +08:00
xtaciandGitHub 190f82f064 Update README.md 2017-01-24 12:59:43 +08:00
xtaciandGitHub 534708138c Add files via upload 2017-01-24 12:58:43 +08:00
xtaciandGitHub e58e3e66d0 Add files via upload 2017-01-24 12:58:16 +08:00
xtaciandGitHub 3bee8ca6e9 Update README.md 2017-01-24 12:33:48 +08:00
xtaci cfabba001d update -mode usage 2017-01-24 12:29:19 +08:00
xtaciandGitHub d4b07015ab Update README.md 2017-01-24 12:23:15 +08:00
xtaciandGitHub f70beed964 Update README.md 2017-01-24 12:21:03 +08:00
xtaciandGitHub 0ba3cfb0a2 Update README.md 2017-01-24 11:36:55 +08:00
xtaciandGitHub ee8e1be581 Update README.md 2017-01-24 11:33:30 +08:00
xtaciandGitHub ae2e3be73d Update README.md 2017-01-24 11:25:22 +08:00
xtaciandGitHub 356bb7615e Update README.md 2017-01-24 11:23:48 +08:00
xtaciandGitHub b870c588ae Update README.md 2017-01-24 11:20:47 +08:00
xtaciandGitHub f31a5e6b61 Update README.md 2017-01-24 11:18:43 +08:00
xtaciandGitHub 3972a6faa1 Update README.md 2017-01-24 11:17:42 +08:00
xtaciandGitHub 20211352e7 Update README.md 2017-01-24 11:16:56 +08:00
xtaciandGitHub 608c598cbf Update README.md 2017-01-24 11:09:09 +08:00
xtaciandGitHub e26d19da09 Update README.md 2017-01-24 11:08:32 +08:00
xtaciandGitHub 9f63ea2362 Update README.md 2017-01-24 11:04:23 +08:00
xtaciandGitHub 81c9f91f44 Update README.md 2017-01-24 11:03:40 +08:00
xtaciandGitHub 8d8ee15381 Update README.md 2017-01-24 10:59:09 +08:00
xtaciandGitHub dc730d8501 Update README.md 2017-01-24 10:58:08 +08:00
xtaciandGitHub 8ae01cd92f Update README.md 2017-01-24 10:57:52 +08:00
xtaciandGitHub 3000a611cc Update README.md 2017-01-24 10:56:35 +08:00
xtaciandGitHub f09f1c9b2d Update README.md 2017-01-24 10:54:38 +08:00
xtaciandGitHub cc12ada5a7 Update README.md 2017-01-24 10:53:00 +08:00
xtaciandGitHub 074f4d54d5 Update README.md 2017-01-24 10:50:01 +08:00
xtaciandGitHub c4253493b4 Update README.md 2017-01-24 10:49:23 +08:00
xtaciandGitHub 7b4c8967d6 Update README.md 2017-01-24 10:45:12 +08:00
xtaciandGitHub a1befb4763 Update README.md 2017-01-24 10:33:00 +08:00
xtaciandGitHub 9b31b71cb5 Update README.md 2017-01-24 00:37:07 +08:00
xtaciandGitHub 0ffe2cd369 Update README.md 2017-01-24 00:30:26 +08:00
xtaciandGitHub 42535dff05 Update README.md 2017-01-23 23:22:09 +08:00
xtaciandGitHub 52a3e0f7ff Update README.md 2017-01-23 23:18:12 +08:00
xtaciandGitHub b643d63bad Update README.md 2017-01-23 23:13:28 +08:00
xtaciandGitHub b7f0cb8408 Update README.md 2017-01-23 23:04:42 +08:00
xtaci dc4d42007b init 2017-01-23 22:30:54 +08:00
17 changed files with 1344 additions and 269 deletions
+27
View File
@@ -0,0 +1,27 @@
问问题前先搜索ISSUE,并搞清楚下面的问题:
1. 检查 ```-key xxx``` 至少三遍, ***保证***两边一致。
2. 保证```-nocomp, -datashard, -parityshard, -key, -crypt```两边一致。
3. 是否在服务器端,正确设定了转发的目标服务器地址 ***--target***。
4. 如果第3条不确定,尝试在服务器上telnet target port试试。
5. 防火墙是否关闭了UDP通信。
6. 两端的版本是否一致?
7. 是不是最新版本?
8. 两端分别是什么操作系统?
9. 两端的输出日志是什么?
Before firing issue, make sure you figured out the following common questions.
PLEASE DO SEARCH FIRST.
1. Check your ```-key xxx``` for at least 3 times, ***MAKE SURE*** both sides share the same secret.
2. ```-nocomp, -datashard, -parityshard, -key, -crypt``` ***must be the same*** on both side.
3. Did you correctly set the ***-target*** on the server side?
4. ***MAKE SURE*** ```telnet target port``` on your server successful(don't ask me why couldn't).
5. Does your ***firewall allows UDP*** communications? (including your ISP Cable-Modem)
6. Are you using the **same version** for both client & server
7. Are you using the **latest release**?
8. Which **OS** do you use?
9. Which end for this issue related to, **client or server**?
+5 -3
View File
@@ -1,6 +1,8 @@
FROM golang:latest
FROM golang:alpine
MAINTAINER xtaci <daniel820313@gmail.com>
RUN go get github.com/xtaci/kcptun/client
RUN go get github.com/xtaci/kcptun/server
RUN apk update && \
apk upgrade && \
apk add git
RUN go get github.com/xtaci/kcptun/client && go get github.com/xtaci/kcptun/server
EXPOSE 29900/udp
EXPOSE 12948
+362
View File
@@ -0,0 +1,362 @@
# <img src="logo.png" alt="kcptun" height="54px" />
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
[1]: https://images.microbadger.com/badges/image/xtaci/kcptun.svg
[2]: https://microbadger.com/images/xtaci/kcptun
[3]: https://travis-ci.org/xtaci/kcptun.svg?branch=master
[4]: https://travis-ci.org/xtaci/kcptun
[5]: https://goreportcard.com/badge/github.com/xtaci/kcptun
[6]: https://goreportcard.com/report/github.com/xtaci/kcptun
[7]: https://img.shields.io/badge/license-MIT-blue.svg
[8]: https://raw.githubusercontent.com/xtaci/kcptun/master/LICENSE.md
[11]: https://img.shields.io/badge/license-MIT-blue.svg
[12]: LICENSE.md
[13]: https://img.shields.io/github/release/xtaci/kcptun.svg
[14]: https://github.com/xtaci/kcptun/releases/latest
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800
[16]: https://github.com/xtaci/kcptun/releases
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
[18]: https://github.com/skywind3000/kcp
<img src="kcptun.png" alt="kcptun" height="300px"/>
-
### 快速设定
客户端、服务器分别**下载**对应平台的[预编译版本](https://github.com/xtaci/kcptun/releases),并**解压**,通过下面的命令**启动**端口转发。
```
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)
### 从源码安装
```
$go get -u github.com/xtaci/kcptun/client
$go get -u github.com/xtaci/kcptun/server
```
注意: 如果出现错误提示,请确保依赖库能正确访问到。
Release中的所有二进制版本,是通过 `build-release.sh` 脚本生成并优化。
### 速度对比
<img src="fast.png" alt="fast.com" height="256px" />
* 测速网站: https://fast.com
* 接入速度: 100Mbps
* WIFI: 5GHz TL-WDR3320
### 使用方法
在Mac OS X El Capitan下的帮助输出,注意默认值:
```
$ ./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
```
#### 分层参数图
<p align="left"><img src="layeredparams.png" alt="params" height="450px"/></p>
**两端参数必须一致的有**:
* datashard --前向纠错
* parityshard --前向纠错
* nocomp --压缩
* key --密钥
* crypt --加密算法
其余为两边可独立设定的参数
### 内置模式
响应速度:
*fast3 > fast2 >* **[fast]** *> normal > default*
有效载荷比:
*default > normal >* **[fast]** *> fast2 > fast3*
中间-mode参数比较均衡,总之就是越快,包重传越激进。
更高级的 **手动档** 需要理解KCP协议,并通过 **隐藏参数** 调整,例如:
```
-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](rs.png)
通过参数```-datashard n -parityshard m``` 在两端同时设定。
数据包发送顺序严格遵循: 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
```
### 内存控制
路由器,手机等嵌入式设备通常对**内存用量敏感**,通过调节环境变量GOGC(例如GOGC=20)后启动client,可以降低内存使用。
参考:https://blog.golang.org/go15gc
### DSCP
DSCP差分服务代码点(Differentiated Services Code Point),IETF于1998年12月发布了Diff-ServDifferentiated 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:~#
```
其中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
}
```
使用```kill -SIGUSR1 pid``` 可以在控制台打印出SNMP信息,通常用于精细调整**当前链路的有效载荷比**。
观察```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: 上层软件的设定错误。
### 免责申明
**用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。**
**对免责声明的解释、修改及更新权均属于作者本人所有。**
### 特别鸣谢
> 郑H立, 南东风, Li, 七七, 凌君, 昶,LesMiserables, KyOn, 噼里啪啦, 继斌, 小苍辛苦, **Ken**,
> 乔槁, 佳晨, 猪肉佬, lcx, 昊文, 冰峰, 凡, alex, **海豹叔叔**, 奥姐, 张冰, 司成,
> 武子, **慎**Alex43211**Coxxs**,荣,NeroNg,吴骁,定一,我不是林JPatrick, 超, 陈,windfarer, 宇,
> 今晶,斌,晓东,最后一缕阳光,亮,Ethan,一心不乱,allenm,冬卯,GELATO,用户1Butterfly,光子曲面,
> 丞佳,捉鱼,TalonBiny,李勇,***阿彪******rinex20***Fabre,路过发光体,池子,kk,杰,维维
好人一生平安!
### 相关软件
1. https://github.com/bettermanbao/openwrt-kcptun
2. https://github.com/EasyPi/openwrt-kcptun
3. https://github.com/kuoruan/luci-app-kcptun
4. https://github.com/dfdragon/kcptun_gclient
5. https://github.com/dfdragon/kcptun_xclient
### 参考资料
1. https://github.com/skywind3000/kcp -- KCP - A Fast and Reliable ARQ Protocol.
2. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go.
3. https://en.wikipedia.org/wiki/Differentiated_services -- DSCP.
4. http://google.github.io/snappy/ -- A fast compressor/decompressor.
5. https://www.backblaze.com/blog/reed-solomon/ -- Reed-Solomon Explained.
6. http://www.qualcomm.cn/products/raptorq -- RaptorQ Forward Error Correction Scheme for Object Delivery.
7. https://en.wikipedia.org/wiki/PBKDF2 -- Key stretching.
8. http://blog.appcanary.com/2016/encrypt-or-compress.html -- Should you encrypt or compress first?
9. https://github.com/hashicorp/yamux -- Connection multiplexing library.
10. https://tools.ietf.org/html/rfc6937 -- Proportional Rate Reduction for TCP.
11. https://tools.ietf.org/html/rfc5827 -- Early Retransmit for TCP and Stream Control Transmission Protocol (SCTP).
12. http://http2.github.io/ -- What is HTTP/2?
13. http://www.lartc.org/LARTC-zh_CN.GB2312.pdf -- Linux Advanced Routing & Traffic Control
14. https://en.wikipedia.org/wiki/Noisy-channel_coding_theorem -- Noisy channel coding theorem
+258 -82
View File
@@ -1,111 +1,287 @@
# <img src="logo.png" alt="kcptun" height="60px" />
[![GoDoc][1]][2] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Coverage Statusd][7]][8]
[1]: https://godoc.org/github.com/xtaci/kcptun?status.svg
[2]: https://godoc.org/github.com/xtaci/kcptun
# <img src="logo.png" alt="kcptun" height="54px" />
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
[1]: https://images.microbadger.com/badges/image/xtaci/kcptun.svg
[2]: https://microbadger.com/images/xtaci/kcptun
[3]: https://travis-ci.org/xtaci/kcptun.svg?branch=master
[4]: https://travis-ci.org/xtaci/kcptun
[5]: https://goreportcard.com/badge/xtaci/kcptun
[6]: https://goreportcard.com/report/xtaci/kcptun
[7]: https://coveralls.io/repos/github/xtaci/kcptun/badge.svg?branch=master
[8]: https://coveralls.io/github/xtaci/kcptun?branch=master
[5]: https://goreportcard.com/badge/github.com/xtaci/kcptun
[6]: https://goreportcard.com/report/github.com/xtaci/kcptun
[7]: https://img.shields.io/badge/license-MIT-blue.svg
[8]: https://raw.githubusercontent.com/xtaci/kcptun/master/LICENSE.md
[11]: https://img.shields.io/badge/license-MIT-blue.svg
[12]: LICENSE.md
[13]: https://img.shields.io/github/release/xtaci/kcptun.svg
[14]: https://github.com/xtaci/kcptun/releases/latest
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800
[16]: https://github.com/xtaci/kcptun/releases
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
[18]: https://github.com/skywind3000/kcp
TCP流转换为KCP+UDP流,:zap:***[下载地址](https://github.com/xtaci/kcptun/releases/latest)***:zap:工作示意图:
![kcptun](kcptun.png)
<img src="kcptun.png" alt="kcptun" height="300px"/>
***kcptun是[kcp](https://github.com/xtaci/kcp-go)协议的一个简单应用,可以用于任意tcp网络程序的传输承载,以提高在丢包环境下的网络流畅度。***
_采用极简设计,客户端+服务器源码总共400行,方便用户自己扩展_
[简体中文](README-CN.md)
-
### QuickStart
Download precompiled [Releases](https://github.com/xtaci/kcptun/releases).
### *快速设定* :lollipop:
```
服务器: ./server_linux_amd64 -t "127.0.0.1:1080" -l ":554" -mode fast2 // 转发到本地1080端口
客户端: ./client_darwin_amd64 -r "IP地址:554" -l ":1080" -mode fast2 // 监听本地1080端口
KCP Client: ./client_darwin_amd64 -r "KCP_SERVER_IP:4000" -l ":8388" -mode fast2
KCP Server: ./server_linux_amd64 -t "TARGET_IP:8388" -l ":4000" -mode fast2
```
The above commands will establish port forwarding for 8388/tcp as:
Application -> KCP Client(8388/tcp) -> KCP Server(4000/udp) -> Server(8388/tcp)
### Install from source
```
$go get -u github.com/xtaci/kcptun/client
$go get -u github.com/xtaci/kcptun/server
```
### *使用の方法* :lollipop:
![client](client.png)
![server](server.png)
All precompiled releases are genereated from `build-release.sh` script.
### *适用范围限定* :lollipop:
1. 实时网络游戏的数据传输
2. 跨运营商的流量传输
3. 其他高丢包通信链路的TCP承载
### Performance
<img src="fast.png" alt="fast.com" height="256px" />
### Basic Tuning Guide
#### Improving Thoughput
> **Q: I have a high speed network link, how to reach the maximum bandwidth?**
> **A:** Increase `-rcvwnd` on KCP Client and `-sndwnd` on KCP Server **simultaneously & gradually**, the mininum one decides the maximum transfer rate of the link, as `wnd * mtu / rtt`; Then try downloading something and to see if it meets your requirements.
#### Improving Latency
> **Q: I'm using kcptun for game, I don't want any lag happening.**
> **A:** Lag means packet loss for most of the time, lags can be improved by changing `-mode`.
> eg: `-mode fast3`
> Aggresiveness/Responsiveness on retransmission for embeded modes are:
> *fast3 > fast2 > fast > normal > default*
-
### Expert Tuning Guide
#### Overview
<p align="left"><img src="layeredparams.png" alt="params" height="450px"/></p>
#### Usage
### *推荐参数* :lollipop:
```
适用大部分ADSL接入(非对称上下行)的参数(实验环境电信100M ADSL)
SERVER: -mtu 1400 -sndwnd 2048 -rcvwnd 2048 -mode fast2
CLIENT: -mtu 1400 -sndwnd 256 -rcvwnd 2048 -mode fast2 -dscp 46
其它带宽请按比例降低窗口,比如50M ADSL,把CLIENT的 -sndwnd -rcvwnd减掉一半,SERVER不变
```
*巭孬嫑乱动*
$ ./client_darwin_amd64 -h
NAME:
kcptun - client(with SMUX)
### *DSCP* :lollipop:
DSCP差分服务代码点(Differentiated Services Code Point),IETF于1998年12月发布了Diff-ServDifferentiated Service)的QoS分类标准。它在每个数据包IP头部的服务类别TOS标识字节中,利用已使用的6比特和未使用的2比特,通过编码值来区分优先级。
常用DSCP值可以参考[Wikipedia DSCP](https://en.wikipedia.org/wiki/Differentiated_services#Commonly_used_DSCP_values),至于有没有用,完全取决于数据包经过的设备。
USAGE:
client_darwin_amd64 [global options] command [command options] [arguments...]
### *内置模式* :lollipop:
响应速度:
*fast3 >* ***[fast2]*** *> fast > normal > default*
有效载荷比:
*default > normal > fast >* ***[fast2]*** *> fast3*
中间mode参数比较均衡,总之就是越快越浪费带宽,推荐模式 ***fast2***
更高级的 ***手动档*** 需要理解KCP协议,并通过 ***隐藏参数*** 调整,例如:
```
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20
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
```
### *前向纠错* :lollipop:
前向纠错采用Reed Solomon纠删码, 它的基本原理如下: 给定n个数据块d1, d2,…, dn,n和一个正整数m, RS根据n个数据块生成m个校验块, c1, c2,…, cm。 对于任意的n和m, 从n个原始数据块和m 个校验块中任取n块就能解码出原始数据, 即RS最多容忍m个数据块或者校验块同时丢失。
#### Forward Error Correction
In coding theory, the ReedSolomon code belongs to the class of non-binary cyclic error-correcting codes. The ReedSolomon code is based on univariate polynomials over finite fields.
It is able to detect and correct multiple symbol errors. By adding t check symbols to the data, a ReedSolomon code can detect any combination of up to t erroneous symbols, or correct up to ⌊t/2⌋ symbols. As an erasure code, it can correct up to t known erasures, or it can detect and correct combinations of errors and erasures. Furthermore, ReedSolomon codes are suitable as multiple-burst bit-error correcting codes, since a sequence of b + 1 consecutive bit errors can affect at most two symbols of size b. The choice of t is up to the designer of the code, and may be selected within wide limits.
![reed-solomon](rs.png)
通过```-datashard 10 -parityshard 3``` 可以调整Reed Solomon参数。
Setting parameters of RS-Code with ```-datashard m -parityshard n``` on both KCP Client & KCP Server.
#### DSCP
Differentiated services or DiffServ is a computer networking architecture that specifies a simple, scalable and coarse-grained mechanism for classifying and managing network traffic and providing quality of service (QoS) on modern IP networks. DiffServ can, for example, be used to provide low-latency to critical network traffic such as voice or streaming media while providing simple best-effort service to non-critical services such as web traffic or file transfers.
DiffServ uses a 6-bit differentiated services code point (DSCP) in the 8-bit differentiated services field (DS field) in the IP header for packet classification purposes. The DS field and ECN field replace the outdated IPv4 TOS field.
setting each side with ```-dscp value```, Here are some [Commonly used DSCP values](https://en.wikipedia.org/wiki/Differentiated_services#Commonly_used_DSCP_values).
#### Security
No matter what encryption you are using for application layer, if you specify ```-crypt none``` to kcptun,
the header will be ***PLAINTEXT*** to everyone; I suggest ```-crypt aes-128``` for encryption at least .
`-crypt` and `-key` must be the same on both KCP Client & KCP Server.
NOTICE: ```-crypt xor``` is also insecure, do not use this unless you know what you are doing.
Benchmarks for crypto algorithms supported by kcptun:
```
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
```
#### Memory Control
Routers, mobile devices are sensitive to memory consumption; by setting GOGC environment(eg: GOGC=20) will lower memory consumption.
Reference: https://blog.golang.org/go15gc
#### Compression
kcptun has builtin snappy algorithms for compressing streams:
> 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/
Compression may save bandwidth for **PLAINTEXT** data, such as HTTP data.
Compression is enabled by default, you can disable it by setting ```-nocomp``` on both KCP Client & KCP Server.
#### SNMP
### *SNMP* :lollipop:
```go
// Snmp defines network statistics indicator
type Snmp struct {
BytesSent uint64 // payload bytes sent
BytesReceived uint64
MaxConn uint64
ActiveOpens uint64
PassiveOpens uint64
CurrEstab uint64
InErrs uint64
InCsumErrors uint64 // checksum errors
InSegs uint64
OutSegs uint64
OutBytes uint64 // udp bytes sent
RetransSegs uint64
FastRetransSegs uint64
EarlyRetransSegs uint64
LostSegs uint64
RepeatSegs uint64
FECRecovered uint64
FECErrs uint64
FECSegs uint64 // fec segments received
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
}
```
使用```kill -SIGUSR1 pid``` 可以在控制台打印出SNMP信息,通常用于精细调整***当前链路的有效载荷比***。
观察```RetransSegs,FastRetransSegs,LostSegs,OutSegs```这几者的数值比例,用于参考调整```-mode manual,fec```的参数。
Sending a `SIGUSR1` signal to KCP Client or KCP Server will dump SNMP information to console, just like `/proc/net/snmp`. You can use this information to do fine-grained tuning.
### *性能对比* :lollipop:
```
root@vultr:~# iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 4.00 MByte (default)
------------------------------------------------------------
[ 4] local 172.7.7.1 port 5001 connected with 172.7.7.2 port 55453
[ ID] Interval Transfer Bandwidth
[ 4] 0.0-18.0 sec 5.50 MBytes 2.56 Mbits/sec <-- connection via kcptun
[ 5] local 45.32.xxx.xxx port 5001 connected with 218.88.xxx.xxx port 17220
[ 5] 0.0-17.9 sec 2.12 MBytes 997 Kbits/sec <-- direct connnection via tcp
```
### Manual Control
### *免责申明* :warning:
用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。
https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
对免责声明的解释、修改及更新权均属于作者本人所有。
`-mode manual -nodelay 1 -interval 20 -resend 2 -nc 1`
Low-level KCP configuration can be altered by using manual mode like above, make sure you really **UNDERSTAND** what these means before doing **ANY** manual settings.
### Support
You can support this project by the following methods:
1. Vultr promotion code:
http://www.vultr.com/?ref=6897065
2. Paypal
https://www.paypal.me/xtaci
Your name or github name will be listed on this page by default.
### References
1. https://github.com/skywind3000/kcp -- KCP - A Fast and Reliable ARQ Protocol.
2. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go.
3. https://en.wikipedia.org/wiki/Differentiated_services -- DSCP.
4. http://google.github.io/snappy/ -- A fast compressor/decompressor.
5. https://www.backblaze.com/blog/reed-solomon/ -- Reed-Solomon Explained.
6. http://www.qualcomm.cn/products/raptorq -- RaptorQ Forward Error Correction Scheme for Object Delivery.
7. https://en.wikipedia.org/wiki/PBKDF2 -- Key stretching.
8. http://blog.appcanary.com/2016/encrypt-or-compress.html -- Should you encrypt or compress first?
9. https://github.com/hashicorp/yamux -- Connection multiplexing library.
10. https://tools.ietf.org/html/rfc6937 -- Proportional Rate Reduction for TCP.
11. https://tools.ietf.org/html/rfc5827 -- Early Retransmit for TCP and Stream Control Transmission Protocol (SCTP).
12. http://http2.github.io/ -- What is HTTP/2?
13. http://www.lartc.org/ -- Linux Advanced Routing & Traffic Control
14. https://en.wikipedia.org/wiki/Noisy-channel_coding_theorem -- Noisy channel coding theorem
+28 -10
View File
@@ -1,8 +1,13 @@
#!/bin/bash
MD5='md5sum'
unamestr=`uname`
if [[ "$unamestr" == 'Darwin' ]]; then
MD5='md5'
sum="sha1sum"
if ! hash sha1sum 2>/dev/null; then
if ! hash shasum 2>/dev/null; then
echo "I can't see 'sha1sum' or 'shasum'"
echo "Please install one of them!"
exit
fi
sum="shasum"
fi
UPX=false
@@ -12,6 +17,7 @@ fi
VERSION=`date -u +%Y%m%d`
LDFLAGS="-X main.VERSION=$VERSION -s -w"
GCFLAGS=""
OSES=(linux darwin windows freebsd)
ARCHS=(amd64 386)
@@ -22,20 +28,32 @@ for os in ${OSES[@]}; do
then
suffix=".exe"
fi
env GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
env GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o server_${os}_${arch}${suffix} github.com/xtaci/kcptun/server
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_${arch}${suffix} github.com/xtaci/kcptun/server
if $UPX; then upx -9 client_${os}_${arch}${suffix} server_${os}_${arch}${suffix};fi
tar -zcf kcptun-${os}-${arch}-$VERSION.tar.gz client_${os}_${arch}${suffix} server_${os}_${arch}${suffix}
$MD5 kcptun-${os}-${arch}-$VERSION.tar.gz
$sum kcptun-${os}-${arch}-$VERSION.tar.gz
done
done
# ARM
ARMS=(5 6 7)
for v in ${ARMS[@]}; do
env GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
env GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
done
if $UPX; then upx -9 client_linux_arm* server_linux_arm*;fi
tar -zcf kcptun-linux-arm-$VERSION.tar.gz client_linux_arm* server_linux_arm*
$MD5 kcptun-linux-arm-$VERSION.tar.gz
$sum kcptun-linux-arm-$VERSION.tar.gz
#MIPS32LE
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mips github.com/xtaci/kcptun/server
if $UPX; then upx -9 client_linux_mips* server_linux_mips*;fi
tar -zcf kcptun-linux-mipsle-$VERSION.tar.gz client_linux_mipsle server_linux_mipsle
tar -zcf kcptun-linux-mips-$VERSION.tar.gz client_linux_mips server_linux_mips
$sum kcptun-linux-mipsle-$VERSION.tar.gz
$sum kcptun-linux-mips-$VERSION.tar.gz
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

+46
View File
@@ -0,0 +1,46 @@
package main
import (
"encoding/json"
"os"
)
// Config for client
type Config struct {
LocalAddr string `json:"localaddr"`
RemoteAddr string `json:"remoteaddr"`
Key string `json:"key"`
Crypt string `json:"crypt"`
Mode string `json:"mode"`
Conn int `json:"conn"`
AutoExpire int `json:"autoexpire"`
ScavengeTTL int `json:"scavengettl"`
MTU int `json:"mtu"`
SndWnd int `json:"sndwnd"`
RcvWnd int `json:"rcvwnd"`
DataShard int `json:"datashard"`
ParityShard int `json:"parityshard"`
DSCP int `json:"dscp"`
NoComp bool `json:"nocomp"`
AckNodelay bool `json:"acknodelay"`
NoDelay int `json:"nodelay"`
Interval int `json:"interval"`
Resend int `json:"resend"`
NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"`
KeepAlive int `json:"keepalive"`
Log string `json:"log"`
SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"`
Pprof bool `json:"pprof"`
}
func parseJSONConfig(config *Config, path string) error {
file, err := os.Open(path) // For read access.
if err != nil {
return err
}
defer file.Close()
return json.NewDecoder(file).Decode(config)
}
+332 -96
View File
@@ -2,26 +2,39 @@ package main
import (
"crypto/sha1"
"encoding/csv"
"fmt"
"io"
"log"
"math/rand"
"net"
"net/http"
_ "net/http/pprof"
"os"
"sync"
"time"
"golang.org/x/crypto/pbkdf2"
"github.com/golang/snappy"
"github.com/hashicorp/yamux"
"github.com/pkg/errors"
"github.com/urfave/cli"
"github.com/xtaci/kcp-go"
kcp "github.com/xtaci/kcp-go"
"github.com/xtaci/smux"
)
var (
// VERSION is injected by buildflags
VERSION = "SELFBUILD"
SALT = "kcp-go"
// SALT is use for pbkdf2 key expansion
SALT = "kcp-go"
)
// global recycle buffer
var copyBuf sync.Pool
const bufSize = 4096
type compStream struct {
conn net.Conn
w *snappy.Writer
@@ -50,23 +63,31 @@ func newCompStream(conn net.Conn) *compStream {
return c
}
func handleClient(p1, p2 io.ReadWriteCloser) {
func handleClient(sess *smux.Session, p1 io.ReadWriteCloser) {
log.Println("stream opened")
defer log.Println("stream closed")
defer p1.Close()
p2, err := sess.OpenStream()
if err != nil {
return
}
defer p2.Close()
// start tunnel
p1die := make(chan struct{})
go func() {
io.Copy(p1, p2)
buf := copyBuf.Get().([]byte)
io.CopyBuffer(p1, p2, buf)
close(p1die)
copyBuf.Put(buf)
}()
p2die := make(chan struct{})
go func() {
io.Copy(p2, p1)
buf := copyBuf.Get().([]byte)
io.CopyBuffer(p2, p1, buf)
close(p2die)
copyBuf.Put(buf)
}()
// wait for tunnel termination
@@ -78,16 +99,23 @@ func handleClient(p1, p2 io.ReadWriteCloser) {
func checkError(err error) {
if err != nil {
log.Println(err)
log.Printf("%+v\n", err)
os.Exit(-1)
}
}
func main() {
rand.Seed(int64(time.Now().Nanosecond()))
copyBuf.New = func() interface{} {
return make([]byte, bufSize)
}
if VERSION == "SELFBUILD" {
// add more log flags for debugging
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
myApp := cli.NewApp()
myApp.Name = "kcptun"
myApp.Usage = "kcptun client"
myApp.Usage = "client(with SMUX)"
myApp.Version = VERSION
myApp.Flags = []cli.Flag{
cli.StringFlag{
@@ -103,28 +131,38 @@ func main() {
cli.StringFlag{
Name: "key",
Value: "it's a secrect",
Usage: "key for communcation, must be the same as kcptun server",
Usage: "pre-shared secret between client and server",
EnvVar: "KCPTUN_KEY",
},
cli.StringFlag{
Name: "crypt",
Value: "aes",
Usage: "methods for encryption: aes, tea, xor, none",
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none",
},
cli.StringFlag{
Name: "mode",
Value: "fast",
Usage: "mode for communication: fast3, fast2, fast, normal",
Usage: "profiles: fast3, fast2, fast, normal, manual",
},
cli.IntFlag{
Name: "conn",
Value: 1,
Usage: "establish N physical connections as specified by 'conn' to server",
Usage: "set num of UDP connections to server",
},
cli.IntFlag{
Name: "autoexpire",
Value: 0,
Usage: "set auto expiration time(in seconds) for a single UDP connection, 0 to disable",
},
cli.IntFlag{
Name: "scavengettl",
Value: 600,
Usage: "set how long an expired connection can live(in sec), -1 to disable",
},
cli.IntFlag{
Name: "mtu",
Value: 1350,
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
Usage: "set maximum transmission unit for UDP packets",
},
cli.IntFlag{
Name: "sndwnd",
@@ -133,32 +171,33 @@ func main() {
},
cli.IntFlag{
Name: "rcvwnd",
Value: 1024,
Value: 512,
Usage: "set receive window size(num of packets)",
},
cli.BoolFlag{
Name: "nocomp",
Usage: "disable compression",
},
cli.IntFlag{
Name: "datashard",
Name: "datashard,ds",
Value: 10,
Usage: "set reed-solomon erasure coding - datashard",
},
cli.IntFlag{
Name: "parityshard",
Name: "parityshard,ps",
Value: 3,
Usage: "set reed-solomon erasure coding - parityshard",
},
cli.BoolFlag{
Name: "acknodelay",
Usage: "flush ack immediately when a packet is received",
},
cli.IntFlag{
Name: "dscp",
Value: 0,
Usage: "set DSCP(6bit)",
},
cli.BoolFlag{
Name: "nocomp",
Usage: "disable compression",
},
cli.BoolFlag{
Name: "acknodelay",
Usage: "flush ack immediately when a packet is received",
Hidden: true,
},
cli.IntFlag{
Name: "nodelay",
Value: 0,
@@ -166,7 +205,7 @@ func main() {
},
cli.IntFlag{
Name: "interval",
Value: 40,
Value: 50,
Hidden: true,
},
cli.IntFlag{
@@ -179,102 +218,299 @@ func main() {
Value: 0,
Hidden: true,
},
cli.IntFlag{
Name: "sockbuf",
Value: 4194304, // socket buffer size in bytes
Hidden: true,
},
cli.IntFlag{
Name: "keepalive",
Value: 10, // nat keepalive interval in seconds
Hidden: true,
},
cli.StringFlag{
Name: "snmplog",
Value: "",
Usage: "collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log",
},
cli.IntFlag{
Name: "snmpperiod",
Value: 60,
Usage: "snmp collect period, in seconds",
},
cli.BoolFlag{
Name: "pprof",
Usage: "start profiling server on :6060",
},
cli.StringFlag{
Name: "log",
Value: "",
Usage: "specify a log file to output, default goes to stderr",
},
cli.StringFlag{
Name: "c",
Value: "", // when the value is not empty, the config path must exists
Usage: "config from json file, which will override the command from shell",
},
}
myApp.Action = func(c *cli.Context) {
myApp.Action = func(c *cli.Context) error {
config := Config{}
config.LocalAddr = c.String("localaddr")
config.RemoteAddr = c.String("remoteaddr")
config.Key = c.String("key")
config.Crypt = c.String("crypt")
config.Mode = c.String("mode")
config.Conn = c.Int("conn")
config.AutoExpire = c.Int("autoexpire")
config.ScavengeTTL = c.Int("scavengettl")
config.MTU = c.Int("mtu")
config.SndWnd = c.Int("sndwnd")
config.RcvWnd = c.Int("rcvwnd")
config.DataShard = c.Int("datashard")
config.ParityShard = c.Int("parityshard")
config.DSCP = c.Int("dscp")
config.NoComp = c.Bool("nocomp")
config.AckNodelay = c.Bool("acknodelay")
config.NoDelay = c.Int("nodelay")
config.Interval = c.Int("interval")
config.Resend = c.Int("resend")
config.NoCongestion = c.Int("nc")
config.SockBuf = c.Int("sockbuf")
config.KeepAlive = c.Int("keepalive")
config.Log = c.String("log")
config.SnmpLog = c.String("snmplog")
config.SnmpPeriod = c.Int("snmpperiod")
config.Pprof = c.Bool("pprof")
if c.String("c") != "" {
err := parseJSONConfig(&config, c.String("c"))
checkError(err)
}
// log redirect
if config.Log != "" {
f, err := os.OpenFile(config.Log, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
checkError(err)
defer f.Close()
log.SetOutput(f)
}
switch config.Mode {
case "normal":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 50, 2, 1
case "fast":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 40, 2, 1
case "fast2":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 30, 2, 1
case "fast3":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1
}
log.Println("version:", VERSION)
addr, err := net.ResolveTCPAddr("tcp", c.String("localaddr"))
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
checkError(err)
listener, err := net.ListenTCP("tcp", addr)
checkError(err)
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
// kcp server
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
switch c.String("mode") {
case "normal":
nodelay, interval, resend, nc = 0, 30, 2, 1
case "fast":
nodelay, interval, resend, nc = 0, 20, 2, 1
case "fast2":
nodelay, interval, resend, nc = 1, 20, 2, 1
case "fast3":
nodelay, interval, resend, nc = 1, 10, 2, 1
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
var block kcp.BlockCrypt
switch config.Crypt {
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
block, _ = kcp.NewSalsa20BlockCrypt(pass)
default:
config.Crypt = "aes"
block, _ = kcp.NewAESBlockCrypt(pass)
}
log.Println("listening on:", listener.Addr())
log.Println("encryption:", c.String("crypt"))
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
log.Println("remote address:", c.String("remoteaddr"))
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
log.Println("compression:", !c.Bool("nocomp"))
log.Println("mtu:", c.Int("mtu"))
log.Println("datashard:", c.Int("datashard"), "parityshard:", c.Int("parityshard"))
log.Println("acknodelay:", c.Bool("acknodelay"))
log.Println("dscp:", c.Int("dscp"))
log.Println("conn:", c.Int("conn"))
log.Println("encryption:", config.Crypt)
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
log.Println("remote address:", config.RemoteAddr)
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
log.Println("compression:", !config.NoComp)
log.Println("mtu:", config.MTU)
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
log.Println("acknodelay:", config.AckNodelay)
log.Println("dscp:", config.DSCP)
log.Println("sockbuf:", config.SockBuf)
log.Println("keepalive:", config.KeepAlive)
log.Println("conn:", config.Conn)
log.Println("autoexpire:", config.AutoExpire)
log.Println("scavengettl:", config.ScavengeTTL)
log.Println("snmplog:", config.SnmpLog)
log.Println("snmpperiod:", config.SnmpPeriod)
log.Println("pprof:", config.Pprof)
createConn := func() *yamux.Session {
var block kcp.BlockCrypt
switch c.String("crypt") {
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
default:
block, _ = kcp.NewAESBlockCrypt(pass)
smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = config.SockBuf
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
createConn := func() (*smux.Session, error) {
kcpconn, err := kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
if err != nil {
return nil, errors.Wrap(err, "createConn()")
}
kcpconn.SetStreamMode(true)
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
kcpconn.SetMtu(config.MTU)
kcpconn.SetACKNoDelay(config.AckNodelay)
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
log.Println("SetDSCP:", err)
}
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
log.Println("SetReadBuffer:", err)
}
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
log.Println("SetWriteBuffer:", err)
}
kcpconn, err := kcp.DialWithOptions(c.String("remoteaddr"), block, c.Int("datashard"), c.Int("parityshard"))
checkError(err)
kcpconn.SetNoDelay(nodelay, interval, resend, nc)
kcpconn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
kcpconn.SetMtu(c.Int("mtu"))
kcpconn.SetACKNoDelay(c.Bool("acknodelay"))
kcpconn.SetDSCP(c.Int("dscp"))
// stream multiplex
config := &yamux.Config{
AcceptBacklog: 256,
EnableKeepAlive: true,
KeepAliveInterval: 30 * time.Second,
ConnectionWriteTimeout: 30 * time.Second,
MaxStreamWindowSize: 16777216,
LogOutput: os.Stderr,
}
var session *yamux.Session
if c.Bool("nocomp") {
session, err = yamux.Client(kcpconn, config)
var session *smux.Session
if config.NoComp {
session, err = smux.Client(kcpconn, smuxConfig)
} else {
session, err = yamux.Client(newCompStream(kcpconn), config)
session, err = smux.Client(newCompStream(kcpconn), smuxConfig)
}
checkError(err)
return session
if err != nil {
return nil, errors.Wrap(err, "createConn()")
}
log.Println("connection:", kcpconn.LocalAddr(), "->", kcpconn.RemoteAddr())
return session, nil
}
numconn := uint16(c.Int("conn"))
var muxes []*yamux.Session
for i := uint16(0); i < numconn; i++ {
muxes = append(muxes, createConn())
// wait until a connection is ready
waitConn := func() *smux.Session {
for {
if session, err := createConn(); err == nil {
return session
} else {
time.Sleep(time.Second)
}
}
}
numconn := uint16(config.Conn)
muxes := make([]struct {
session *smux.Session
ttl time.Time
}, numconn)
for k := range muxes {
muxes[k].session = waitConn()
muxes[k].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
}
chScavenger := make(chan *smux.Session, 128)
go scavenger(chScavenger, config.ScavengeTTL)
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
if config.Pprof {
go http.ListenAndServe(":6060", nil)
}
rr := uint16(0)
for {
p1, err := listener.AcceptTCP()
checkError(err)
mux := muxes[rr%numconn]
p2, err := mux.Open()
if err != nil { // yamux failure
log.Println(err)
p1.Close()
mux.Close()
muxes[rr%numconn] = createConn()
continue
if err != nil {
log.Fatalln(err)
}
go handleClient(p1, p2)
checkError(err)
idx := rr % numconn
// do auto expiration && reconnection
if muxes[idx].session.IsClosed() || (config.AutoExpire > 0 && time.Now().After(muxes[idx].ttl)) {
chScavenger <- muxes[idx].session
muxes[idx].session = waitConn()
muxes[idx].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
}
go handleClient(muxes[idx].session, p1)
rr++
}
}
myApp.Run(os.Args)
}
type scavengeSession struct {
session *smux.Session
ts time.Time
}
func scavenger(ch chan *smux.Session, ttl int) {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
var sessionList []scavengeSession
for {
select {
case sess := <-ch:
sessionList = append(sessionList, scavengeSession{sess, time.Now()})
log.Println("session marked as expired")
case <-ticker.C:
var newList []scavengeSession
for k := range sessionList {
s := sessionList[k]
if s.session.NumStreams() == 0 || s.session.IsClosed() {
log.Println("session normally closed")
s.session.Close()
} else if ttl >= 0 && time.Since(s.ts) >= time.Duration(ttl)*time.Second {
log.Println("session reached scavenge ttl")
s.session.Close()
} else {
newList = append(newList, sessionList[k])
}
}
sessionList = newList
}
}
}
func snmpLogger(path string, interval int) {
if path == "" || interval == 0 {
return
}
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
f, err := os.OpenFile(time.Now().Format(path), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Println(err)
return
}
w := csv.NewWriter(f)
// write header in empty file
if stat, err := f.Stat(); err == nil && stat.Size() == 0 {
if err := w.Write(append([]string{"Unix"}, kcp.DefaultSnmp.Header()...)); err != nil {
log.Println(err)
}
}
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
log.Println(err)
}
kcp.DefaultSnmp.Reset()
w.Flush()
f.Close()
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"os/signal"
"syscall"
"github.com/xtaci/kcp-go"
kcp "github.com/xtaci/kcp-go"
)
func init() {
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 33 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

+43
View File
@@ -0,0 +1,43 @@
package main
import (
"encoding/json"
"os"
)
// Config for server
type Config struct {
Listen string `json:"listen"`
Target string `json:"target"`
Key string `json:"key"`
Crypt string `json:"crypt"`
Mode string `json:"mode"`
MTU int `json:"mtu"`
SndWnd int `json:"sndwnd"`
RcvWnd int `json:"rcvwnd"`
DataShard int `json:"datashard"`
ParityShard int `json:"parityshard"`
DSCP int `json:"dscp"`
NoComp bool `json:"nocomp"`
AckNodelay bool `json:"acknodelay"`
NoDelay int `json:"nodelay"`
Interval int `json:"interval"`
Resend int `json:"resend"`
NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"`
KeepAlive int `json:"keepalive"`
Log string `json:"log"`
SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"`
Pprof bool `json:"pprof"`
}
func parseJSONConfig(config *Config, path string) error {
file, err := os.Open(path) // For read access.
if err != nil {
return err
}
defer file.Close()
return json.NewDecoder(file).Decode(config)
}
+241 -76
View File
@@ -2,26 +2,38 @@ package main
import (
"crypto/sha1"
"encoding/csv"
"fmt"
"io"
"log"
"math/rand"
"net"
"net/http"
_ "net/http/pprof"
"os"
"sync"
"time"
"golang.org/x/crypto/pbkdf2"
"github.com/golang/snappy"
"github.com/hashicorp/yamux"
"github.com/urfave/cli"
"github.com/xtaci/kcp-go"
kcp "github.com/xtaci/kcp-go"
"github.com/xtaci/smux"
)
var (
// VERSION is injected by buildflags
VERSION = "SELFBUILD"
SALT = "kcp-go"
// SALT is use for pbkdf2 key expansion
SALT = "kcp-go"
)
// global recycle buffer
var copyBuf sync.Pool
const bufSize = 4096
type compStream struct {
conn net.Conn
w *snappy.Writer
@@ -51,35 +63,29 @@ func newCompStream(conn net.Conn) *compStream {
}
// handle multiplex-ed connection
func handleMux(conn io.ReadWriteCloser, target string) {
func handleMux(conn io.ReadWriteCloser, config *Config) {
// stream multiplex
var mux *yamux.Session
config := &yamux.Config{
AcceptBacklog: 256,
EnableKeepAlive: true,
KeepAliveInterval: 30 * time.Second,
ConnectionWriteTimeout: 30 * time.Second,
MaxStreamWindowSize: 16777216,
LogOutput: os.Stderr,
}
m, err := yamux.Server(conn, config)
smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = config.SockBuf
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
mux, err := smux.Server(conn, smuxConfig)
if err != nil {
log.Println(err)
return
}
mux = m
defer mux.Close()
for {
p1, err := mux.Accept()
p1, err := mux.AcceptStream()
if err != nil {
log.Println(err)
return
}
p2, err := net.DialTimeout("tcp", target, 5*time.Second)
p2, err := net.DialTimeout("tcp", config.Target, 5*time.Second)
if err != nil {
p1.Close()
log.Println(err)
return
continue
}
go handleClient(p1, p2)
}
@@ -94,14 +100,18 @@ func handleClient(p1, p2 io.ReadWriteCloser) {
// start tunnel
p1die := make(chan struct{})
go func() {
io.Copy(p1, p2)
buf := copyBuf.Get().([]byte)
io.CopyBuffer(p1, p2, buf)
close(p1die)
copyBuf.Put(buf)
}()
p2die := make(chan struct{})
go func() {
io.Copy(p2, p1)
buf := copyBuf.Get().([]byte)
io.CopyBuffer(p2, p1, buf)
close(p2die)
copyBuf.Put(buf)
}()
// wait for tunnel termination
@@ -111,11 +121,25 @@ func handleClient(p1, p2 io.ReadWriteCloser) {
}
}
func checkError(err error) {
if err != nil {
log.Printf("%+v\n", err)
os.Exit(-1)
}
}
func main() {
rand.Seed(int64(time.Now().Nanosecond()))
copyBuf.New = func() interface{} {
return make([]byte, bufSize)
}
if VERSION == "SELFBUILD" {
// add more log flags for debugging
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
myApp := cli.NewApp()
myApp.Name = "kcptun"
myApp.Usage = "kcptun server"
myApp.Usage = "server(with SMUX)"
myApp.Version = VERSION
myApp.Flags = []cli.Flag{
cli.StringFlag{
@@ -131,23 +155,23 @@ func main() {
cli.StringFlag{
Name: "key",
Value: "it's a secrect",
Usage: "key for communcation, must be the same as kcptun client",
Usage: "pre-shared secret between client and server",
EnvVar: "KCPTUN_KEY",
},
cli.StringFlag{
Name: "crypt",
Value: "aes",
Usage: "methods for encryption: aes, tea, xor, none",
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none",
},
cli.StringFlag{
Name: "mode",
Value: "fast",
Usage: "mode for communication: fast3, fast2, fast, normal",
Usage: "profiles: fast3, fast2, fast, normal, manual",
},
cli.IntFlag{
Name: "mtu",
Value: 1350,
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
Usage: "set maximum transmission unit for UDP packets",
},
cli.IntFlag{
Name: "sndwnd",
@@ -159,29 +183,30 @@ func main() {
Value: 1024,
Usage: "set receive window size(num of packets)",
},
cli.BoolFlag{
Name: "nocomp",
Usage: "disable compression",
},
cli.IntFlag{
Name: "datashard",
Name: "datashard,ds",
Value: 10,
Usage: "set reed-solomon erasure coding - datashard",
},
cli.IntFlag{
Name: "parityshard",
Name: "parityshard,ps",
Value: 3,
Usage: "set reed-solomon erasure coding - parityshard",
},
cli.BoolFlag{
Name: "acknodelay",
Usage: "flush ack immediately when a packet is received",
},
cli.IntFlag{
Name: "dscp",
Value: 0,
Usage: "set DSCP(6bit)",
},
cli.BoolFlag{
Name: "nocomp",
Usage: "disable compression",
},
cli.BoolFlag{
Name: "acknodelay",
Usage: "flush ack immediately when a packet is received",
Hidden: true,
},
cli.IntFlag{
Name: "nodelay",
Value: 0,
@@ -189,7 +214,7 @@ func main() {
},
cli.IntFlag{
Name: "interval",
Value: 40,
Value: 50,
Hidden: true,
},
cli.IntFlag{
@@ -202,65 +227,205 @@ func main() {
Value: 0,
Hidden: true,
},
cli.IntFlag{
Name: "sockbuf",
Value: 4194304, // socket buffer size in bytes
Hidden: true,
},
cli.IntFlag{
Name: "keepalive",
Value: 10, // nat keepalive interval in seconds
Hidden: true,
},
cli.StringFlag{
Name: "snmplog",
Value: "",
Usage: "collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log",
},
cli.IntFlag{
Name: "snmpperiod",
Value: 60,
Usage: "snmp collect period, in seconds",
},
cli.BoolFlag{
Name: "pprof",
Usage: "start profiling server on :6060",
},
cli.StringFlag{
Name: "log",
Value: "",
Usage: "specify a log file to output, default goes to stderr",
},
cli.StringFlag{
Name: "c",
Value: "", // when the value is not empty, the config path must exists
Usage: "config from json file, which will override the command from shell",
},
}
myApp.Action = func(c *cli.Context) {
log.Println("version:", VERSION)
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
switch c.String("mode") {
case "normal":
nodelay, interval, resend, nc = 0, 30, 2, 1
case "fast":
nodelay, interval, resend, nc = 0, 20, 2, 1
case "fast2":
nodelay, interval, resend, nc = 1, 20, 2, 1
case "fast3":
nodelay, interval, resend, nc = 1, 10, 2, 1
myApp.Action = func(c *cli.Context) error {
config := Config{}
config.Listen = c.String("listen")
config.Target = c.String("target")
config.Key = c.String("key")
config.Crypt = c.String("crypt")
config.Mode = c.String("mode")
config.MTU = c.Int("mtu")
config.SndWnd = c.Int("sndwnd")
config.RcvWnd = c.Int("rcvwnd")
config.DataShard = c.Int("datashard")
config.ParityShard = c.Int("parityshard")
config.DSCP = c.Int("dscp")
config.NoComp = c.Bool("nocomp")
config.AckNodelay = c.Bool("acknodelay")
config.NoDelay = c.Int("nodelay")
config.Interval = c.Int("interval")
config.Resend = c.Int("resend")
config.NoCongestion = c.Int("nc")
config.SockBuf = c.Int("sockbuf")
config.KeepAlive = c.Int("keepalive")
config.Log = c.String("log")
config.SnmpLog = c.String("snmplog")
config.SnmpPeriod = c.Int("snmpperiod")
config.Pprof = c.Bool("pprof")
if c.String("c") != "" {
//Now only support json config file
err := parseJSONConfig(&config, c.String("c"))
checkError(err)
}
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
// log redirect
if config.Log != "" {
f, err := os.OpenFile(config.Log, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
checkError(err)
defer f.Close()
log.SetOutput(f)
}
switch config.Mode {
case "normal":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 50, 2, 1
case "fast":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 40, 2, 1
case "fast2":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 30, 2, 1
case "fast3":
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1
}
log.Println("version:", VERSION)
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
var block kcp.BlockCrypt
switch c.String("crypt") {
switch config.Crypt {
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
block, _ = kcp.NewSalsa20BlockCrypt(pass)
default:
config.Crypt = "aes"
block, _ = kcp.NewAESBlockCrypt(pass)
}
lis, err := kcp.ListenWithOptions(c.String("listen"), block, c.Int("datashard"), c.Int("parityshard"))
if err != nil {
log.Fatal(err)
}
log.Println("listening on ", lis.Addr())
log.Println("encryption:", c.String("crypt"))
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
log.Println("compression:", !c.Bool("nocomp"))
log.Println("mtu:", c.Int("mtu"))
log.Println("datashard:", c.Int("datashard"), "parityshard:", c.Int("parityshard"))
log.Println("acknodelay:", c.Bool("acknodelay"))
log.Println("dscp:", c.Int("dscp"))
for {
if conn, err := lis.Accept(); err == nil {
log.Println("remote address:", conn.RemoteAddr())
conn.SetNoDelay(nodelay, interval, resend, nc)
conn.SetMtu(c.Int("mtu"))
conn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
conn.SetACKNoDelay(c.Bool("acknodelay"))
conn.SetDSCP(c.Int("dscp"))
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
checkError(err)
log.Println("listening on:", lis.Addr())
log.Println("target:", config.Target)
log.Println("encryption:", config.Crypt)
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
log.Println("compression:", !config.NoComp)
log.Println("mtu:", config.MTU)
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
log.Println("acknodelay:", config.AckNodelay)
log.Println("dscp:", config.DSCP)
log.Println("sockbuf:", config.SockBuf)
log.Println("keepalive:", config.KeepAlive)
log.Println("snmplog:", config.SnmpLog)
log.Println("snmpperiod:", config.SnmpPeriod)
log.Println("pprof:", config.Pprof)
if c.Bool("nocomp") {
go handleMux(conn, c.String("target"))
if err := lis.SetDSCP(config.DSCP); err != nil {
log.Println("SetDSCP:", err)
}
if err := lis.SetReadBuffer(config.SockBuf); err != nil {
log.Println("SetReadBuffer:", err)
}
if err := lis.SetWriteBuffer(config.SockBuf); err != nil {
log.Println("SetWriteBuffer:", err)
}
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
if config.Pprof {
go http.ListenAndServe(":6060", nil)
}
for {
if conn, err := lis.AcceptKCP(); err == nil {
log.Println("remote address:", conn.RemoteAddr())
conn.SetStreamMode(true)
conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
conn.SetMtu(config.MTU)
conn.SetWindowSize(config.SndWnd, config.RcvWnd)
conn.SetACKNoDelay(config.AckNodelay)
if config.NoComp {
go handleMux(conn, &config)
} else {
go handleMux(newCompStream(conn), c.String("target"))
go handleMux(newCompStream(conn), &config)
}
} else {
log.Println(err)
log.Printf("%+v", err)
}
}
}
myApp.Run(os.Args)
}
func snmpLogger(path string, interval int) {
if path == "" || interval == 0 {
return
}
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
f, err := os.OpenFile(time.Now().Format(path), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Println(err)
return
}
w := csv.NewWriter(f)
// write header in empty file
if stat, err := f.Stat(); err == nil && stat.Size() == 0 {
if err := w.Write(append([]string{"Unix"}, kcp.DefaultSnmp.Header()...)); err != nil {
log.Println(err)
}
}
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
log.Println(err)
}
kcp.DefaultSnmp.Reset()
w.Flush()
f.Close()
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"os/signal"
"syscall"
"github.com/xtaci/kcp-go"
kcp "github.com/xtaci/kcp-go"
)
func init() {