Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0552bebe46 | ||
|
|
d158e6df3f | ||
|
|
cf7bca5cb7 | ||
|
|
99a2b60c7d | ||
|
|
4a5024b361 | ||
|
|
12bd3853d5 | ||
|
|
7992b87889 | ||
|
|
cfcc2e0676 | ||
|
|
1e870f02af | ||
|
|
bf2abf14e7 | ||
|
|
2df3e6c438 | ||
|
|
ba4c93c3f5 | ||
|
|
bc22f46065 | ||
|
|
3fc15de95d | ||
|
|
6f9449bb10 | ||
|
|
c6cbfd307e | ||
|
|
d68792cb1a | ||
|
|
117edce137 | ||
|
|
990a5f7f87 | ||
|
|
19d7d74d59 | ||
|
|
31afe325ad | ||
|
|
ab7519c2d6 | ||
|
|
d6da2e1ead | ||
|
|
4d5a3dd791 | ||
|
|
4a46d696b6 | ||
|
|
33d50dcd45 | ||
|
|
2b3f6dbabe | ||
|
|
448717fa1c | ||
|
|
887fba3381 | ||
|
|
fb62d45df8 | ||
|
|
cd1e2b4ff7 | ||
|
|
5ee659dd41 | ||
|
|
1a596f55f4 | ||
|
|
889a904dca | ||
|
|
172dcf6481 | ||
|
|
ff92c70b40 | ||
|
|
af1d28ae5a | ||
|
|
4a7d143c69 | ||
|
|
7ff69f16d5 | ||
|
|
7eb037c4dd | ||
|
|
a87f6af812 | ||
|
|
7574f9bfd3 | ||
|
|
d5b5cf683b | ||
|
|
fded353b68 | ||
|
|
832dbed064 | ||
|
|
4b660fa02c | ||
|
|
59e6ebee05 | ||
|
|
c1a895426d | ||
|
|
ebdcc9ae8a | ||
|
|
2c076f7a34 | ||
|
|
a1a0fbd060 | ||
|
|
bc4214e4a8 | ||
|
|
61e6dfe87b | ||
|
|
4448a2658d | ||
|
|
c9a312f12b | ||
|
|
2bb48495d3 | ||
|
|
06f0a6f80b | ||
|
|
ea895ad824 | ||
|
|
a72bf0c9b5 | ||
|
|
2b83dbda07 | ||
|
|
c35ef339af | ||
|
|
c96d4b534e | ||
|
|
6216049c14 | ||
|
|
0d98020e59 | ||
|
|
14ea6f8a71 | ||
|
|
214669eaf9 | ||
|
|
48d2cad6ac | ||
|
|
67cd5a4e22 | ||
|
|
f49392c95e | ||
|
|
4c9370a252 | ||
|
|
ef3cf3d982 | ||
|
|
8e5f405336 | ||
|
|
2aa6887860 | ||
|
|
9d13ec9350 | ||
|
|
ef9d08567f | ||
|
|
66ebcf2773 | ||
|
|
67a08b43ed | ||
|
|
46ce8a2e51 | ||
|
|
fd6a6e4a64 | ||
|
|
1de3c1fa8a | ||
|
|
cc821b6eaf | ||
|
|
6f8ce90c23 | ||
|
|
32be3e836b | ||
|
|
664a79e488 | ||
|
|
74d874b0db | ||
|
|
58874784f3 | ||
|
|
f92bae1d03 | ||
|
|
a1aa89ea1c | ||
|
|
52c75f68ba | ||
|
|
57fce73ca6 | ||
|
|
13e980cdff |
@@ -1,29 +0,0 @@
|
||||
问问题前先搜索ISSUE,并搞清楚下面的问题:
|
||||
|
||||
1. 检查 ```-key xxx``` 至少三遍, ***保证***两边一致。
|
||||
2. 保证```-nocomp, -datashard, -parityshard, -key, -crypt, -smuxver```两边一致。
|
||||
3. 是否在服务器端,正确设定了转发的目标服务器地址 ***--target***。
|
||||
4. 是否在客户端,正确的连接到了 client的监听端口。
|
||||
5. 如果第3条不确定,尝试在服务器上telnet target port试试。
|
||||
6. 防火墙是否关闭了UDP通信,或者设置了UDP的最大发包速率?
|
||||
7. 两端的版本是否一致?
|
||||
8. 是不是最新版本?
|
||||
9. 两端分别是什么操作系统?
|
||||
10. 两端的输出日志是什么?
|
||||
|
||||
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, -smuxver``` ***must be the same*** on both side.
|
||||
3. Did you correctly set the ***-target*** on the server side?
|
||||
4. Did you correctly connected to the listening port on client side?
|
||||
5. ***MAKE SURE*** ```telnet target port``` on your server successful(don't ask me why couldn't).
|
||||
6. Does your ***firewall allows UDP*** communications? (including your ISP Cable-Modem)
|
||||
7. Are you using the **same version** for both client & server
|
||||
8. Are you using the **latest release**?
|
||||
9. Which **OS** do you use?
|
||||
10. Which end for this issue related to, **client or server**?
|
||||
|
||||
|
||||
@@ -24,5 +24,3 @@ _testmain.go
|
||||
*.prof
|
||||
client/client
|
||||
server/server
|
||||
build/*
|
||||
.DS_Store
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- 1.6
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
install:
|
||||
- env GO111MODULE=on go get github.com/xtaci/kcptun/client
|
||||
- env GO111MODULE=on go get github.com/xtaci/kcptun/server
|
||||
- go get github.com/xtaci/kcptun/client
|
||||
- go get github.com/xtaci/kcptun/server
|
||||
before_script:
|
||||
script:
|
||||
- cd $HOME/gopath/src/github.com/xtaci/kcptun/client
|
||||
- env GO111MODULE=on $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- cd $HOME/gopath/src/github.com/xtaci/kcptun/server
|
||||
- env GO111MODULE=on $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- exit 0
|
||||
|
||||
|
Before Width: | Height: | Size: 636 B |
@@ -1,12 +1,9 @@
|
||||
FROM golang:alpine as builder
|
||||
FROM golang:alpine
|
||||
MAINTAINER xtaci <daniel820313@gmail.com>
|
||||
ENV GO111MODULE=on
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add git gcc libc-dev linux-headers
|
||||
RUN go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/client && go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/server
|
||||
|
||||
FROM alpine:3.9
|
||||
COPY --from=builder /go/bin /bin
|
||||
apk add git
|
||||
RUN go get github.com/xtaci/kcptun/client
|
||||
RUN go get github.com/xtaci/kcptun/server
|
||||
EXPOSE 29900/udp
|
||||
EXPOSE 12948
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
# <img src="logo.png" alt="kcptun" height="60px" />
|
||||
[![GoDoc][1]][2] [![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Gitter][19]][20]
|
||||
[1]: https://godoc.org/github.com/xtaci/kcptun?status.svg
|
||||
[2]: https://godoc.org/github.com/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
|
||||
[9]: https://img.shields.io/github/stars/xtaci/kcptun.svg
|
||||
[10]: https://github.com/xtaci/kcptun/stargazers
|
||||
[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
|
||||
[19]: https://badges.gitter.im/xtaci/kcptun.svg
|
||||
[20]: https://gitter.im/xtaci/kcptun?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
|
||||
A tool for converting tcp stream into kcp+udp stream, :zap: ***[download address](https://github.com/xtaci/kcptun/releases/latest)***:zap:
|
||||
|
||||

|
||||
|
||||
***kcptun is based on [kcp-go](https://github.com/xtaci/kcp-go)***
|
||||
|
||||
### *QuickStart* :lollipop:
|
||||
```
|
||||
Server Side: ./server_linux_amd64 -t "127.0.0.1:1080" -l ":554" -mode fast2 // forwarding to local port 1080
|
||||
Client Side: ./client_darwin_amd64 -r "SERVERIP:554" -l ":1080" -mode fast2 // listening on port 1080
|
||||
```
|
||||
|
||||
### *Performance* :lollipop:
|
||||
<img src="fast.png" alt="fast.com" height="256px" />
|
||||
* Speed tested with: https://fast.com
|
||||
* WAN Link Speed: 100M ADSL
|
||||
* WIFI: 5GHz TL-WDR3320
|
||||
|
||||
### *Usage* :lollipop:
|
||||

|
||||

|
||||
|
||||
### *Applications* :lollipop:
|
||||
1. Real-time gaming.
|
||||
2. Cross-ISP data exchange in PRC.
|
||||
3. Other lossy network.
|
||||
|
||||
### *Parameters Recommended* :lollipop:
|
||||
```
|
||||
Test Environment: China Telecom 100M ADSL(100mbps up/8mbps down)
|
||||
SERVER: -mtu 1400 -sndwnd 2048 -rcvwnd 2048 -mode fast2
|
||||
CLIENT: -mtu 1400 -sndwnd 256 -rcvwnd 2048 -mode fast2 -dscp 46
|
||||
```
|
||||
|
||||
*How to optimize*:
|
||||
> Step 1:Increase client rcvwnd & server sndwnd simultaneously & gradually。
|
||||
> Step 2:Try download something and observer, if the bandwidth usage is close the limit then stop, otherwise goto step 1.
|
||||
|
||||
***NOTICE: if too much retranmission happens, it's quite possible the windows are too large***
|
||||
|
||||
### *Traffic Control* :lollipop:
|
||||
***Intended audience : for those server's bandwidth is quite limited.***
|
||||
|
||||
Example: To limit outgoing bandwidth to 32mbit/s on server.
|
||||
```
|
||||
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:~#
|
||||
```
|
||||
|
||||
### *DSCP* :lollipop:
|
||||
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.[1]
|
||||
|
||||
setting each side with ```-dscp value```.
|
||||
|
||||
### *Embeded Mode* :lollipop:
|
||||
Latency:
|
||||
*fast3 >* ***[fast2]*** *> fast > normal > default*
|
||||
Payload Ratio:
|
||||
*default > normal > fast >* ***[fast2]*** *> fast3*
|
||||
Parameters in middle is balanced for latency & payload ratio, the faster you get the more wasteful you are.
|
||||
Manual control is supported with hidden parameters, you must understand KCP protocol before doing this.
|
||||
```
|
||||
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20
|
||||
```
|
||||
|
||||
### *Forward Error Correction* :lollipop:
|
||||
In coding theory, the Reed–Solomon code belongs to the class of non-binary cyclic error-correcting codes. The Reed–Solomon 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 Reed–Solomon 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, Reed–Solomon 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.
|
||||
|
||||

|
||||
|
||||
Setting parameters of RS-Code with ```-datashard 10 -parityshard 3```
|
||||
|
||||
### *Snappy Stream Compression* :lollipop:
|
||||
> 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/
|
||||
|
||||
disable compression by setting ```-nocomp``` on both side.
|
||||
|
||||
> Tips: Turning off compression may reduce latency.
|
||||
|
||||
### *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
|
||||
}
|
||||
```
|
||||
|
||||
Sending a signal by ```kill -SIGUSR1 pid``` will give SNMP information for KCP,useful for fine-grained adjustment.
|
||||
Of which ```RetransSegs,FastRetransSegs,LostSegs,OutSegs``` is the most useful.
|
||||
|
||||
### *Donations* :dollar:
|
||||

|
||||
|
||||
All donations to this project will be used on the R&D of [gonet/2](http://gonet2.github.io/).
|
||||
|
||||
### *References* :paperclip:
|
||||
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
|
||||
@@ -1,9 +1,7 @@
|
||||
# <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
|
||||
# <img src="logo.png" alt="kcptun" height="60px" />
|
||||
[![GoDoc][1]][2] [![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Gitter][19]][20]
|
||||
[1]: https://godoc.org/github.com/xtaci/kcptun?status.svg
|
||||
[2]: https://godoc.org/github.com/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
|
||||
@@ -18,279 +16,115 @@
|
||||
[16]: https://github.com/xtaci/kcptun/releases
|
||||
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
|
||||
[18]: https://github.com/skywind3000/kcp
|
||||
[19]: https://badges.gitter.im/xtaci/kcptun.svg
|
||||
[20]: https://gitter.im/xtaci/kcptun?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
|
||||
|
||||
<img src="kcptun.png" alt="kcptun" height="300px"/>
|
||||
|
||||
> *Disclaimer: kcptun maintains a single website — [github.com/xtaci/kcptun](https://github.com/xtaci/kcptun). Any websites other than [github.com/xtaci/kcptun](https://github.com/xtaci/kcptun) are not endorsed by xtaci.*
|
||||
|
||||
### QuickStart
|
||||
|
||||
Increase the number of open files on your server, as:
|
||||
|
||||
`ulimit -n 65535`, or write it in `~/.bashrc`.
|
||||
|
||||
Suggested `sysctl.conf` parameters for better handling of UDP packets:
|
||||
***[kcp-go](https://github.com/xtaci/kcp-go)协议测试小工具 :zap: [官方下载地址](https://github.com/xtaci/kcptun/releases/latest):zap:***
|
||||
|
||||

|
||||
[English Readme](README.en.md)
|
||||
### *快速设定* :lollipop:
|
||||
```
|
||||
net.core.rmem_max=26214400 // BDP - bandwidth delay product
|
||||
net.core.rmem_default=26214400
|
||||
net.core.wmem_max=26214400
|
||||
net.core.wmem_default=26214400
|
||||
net.core.netdev_max_backlog=2048 // proportional to -rcvwnd
|
||||
服务器: ./server_linux_amd64 -t "127.0.0.1:8388" -l ":554" -mode fast2 // 转发到服务器的本地8388端口
|
||||
客户端: ./client_darwin_amd64 -r "服务器IP地址:554" -l ":8388" -mode fast2 // 监听客户端的本地8388端口
|
||||
注: 服务器端需要有服务监听8388端口
|
||||
```
|
||||
|
||||
You can also increase the per-socket buffer by adding parameter(default 4MB):
|
||||
### *速度对比* :lollipop:
|
||||
<img src="fast.png" alt="fast.com" height="256px" />
|
||||
* 测速网站: https://fast.com
|
||||
* 接入: 100M ADSL
|
||||
* WIFI: 5GHz TL-WDR3320
|
||||
|
||||
### *使用方法* :lollipop:
|
||||
在Mac OS X El Capitan下的帮助输出:
|
||||
|
||||

|
||||

|
||||
|
||||
### *推荐参数* :lollipop:
|
||||
```
|
||||
-sockbuf 16777217
|
||||
```
|
||||
for **slow processors**, increasing this buffer is **CRITICAL** to receive packets properly.
|
||||
适用大部分ADSL接入(非对称上下行)的参数(实验环境电信100M ADSL)
|
||||
其它带宽请按比例调整,比如 50M ADSL,把 CLIENT 的 -sndwnd -rcvwnd 减掉一半,SERVER 不变
|
||||
|
||||
Download a corresponding one from precompiled [Releases](https://github.com/xtaci/kcptun/releases).
|
||||
|
||||
```
|
||||
KCP Client: ./client_darwin_amd64 -r "KCP_SERVER_IP:4000" -l ":8388" -mode fast3 -nocomp -autoexpire 900 -sockbuf 16777217 -dscp 46
|
||||
KCP Server: ./server_linux_amd64 -t "TARGET_IP:8388" -l ":4000" -mode fast3 -nocomp -sockbuf 16777217 -dscp 46
|
||||
```
|
||||
The above commands will establish port forwarding channel for 8388/tcp as:
|
||||
|
||||
> Application -> **KCP Client(8388/tcp) -> KCP Server(4000/udp)** -> Target Server(8388/tcp)
|
||||
|
||||
which tunnels the original connection:
|
||||
|
||||
> Application -> Target Server(8388/tcp)
|
||||
|
||||
### Install from source
|
||||
|
||||
```
|
||||
$ export GO111MODULE=on
|
||||
$ go get -u github.com/xtaci/kcptun/...
|
||||
SERVER: -mtu 1400 -sndwnd 2048 -rcvwnd 2048 -mode fast2
|
||||
CLIENT: -mtu 1400 -sndwnd 256 -rcvwnd 2048 -mode fast2 -dscp 46
|
||||
*巭孬嫑乱动*
|
||||
```
|
||||
|
||||
All precompiled releases are genereated from `build-release.sh` script.
|
||||
两端参数必须一致的有:
|
||||
* datashard
|
||||
* parityshard
|
||||
* nocomp
|
||||
* key
|
||||
* crypt
|
||||
|
||||
### Performance
|
||||
其余为两边可独立设定的参数
|
||||
|
||||
<img src="fast.png" alt="fast.com" height="256px" />
|
||||
*简易自我调优方法*:
|
||||
> 第一步:同时在两端逐步增大client rcvwnd和server sndwnd;
|
||||
> 第二步:尝试下载,观察如果带宽利用率(服务器+客户端两端都要观察)接近物理带宽则停止,否则跳转到第一步。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
> Practical bandwidth graph with parameters: -mode fast3 -ds 10 -ps 3
|
||||
|
||||
|
||||
|
||||
### 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.
|
||||
(mtu is adjustable by `-mtu`)
|
||||
|
||||
#### 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*
|
||||
|
||||
#### HOLB
|
||||
|
||||
Since streams are multiplexed into a single physical channel, head of line blocking may appear under certain circumstances, by
|
||||
increasing `-smuxbuf` to a larger value (default 4MB) may mitigate this problem, obviously this will costs more memory.
|
||||
|
||||
For versions >= v20190924, you can switch to smux version 2, smux v2 has options to limit per-stream memory usage, now set `-smuxver 2` to enable smux v2, and adjust `-streambuf` to limit per-stream memory usage, eg: `-streambuf 2097152` can limit per-stream memory usage to 2MB. By limiting stream buffer on the receiver side, a back-pressure will be conducted to the sender and limits reading, and finally prevent source from sending too much data to occupy every bits of buffer along the link. (Setting -smuxver **MUST** be **IDENTICAL** on both side, default is 1. )
|
||||
|
||||
#### Slow Devices
|
||||
|
||||
kcptun made use of **ReedSolomon-Codes** to recover lost packets, which requires massive amount of computation, a low-end ARM device cannot satisfy kcptun well. To unleash the full potential of kcptun, a multi-core x86 homeserver CPU like AMD Opteron is recommended.
|
||||
If you insist on running under some ARM routers, you'd better turn off `FEC` and use `salsa20` as the encryption method.
|
||||
|
||||
### Expert Tuning Guide
|
||||
|
||||
#### Overview
|
||||
|
||||
<p align="left"><img src="layeredparams.png" alt="params" height="450px"/></p>
|
||||
|
||||
#### Usage
|
||||
***注意:产生大量重传时,一定是窗口偏大了***
|
||||
|
||||
*带宽计算公式*:
|
||||
```
|
||||
➜ ~ ./client_linux_amd64 -h
|
||||
NAME:
|
||||
kcptun - client(with SMUX)
|
||||
在不丢包的情况下,有最大-rcvwnd 个数据包在网络上正在向你传输,以平均数据包大小avgsize计算,在任意时刻,有:
|
||||
|
||||
USAGE:
|
||||
client_linux_amd64 [global options] command [command options] [arguments...]
|
||||
network_cap = rcvwnd*avgsize
|
||||
|
||||
VERSION:
|
||||
20190924
|
||||
数据流向你,这个值再除以ping值(rtt),等于最大带宽使用量。
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
max_bandwidth = network_cap/rtt = rcvwnd*avgsize/rtt
|
||||
|
||||
举例,设rcvwnd = 1024, avgsize = 1KB, rtt = 400ms,则:
|
||||
|
||||
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, sm4, none (default: "aes")
|
||||
--mode value profiles: fast3, fast2, fast, normal, manual (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)
|
||||
--scavengettl value set how long an expired connection can live(in sec), -1 to disable (default: 600)
|
||||
--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
|
||||
--sockbuf value per-socket buffer in bytes (default: 4194304)
|
||||
--smuxver value specify smux version, available 1,2 (default: 1)
|
||||
--smuxbuf value the overall de-mux buffer in bytes (default: 4194304)
|
||||
--streambuf value per stream receive buffer in bytes, smux v2+ (default: 2097152)
|
||||
--keepalive value seconds between heartbeats (default: 10)
|
||||
--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
|
||||
--quiet to suppress the 'stream open/close' messages
|
||||
--tcp to emulate a TCP connection(linux)
|
||||
-c value config from json file, which will override the command from shell
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
|
||||
➜ ~ ./server_linux_amd64 -h
|
||||
NAME:
|
||||
kcptun - server(with SMUX)
|
||||
max_bandwidth = 1024 * 1KB / 400ms = 2.5MB/s ~= 25Mbps
|
||||
|
||||
(注:以上计算不包括前向纠错的数据量)
|
||||
|
||||
USAGE:
|
||||
server_linux_amd64 [global options] command [command options] [arguments...]
|
||||
前向纠错是最大带宽量的一个固定比例增加:
|
||||
|
||||
VERSION:
|
||||
20190924
|
||||
max_bandwidth_fec = max_bandwidth*(datashard+parityshard)/datashard
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
举例,设datashard = 10 , partiyshard = 3,则:
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--listen value, -l value kcp server listen address (default: ":29900")
|
||||
--target value, -t value target server address, or path/to/unix_socket (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, sm4, none (default: "aes")
|
||||
--mode value profiles: fast3, fast2, fast, normal, manual (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
|
||||
--sockbuf value per-socket buffer in bytes (default: 4194304)
|
||||
--smuxver value specify smux version, available 1,2 (default: 1)
|
||||
--smuxbuf value the overall de-mux buffer in bytes (default: 4194304)
|
||||
--streambuf value per stream receive buffer in bytes, smux v2+ (default: 2097152)
|
||||
--keepalive value seconds between heartbeats (default: 10)
|
||||
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
|
||||
--snmpperiod value snmp collect period, in seconds (default: 60)
|
||||
--pprof start profiling server on :6060
|
||||
--log value specify a log file to output, default goes to stderr
|
||||
--quiet to suppress the 'stream open/close' messages
|
||||
--tcp to emulate a TCP connection(linux)
|
||||
-c value config from json file, which will override the command from shell
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
max_bandwidth_fec = max_bandwidth * (10 + 3) /10 = 1.3*max_bandwidth = 1.3 * 25Mbps = 32.5Mbps
|
||||
```
|
||||
|
||||
#### Forward Error Correction
|
||||
### *流量控制* :lollipop:
|
||||
***必要性: 针对流量敏感的服务器,做双保险。***
|
||||
|
||||
In coding theory, the [Reed–Solomon code](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) belongs to the class of non-binary cyclic error-correcting codes. The Reed–Solomon 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 Reed–Solomon 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, Reed–Solomon 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.
|
||||
|
||||

|
||||
|
||||
Setting parameters of RS-Code with ```-datashard m -parityshard n``` on **BOTH** KCP Client & KCP Server **MUST** be **IDENTICAL**.
|
||||
|
||||
#### 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).
|
||||
|
||||
#### Cryptanalysis
|
||||
|
||||
kcptun is shipped with builtin packet encryption powered by various block encryption algorithms and works in [Cipher Feedback Mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Feedback_(CFB)), for each packet to be sent, the encryption process will start from encrypting a [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce) from the [system entropy](https://en.wikipedia.org/wiki//dev/random), so encryption to same plaintexts never leads to a same ciphertexts thereafter.
|
||||
|
||||
The contents of the packets are completely anonymous with encryption, including the headers(FEC,KCP), checksums and contents. Note that, no matter which encryption method you choose on you upper layer, if you disable encryption by specifying `-crypt none` to kcptun, the transmit will be insecure somehow, since the header is ***PLAINTEXT*** to everyone it would be susceptible to header tampering, such as jamming the *sliding window size*, *round-trip time*, *FEC property* and *checksums*. ```aes-128``` is suggested for minimal encryption since modern CPUs are shipped with [AES-NI](https://en.wikipedia.org/wiki/AES_instruction_set) instructions and performs even better than `salsa20`(check the table below).
|
||||
|
||||
Other possible attacks to kcptun includes: a) [traffic analysis](https://en.wikipedia.org/wiki/Traffic_analysis), dataflow on specific websites may have pattern while interchanging data, but this type of eavesdropping has been mitigated by adapting [smux](https://github.com/xtaci/smux) to mix data streams so as to introduce noises, perfect solution to this has not appeared yet, theroretically by shuffling/mixing messages on larger scale network may mitigate this problem. b) [replay attack](https://en.wikipedia.org/wiki/Replay_attack), since the asymmetrical encryption has not been introduced into kcptun for some reason, capturing the packets and replay them on a different machine is possible, (notice: hijacking the session and decrypting the contents is still *impossible*), so upper layers should contain a asymmetrical encryption system to guarantee the authenticity of each message(to process message exactly once), such as HTTPS/OpenSSL/LibreSSL, only by signing the requests with private keys can eliminate this type of attack.
|
||||
|
||||
Important:
|
||||
1. `-crypt` and `-key` must be the same on both KCP Client & KCP Server.
|
||||
2. `-crypt xor` is also insecure and vulnerable to [known-plaintext attack](https://en.wikipedia.org/wiki/Known-plaintext_attack), do not use this unless you know what you are doing. (*cryptanalysis note: any type of [counter mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) is insecure in packet encryption due to the shorten of counter period and leads to iv/nonce collision*)
|
||||
|
||||
Benchmarks for crypto algorithms supported by kcptun:
|
||||
> 基本原则: SERVER的发送速率不能超过ADSL下行带宽,否则只会浪费您的服务器带宽。
|
||||
|
||||
在server通过linux tc,可以限制服务器发送带宽。
|
||||
举例: 用linux tc限制server发送带宽为32mbit/s:
|
||||
```
|
||||
BenchmarkSM4-4 50000 32087 ns/op 93.49 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkAES128-4 500000 3274 ns/op 916.15 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkAES192-4 500000 3587 ns/op 836.34 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkAES256-4 300000 3828 ns/op 783.60 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkTEA-4 100000 15359 ns/op 195.32 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkXOR-4 20000000 90.2 ns/op 33249.02 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkBlowfish-4 50000 26885 ns/op 111.58 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkNone-4 30000000 45.8 ns/op 65557.11 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkCast5-4 50000 34370 ns/op 87.29 MB/s 0 B/op 0 allocs/op
|
||||
Benchmark3DES-4 10000 117893 ns/op 25.45 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkTwofish-4 50000 33477 ns/op 89.61 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkXTEA-4 30000 45825 ns/op 65.47 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkSalsa20-4 500000 3282 ns/op 913.90 MB/s 0 B/op 0 allocs/op
|
||||
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:~#
|
||||
```
|
||||
|
||||
Benchmark result from openssl
|
||||
|
||||
```
|
||||
$ openssl speed -evp aes-128-cfb
|
||||
Doing aes-128-cfb for 3s on 16 size blocks: 157794127 aes-128-cfb's in 2.98s
|
||||
Doing aes-128-cfb for 3s on 64 size blocks: 39614018 aes-128-cfb's in 2.98s
|
||||
Doing aes-128-cfb for 3s on 256 size blocks: 9971090 aes-128-cfb's in 2.99s
|
||||
Doing aes-128-cfb for 3s on 1024 size blocks: 2510877 aes-128-cfb's in 2.99s
|
||||
Doing aes-128-cfb for 3s on 8192 size blocks: 310865 aes-128-cfb's in 2.98s
|
||||
OpenSSL 1.0.2p 14 Aug 2018
|
||||
built on: reproducible build, date unspecified
|
||||
options:bn(64,64) rc4(ptr,int) des(idx,cisc,16,int) aes(partial) idea(int) blowfish(idx)
|
||||
compiler: clang -I. -I.. -I../include -fPIC -fno-common -DOPENSSL_PIC -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -arch x86_64 -O3 -DL_ENDIAN -Wall -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM
|
||||
The 'numbers' are in 1000s of bytes per second processed.
|
||||
type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes
|
||||
aes-128-cfb 847216.79k 850770.86k 853712.05k 859912.39k 854565.80k
|
||||
```
|
||||
|
||||
The encrytion performance in kcptun is as fast as in openssl library(if not faster).
|
||||
其中eth0为网卡,有些服务器为ens3,有些为p2p1,通过ifconfig查询修改。
|
||||
|
||||
|
||||
#### Memory Control
|
||||
### *DSCP* :lollipop:
|
||||
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),至于有没有用,完全取决于数据包经过的设备。
|
||||
|
||||
Routers, mobile devices are susceptible to memory consumption; by setting GOGC environment(eg: GOGC=20) will make the garbage collector to recycle faster.
|
||||
Reference: https://blog.golang.org/go15gc
|
||||
通过 ```-dscp ``` 参数指定dscp值,两端可分别设定。
|
||||
|
||||
Primary memory allocation are done from a global buffer pool *xmit.Buf*, in kcp-go, when we need to allocate some bytes, we can get from that pool, and a *fixed-capacity* 1500 bytes(mtuLimit) will be returned, the *rx queue*, *tx queue* and *fec queue* all receive bytes from there, and they will return the bytes to the pool after using to prevent *unnecessary zer0ing* of bytes.
|
||||
The pool mechanism maintained a *high watermark* for slice objects, these *in-flight* objects from the pool will survive from the perodical garbage collection, meanwhile the pool kept the ability to return the memory to runtime if in idle, `-sndwnd`,`-rcvwnd`,`-ds`, `-ps`, these parameters affect this *high watermark*, the larger the value, the bigger the memory consumption will be.
|
||||
### *前向纠错* :lollipop:
|
||||
前向纠错采用Reed Solomon纠删码, 它的基本原理如下: 给定n个数据块d1, d2,…, dn,n和一个正整数m, RS根据n个数据块生成m个校验块, c1, c2,…, cm。 对于任意的n和m, 从n个原始数据块和m 个校验块中任取n块就能解码出原始数据, 即RS最多容忍m个数据块或者校验块同时丢失。
|
||||
|
||||
`-smuxbuf` also affects the maximum memory consumption, this parameter maintains a subtle balance between *concurrency* and *resource*, you can increase this value(default 4MB) to boost concurrency if you have many clients to serve and you get a powerful server at the same time, and also you can decrease this value to serve only 1 or 2 clients and hope this program can run under some embeded SoC system with limited memory and only you can access. (Notice that the `-smuxbuf` value is not proprotional to concurrency, you need to test.)
|
||||

|
||||
|
||||
通过参数```-datashard 10 -parityshard 3``` 在两端同时设定。
|
||||
|
||||
#### Compression
|
||||
|
||||
kcptun has builtin snappy algorithms for compressing streams:
|
||||
|
||||
### *Snappy数据流压缩* :lollipop:
|
||||
> 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,
|
||||
@@ -300,81 +134,82 @@ kcptun has builtin snappy algorithms for compressing streams:
|
||||
|
||||
> Reference: http://google.github.io/snappy/
|
||||
|
||||
Compression may save bandwidth for **PLAINTEXT** data, it's quite useful for specific scenarios as cross-datacenter replications, by compressing the redologs in dbms or kafka-like message queues and then transfer the data streams across the continent can be much faster.
|
||||
通过参数 ```-nocomp``` 在两端同时设定以关闭压缩。
|
||||
> 提示: 关闭压缩可能会降低延迟。
|
||||
|
||||
Compression is enabled by default, you can disable it by setting ```-nocomp``` on **BOTH** KCP Client & KCP Server **MUST** be **IDENTICAL**.
|
||||
|
||||
#### SNMP
|
||||
### *内置模式* :lollipop:
|
||||
响应速度:
|
||||
*fast3 >* ***[fast2]*** *> fast > normal > default*
|
||||
有效载荷比:
|
||||
*default > normal > fast >* ***[fast2]*** *> fast3*
|
||||
中间mode参数比较均衡,总之就是越快越浪费带宽,推荐模式 ***fast2***
|
||||
更高级的 ***手动档*** 需要理解KCP协议,并通过 ***隐藏参数*** 调整,例如:
|
||||
```
|
||||
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20
|
||||
```
|
||||
|
||||
### *SNMP* :lollipop:
|
||||
```go
|
||||
// Snmp defines network statistics indicator
|
||||
type Snmp struct {
|
||||
BytesSent uint64 // bytes sent from upper level
|
||||
BytesReceived uint64 // bytes received to upper level
|
||||
MaxConn uint64 // max number of connections ever reached
|
||||
ActiveOpens uint64 // accumulated active open connections
|
||||
PassiveOpens uint64 // accumulated passive open connections
|
||||
CurrEstab uint64 // current number of established connections
|
||||
InErrs uint64 // UDP read errors reported from net.PacketConn
|
||||
InCsumErrors uint64 // checksum errors from CRC32
|
||||
KCPInErrors uint64 // packet iput errors reported from KCP
|
||||
InPkts uint64 // incoming packets count
|
||||
OutPkts uint64 // outgoing packets count
|
||||
InSegs uint64 // incoming KCP segments
|
||||
OutSegs uint64 // outgoing KCP segments
|
||||
InBytes uint64 // UDP bytes received
|
||||
OutBytes uint64 // UDP bytes sent
|
||||
RetransSegs uint64 // accmulated retransmited segments
|
||||
FastRetransSegs uint64 // accmulated fast retransmitted segments
|
||||
EarlyRetransSegs uint64 // accmulated early retransmitted segments
|
||||
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
|
||||
FECParityShards uint64 // FEC segments received
|
||||
FECShortShards uint64 // number of data shards that's not enough for recovery
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
使用```kill -SIGUSR1 pid``` 可以在控制台打印出SNMP信息,通常用于精细调整***当前链路的有效载荷比***。
|
||||
观察```RetransSegs,FastRetransSegs,LostSegs,OutSegs```这几者的数值比例,用于参考调整```-mode manual,fec```的参数。
|
||||
|
||||
### Manual Control
|
||||
### *故障排除* :lollipop:
|
||||
> Q: 客户端和服务器端***皆无*** ```stream opened```信息。
|
||||
> A: 连接客户端程序的端口设置错误。
|
||||
|
||||
https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
|
||||
> Q: 客户端有 ```stream opened```信息,服务器端没有。
|
||||
> A: 连接服务器的端口设置错误,或者被防火墙拦截。
|
||||
|
||||
`-mode manual -nodelay 1 -interval 20 -resend 2 -nc 1`
|
||||
> Q: 客户端服务器***皆有*** ```stream opened```信息,但无法通信。
|
||||
> A: 上层软件的设定错误。
|
||||
|
||||
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.
|
||||
### *免责申明* :warning:
|
||||
用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。
|
||||
|
||||
对免责声明的解释、修改及更新权均属于作者本人所有。
|
||||
|
||||
### Identical Parmeters
|
||||
### *捐赠* :dollar:
|
||||

|
||||
|
||||
The parameters below **MUST** be **IDENTICAL** on **BOTH** side:
|
||||
对该项目的捐款将用于[gonet/2](http://gonet2.github.io/)游戏服务器框架的研发。
|
||||
|
||||
1. -key
|
||||
1. -crypt
|
||||
1. -nocomp
|
||||
1. -datashard
|
||||
1. -parityshard
|
||||
1. -smuxver
|
||||
|
||||
### References
|
||||
> 特别感谢: 郑H立, 南D风, Li, 七q, 凌J,昶,Les*ables, Ky*n等,名字已做特殊处理。
|
||||
|
||||
### *参考资料* :paperclip:
|
||||
1. https://github.com/skywind3000/kcp -- KCP - A Fast and Reliable ARQ Protocol.
|
||||
1. https://github.com/xtaci/kcp-go/ -- A Production-Grade Reliable-UDP Library for golang
|
||||
1. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go.
|
||||
1. https://en.wikipedia.org/wiki/Differentiated_services -- DSCP.
|
||||
1. http://google.github.io/snappy/ -- A fast compressor/decompressor.
|
||||
1. https://www.backblaze.com/blog/reed-solomon/ -- Reed-Solomon Explained.
|
||||
1. http://www.qualcomm.cn/products/raptorq -- RaptorQ Forward Error Correction Scheme for Object Delivery.
|
||||
1. https://en.wikipedia.org/wiki/PBKDF2 -- Key stretching.
|
||||
1. http://blog.appcanary.com/2016/encrypt-or-compress.html -- Should you encrypt or compress first?
|
||||
1. https://github.com/hashicorp/yamux -- Connection multiplexing library.
|
||||
1. https://tools.ietf.org/html/rfc6937 -- Proportional Rate Reduction for TCP.
|
||||
1. https://tools.ietf.org/html/rfc5827 -- Early Retransmit for TCP and Stream Control Transmission Protocol (SCTP).
|
||||
1. http://http2.github.io/ -- What is HTTP/2?
|
||||
1. http://www.lartc.org/ -- Linux Advanced Routing & Traffic Control
|
||||
1. https://en.wikipedia.org/wiki/Noisy-channel_coding_theorem -- Noisy channel coding theorem
|
||||
1. https://zhuanlan.zhihu.com/p/53849089 -- kcptun开发小记
|
||||
|
||||
|
||||
(注意:我没有任何社交网站的账号,请小心骗子。)
|
||||
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
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
BUILD_DIR=$(dirname "$0")/build
|
||||
mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
|
||||
sum="sha1sum"
|
||||
|
||||
if [ "$GO111MODULE" != "on" ]; then
|
||||
echo "GO111MODULE is off"
|
||||
else
|
||||
echo "GO111MODULE is on"
|
||||
fi
|
||||
|
||||
echo "Prerequisites for cross-compiling were written in build-release.sh"
|
||||
|
||||
# required library for cross-compiling
|
||||
# sudo apt-get install -y automake autogen build-essential ca-certificates gcc-5-arm-linux-gnueabi g++-5-arm-linux-gnueabi libc6-dev-armel-cross gcc-5-arm-linux-gnueabihf g++-5-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-5-aarch64-linux-gnu g++-5-aarch64-linux-gnu libc6-dev-arm64-cross gcc-5-mips-linux-gnu g++-5-mips-linux-gnu libc6-dev-mips-cross gcc-5-mipsel-linux-gnu g++-5-mipsel-linux-gnu libc6-dev-mipsel-cross gcc-5-mips64-linux-gnuabi64 g++-5-mips64-linux-gnuabi64 libc6-dev-mips64-cross gcc-5-mips64el-linux-gnuabi64 g++-5-mips64el-linux-gnuabi64 libc6-dev-mips64el-cross gcc-5-multilib g++-5-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev libtool libxml2-dev uuid-dev libssl-dev swig openjdk-8-jdk pkg-config patch make xz-utils cpio wget zip unzip p7zip git mercurial bzr texinfo help2man --no-install-recommends
|
||||
|
||||
# if error message:
|
||||
# /usr/include/linux/errno.h:1:23: fatal error: asm/errno.h: No such file or directory
|
||||
# try:
|
||||
# ln -s /usr/include/asm-generic /usr/include/asm
|
||||
|
||||
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
|
||||
if hash upx 2>/dev/null; then
|
||||
UPX=true
|
||||
fi
|
||||
|
||||
VERSION=`date -u +%Y%m%d`
|
||||
LDFLAGS="-X main.VERSION=$VERSION -s -w"
|
||||
LDFLAGS_LINUX='-X main.VERSION='$VERSION' -s -w -linkmode "external" -extldflags "-static"'
|
||||
LDFLAGS_LINUX32='-X main.VERSION='$VERSION' -s -w -linkmode "external" -extldflags "-static -m32 -L/usr/lib32"'
|
||||
echo "-ldflag for linux/amd64:" $LDFLAGS_LINUX
|
||||
echo "-ldflag for linux/386:" $LDFLAGS_LINUX32
|
||||
echo "-ldflag for other:" $LDFLAGS
|
||||
|
||||
echo "=== Building ==="
|
||||
|
||||
# 386
|
||||
OSES=(linux windows)
|
||||
for os in ${OSES[@]}; do
|
||||
suffix=""
|
||||
if [ "$os" == "windows" ]
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
|
||||
if [ "$os" == "linux" ];then
|
||||
CC=gcc-5 CGO_ENABLED=1 GOOS=$os GOARCH=386 CGO_CFLAGS="-m32 -L/usr/lib32" CGO_CXXFLAGS="-m32 -L/usr/lib32" go build -ldflags "$LDFLAGS_LINUX32" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
CC=gcc-5 CGO_ENABLED=1 GOOS=$os GOARCH=386 CGO_CFLAGS="-m32 -L/usr/lib32" CGO_CXXFLAGS="-m32 -L/usr/lib32" go build -ldflags "$LDFLAGS_LINUX32" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
else
|
||||
CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
fi
|
||||
|
||||
if $UPX; then upx -9 client_${os}_386${suffix} server_${os}_386${suffix};fi
|
||||
tar -zcf kcptun-${os}-386-$VERSION.tar.gz client_${os}_386${suffix} server_${os}_386${suffix}
|
||||
$sum kcptun-${os}-386-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
# AMD64
|
||||
OSES=(linux darwin windows freebsd)
|
||||
for os in ${OSES[@]}; do
|
||||
suffix=""
|
||||
if [ "$os" == "windows" ]
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
|
||||
if [ "$os" == "linux" ];then
|
||||
CC=gcc-5 CGO_ENABLED=1 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS_LINUX" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
CC=gcc-5 CGO_ENABLED=1 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS_LINUX" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
else
|
||||
CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
fi
|
||||
|
||||
if $UPX; then upx -9 client_${os}_amd64${suffix} server_${os}_amd64${suffix};fi
|
||||
tar -zcf kcptun-${os}-amd64-$VERSION.tar.gz client_${os}_amd64${suffix} server_${os}_amd64${suffix}
|
||||
$sum kcptun-${os}-amd64-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
# ARM-5
|
||||
#CC=arm-linux-gnueabi-gcc-5 GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5" CGO_CXXFLAGS="-march=armv5" go install std
|
||||
CC=arm-linux-gnueabi-gcc-5 CXX=arm-linux-gnueabi-g++-5 GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5" CGO_CXXFLAGS="-march=armv5" go build -ldflags "$LDFLAGS_LINUX" -o client_linux_arm5 github.com/xtaci/kcptun/client
|
||||
CC=arm-linux-gnueabi-gcc-5 CXX=arm-linux-gnueabi-g++-5 GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=1 CGO_CFLAGS="-march=armv5" CGO_CXXFLAGS="-march=armv5" go build -ldflags "$LDFLAGS_LINUX" -o server_linux_arm5 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm5 server_linux_arm5;fi
|
||||
tar -zcf kcptun-linux-arm5-$VERSION.tar.gz client_linux_arm5 server_linux_arm5
|
||||
$sum kcptun-linux-arm5-$VERSION.tar.gz
|
||||
|
||||
# ARM-6
|
||||
#CC=arm-linux-gnueabi-gcc-5 GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go install std
|
||||
CC=arm-linux-gnueabi-gcc-5 CXX=arm-linux-gnueabi-g++-5 GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go build -ldflags "$LDFLAGS_LINUX" -o client_linux_arm6 github.com/xtaci/kcptun/client
|
||||
CC=arm-linux-gnueabi-gcc-5 CXX=arm-linux-gnueabi-g++-5 GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=1 CGO_CFLAGS="-march=armv6" CGO_CXXFLAGS="-march=armv6" go build -ldflags "$LDFLAGS_LINUX" -o server_linux_arm6 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm6 server_linux_arm6;fi
|
||||
tar -zcf kcptun-linux-arm6-$VERSION.tar.gz client_linux_arm6 server_linux_arm6
|
||||
$sum kcptun-linux-arm6-$VERSION.tar.gz
|
||||
|
||||
# ARM-7
|
||||
ARMS=(7)
|
||||
#CC=arm-linux-gnueabihf-gcc-5 GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="-march=armv7-a" CGO_CXXFLAGS="-march=armv7-a" go install std
|
||||
CC=arm-linux-gnueabihf-gcc-5 CXX=arm-linux-gnueabihf-g++-5 GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="-march=armv7-a -fPIC" CGO_CXXFLAGS="-march=armv7-a -fPIC" go build -ldflags "$LDFLAGS_LINUX" -o client_linux_arm7 github.com/xtaci/kcptun/client
|
||||
CC=arm-linux-gnueabihf-gcc-5 CXX=arm-linux-gnueabihf-g++-5 GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="-march=armv7-a -fPIC" CGO_CXXFLAGS="-march=armv7-a -fPIC" go build -ldflags "$LDFLAGS_LINUX" -o server_linux_arm7 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm7 server_linux_arm7;fi
|
||||
tar -zcf kcptun-linux-arm7-$VERSION.tar.gz client_linux_arm7 server_linux_arm7
|
||||
$sum kcptun-linux-arm7-$VERSION.tar.gz
|
||||
|
||||
# ARM64
|
||||
CC=aarch64-linux-gnu-gcc-5 CXX=aarch64-linux-gnu-g++-5 GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -ldflags "$LDFLAGS_LINUX" -o client_linux_arm64 github.com/xtaci/kcptun/client
|
||||
CC=aarch64-linux-gnu-gcc-5 CXX=aarch64-linux-gnu-g++-5 GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -ldflags "$LDFLAGS_LINUX" -o server_linux_arm64 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm64 server_linux_arm64*;fi
|
||||
tar -zcf kcptun-linux-arm64-$VERSION.tar.gz client_linux_arm64 server_linux_arm64
|
||||
$sum kcptun-linux-arm64-$VERSION.tar.gz
|
||||
|
||||
#MIPS32LE
|
||||
CC=mipsel-linux-gnu-gcc-5 CXX=mipsel-linux-gnu-g++-5 GOOS=linux GOARCH=mipsle CGO_ENABLED=1 GOMIPS=softfloat go build -ldflags "$LDFLAGS_LINUX" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
CC=mipsel-linux-gnu-gcc-5 CXX=mipsel-linux-gnu-g++-5 GOOS=linux GOARCH=mipsle CGO_ENABLED=1 GOMIPS=softfloat go build -ldflags "$LDFLAGS_LINUX" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
|
||||
#MIPS32
|
||||
CC=mips-linux-gnu-gcc-5 CXX=mips-linux-gnu-g++-5 GOOS=linux GOARCH=mips CGO_ENABLED=1 GOMIPS=softfloat go build -ldflags "$LDFLAGS_LINUX" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
CC=mips-linux-gnu-gcc-5 CXX=mips-linux-gnu-g++-5 GOOS=linux GOARCH=mips CGO_ENABLED=1 GOMIPS=softfloat go build -ldflags "$LDFLAGS_LINUX" -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
|
||||
|
||||
echo "=== Building Completed ==="
|
||||
@@ -1,24 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
BUILD_DIR=$(dirname "$0")/build
|
||||
mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
|
||||
sum="sha1sum"
|
||||
|
||||
if [ "$GO111MODULE" != "on" ]; then
|
||||
echo "GO111MODULE is off"
|
||||
else
|
||||
echo "GO111MODULE is on"
|
||||
fi
|
||||
|
||||
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"
|
||||
MD5='md5sum'
|
||||
unamestr=`uname`
|
||||
if [[ "$unamestr" == 'Darwin' ]]; then
|
||||
MD5='md5'
|
||||
fi
|
||||
|
||||
UPX=false
|
||||
@@ -28,63 +12,30 @@ fi
|
||||
|
||||
VERSION=`date -u +%Y%m%d`
|
||||
LDFLAGS="-X main.VERSION=$VERSION -s -w"
|
||||
GCFLAGS=""
|
||||
|
||||
# AMD64
|
||||
OSES=(linux darwin windows freebsd)
|
||||
ARCHS=(amd64 386)
|
||||
for os in ${OSES[@]}; do
|
||||
suffix=""
|
||||
if [ "$os" == "windows" ]
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_amd64${suffix} server_${os}_amd64${suffix};fi
|
||||
tar -zcf kcptun-${os}-amd64-$VERSION.tar.gz client_${os}_amd64${suffix} server_${os}_amd64${suffix}
|
||||
$sum kcptun-${os}-amd64-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
# 386
|
||||
OSES=(linux windows)
|
||||
for os in ${OSES[@]}; do
|
||||
suffix=""
|
||||
if [ "$os" == "windows" ]
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_386${suffix} server_${os}_386${suffix};fi
|
||||
tar -zcf kcptun-${os}-386-$VERSION.tar.gz client_${os}_386${suffix} server_${os}_386${suffix}
|
||||
$sum kcptun-${os}-386-$VERSION.tar.gz
|
||||
for arch in ${ARCHS[@]}; do
|
||||
suffix=""
|
||||
if [ "$os" == "windows" ]
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -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
|
||||
done
|
||||
done
|
||||
|
||||
# ARM
|
||||
ARMS=(5 6 7)
|
||||
for v in ${ARMS[@]}; do
|
||||
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
|
||||
if $UPX; then upx -9 client_linux_arm$v server_linux_arm$v;fi
|
||||
tar -zcf kcptun-linux-arm$v-$VERSION.tar.gz client_linux_arm$v server_linux_arm$v
|
||||
$sum kcptun-linux-arm$v-$VERSION.tar.gz
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
done
|
||||
|
||||
# ARM64
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm64 github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm64 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm64 server_linux_arm64*;fi
|
||||
tar -zcf kcptun-linux-arm64-$VERSION.tar.gz client_linux_arm64 server_linux_arm64
|
||||
$sum kcptun-linux-arm64-$VERSION.tar.gz
|
||||
|
||||
#MIPS32LE
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat 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
|
||||
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
|
||||
|
||||
|
After Width: | Height: | Size: 65 KiB |
@@ -1,50 +0,0 @@
|
||||
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"`
|
||||
SmuxVer int `json:"smuxver"`
|
||||
SmuxBuf int `json:"smuxbuf"`
|
||||
StreamBuf int `json:"streambuf"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
Log string `json:"log"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
SnmpPeriod int `json:"snmpperiod"`
|
||||
Quiet bool `json:"quiet"`
|
||||
TCP bool `json:"tcp"`
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/tcpraw"
|
||||
)
|
||||
|
||||
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
|
||||
if config.TCP {
|
||||
conn, err := tcpraw.Dial("tcp", config.RemoteAddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tcpraw.Dial()")
|
||||
}
|
||||
return kcp.NewConn(config.RemoteAddr, block, config.DataShard, config.ParityShard, conn)
|
||||
}
|
||||
return kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
|
||||
}
|
||||
@@ -2,110 +2,94 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/golang/snappy"
|
||||
"github.com/hashicorp/yamux"
|
||||
"github.com/urfave/cli"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/kcptun/generic"
|
||||
"github.com/xtaci/smux"
|
||||
smuxv2 "github.com/xtaci/smux/v2"
|
||||
)
|
||||
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
const SALT = "kcp-go"
|
||||
var (
|
||||
// VERSION is injected by buildflags
|
||||
VERSION = "SELFBUILD"
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
// maximum supported smux version
|
||||
const maxSmuxVer = 2
|
||||
type compStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
// VERSION is injected by buildflags
|
||||
var VERSION = "SELFBUILD"
|
||||
func (c *compStream) Read(p []byte) (n int, err error) {
|
||||
return c.r.Read(p)
|
||||
}
|
||||
|
||||
// A pool for stream copying
|
||||
var xmitBuf sync.Pool
|
||||
func (c *compStream) Write(p []byte) (n int, err error) {
|
||||
n, err = c.w.Write(p)
|
||||
err = c.w.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func handleClient(mux generic.Mux, p1 net.Conn, quiet bool) {
|
||||
logln := func(v ...interface{}) {
|
||||
if !quiet {
|
||||
log.Println(v...)
|
||||
}
|
||||
}
|
||||
func (c *compStream) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func newCompStream(conn net.Conn) *compStream {
|
||||
c := new(compStream)
|
||||
c.conn = conn
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
log.Println("stream opened")
|
||||
defer log.Println("stream closed")
|
||||
defer p1.Close()
|
||||
p2, err := mux.Open()
|
||||
if err != nil {
|
||||
logln(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer p2.Close()
|
||||
|
||||
if s2, ok := p2.(generic.Stream); ok {
|
||||
logln("stream opened", "in:", p1.RemoteAddr(), "out:", fmt.Sprint(s2.RemoteAddr(), "(", s2.ID(), ")"))
|
||||
defer logln("stream closed", "in:", p1.RemoteAddr(), "out:", fmt.Sprint(s2.RemoteAddr(), "(", s2.ID(), ")"))
|
||||
}
|
||||
// start tunnel
|
||||
p1die := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(p1, p2)
|
||||
close(p1die)
|
||||
}()
|
||||
|
||||
// start tunnel & wait for tunnel termination
|
||||
streamCopy := func(dst io.Writer, src io.ReadCloser) chan struct{} {
|
||||
die := make(chan struct{})
|
||||
go func() {
|
||||
buf := xmitBuf.Get().([]byte)
|
||||
if _, err := generic.CopyBuffer(dst, src, buf); err != nil {
|
||||
if s2, ok := p2.(generic.Stream); ok {
|
||||
// verbose error handling
|
||||
cause := err
|
||||
if e, ok := err.(interface{ Cause() error }); ok {
|
||||
cause = e.Cause()
|
||||
}
|
||||
|
||||
switch cause {
|
||||
case smux.ErrInvalidProtocol:
|
||||
log.Println("smux version:1", err, "in:", p1.RemoteAddr(), "out:", fmt.Sprint(s2.RemoteAddr(), "(", s2.ID(), ")"))
|
||||
case smuxv2.ErrInvalidProtocol:
|
||||
log.Println("smux version:2", err, "in:", p1.RemoteAddr(), "out:", fmt.Sprint(s2.RemoteAddr(), "(", s2.ID(), ")"))
|
||||
}
|
||||
}
|
||||
}
|
||||
xmitBuf.Put(buf)
|
||||
close(die)
|
||||
}()
|
||||
return die
|
||||
}
|
||||
p2die := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(p2, p1)
|
||||
close(p2die)
|
||||
}()
|
||||
|
||||
// wait for tunnel termination
|
||||
select {
|
||||
case <-streamCopy(p1, p2):
|
||||
case <-streamCopy(p2, p1):
|
||||
case <-p1die:
|
||||
case <-p2die:
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Printf("%+v\n", err)
|
||||
log.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
if VERSION == "SELFBUILD" {
|
||||
// add more log flags for debugging
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
xmitBuf.New = func() interface{} {
|
||||
return make([]byte, 32768)
|
||||
}
|
||||
|
||||
myApp := cli.NewApp()
|
||||
myApp.Name = "kcptun"
|
||||
myApp.Usage = "client(with SMUX)"
|
||||
myApp.Usage = "kcptun client"
|
||||
myApp.Version = VERSION
|
||||
myApp.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@@ -121,38 +105,28 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "it's a secrect",
|
||||
Usage: "pre-shared secret between client and server",
|
||||
Usage: "key for communcation, must be the same as kcptun server",
|
||||
EnvVar: "KCPTUN_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none",
|
||||
Usage: "methods for encryption: aes, tea, xor, none",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
Value: "fast",
|
||||
Usage: "profiles: fast3, fast2, fast, normal, manual",
|
||||
Usage: "mode for communication: fast3, fast2, fast, normal",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "conn",
|
||||
Value: 1,
|
||||
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",
|
||||
Usage: "establish N physical connections as specified by 'conn' to server",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
Value: 1350,
|
||||
Usage: "set maximum transmission unit for UDP packets",
|
||||
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sndwnd",
|
||||
@@ -161,33 +135,33 @@ func main() {
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "rcvwnd",
|
||||
Value: 512,
|
||||
Value: 1024,
|
||||
Usage: "set receive window size(num of packets)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "datashard,ds",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard,ps",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
Value: 0,
|
||||
Usage: "set DSCP(6bit)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "datashard",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
Value: 0,
|
||||
Usage: "set DSCP(6bit)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "nodelay",
|
||||
Value: 0,
|
||||
@@ -195,7 +169,7 @@ func main() {
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "interval",
|
||||
Value: 50,
|
||||
Value: 40,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
@@ -209,322 +183,132 @@ func main() {
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sockbuf",
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Usage: "per-socket buffer in bytes",
|
||||
Name: "sockbuf",
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxver",
|
||||
Value: 1,
|
||||
Usage: "specify smux version, available 1,2",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxbuf",
|
||||
Value: 4194304,
|
||||
Usage: "the overall de-mux buffer in bytes",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "streambuf",
|
||||
Value: 2097152,
|
||||
Usage: "per stream receive buffer in bytes, smux v2+",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
Usage: "seconds between heartbeats",
|
||||
},
|
||||
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.StringFlag{
|
||||
Name: "log",
|
||||
Value: "",
|
||||
Usage: "specify a log file to output, default goes to stderr",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "to suppress the 'stream open/close' messages",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tcp",
|
||||
Usage: "to emulate a TCP connection(linux)",
|
||||
},
|
||||
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",
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
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.SmuxBuf = c.Int("smuxbuf")
|
||||
config.StreamBuf = c.Int("streambuf")
|
||||
config.SmuxVer = c.Int("smuxver")
|
||||
config.KeepAlive = c.Int("keepalive")
|
||||
config.Log = c.String("log")
|
||||
config.SnmpLog = c.String("snmplog")
|
||||
config.SnmpPeriod = c.Int("snmpperiod")
|
||||
config.Quiet = c.Bool("quiet")
|
||||
config.TCP = c.Bool("tcp")
|
||||
|
||||
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, 40, 2, 1
|
||||
case "fast":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 30, 2, 1
|
||||
case "fast2":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1
|
||||
case "fast3":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 10, 2, 1
|
||||
}
|
||||
|
||||
log.Println("version:", VERSION)
|
||||
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
|
||||
addr, err := net.ResolveTCPAddr("tcp", c.String("localaddr"))
|
||||
checkError(err)
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
checkError(err)
|
||||
|
||||
log.Println("smux version:", config.SmuxVer)
|
||||
log.Println("listening on:", listener.Addr())
|
||||
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("smuxbuf:", config.SmuxBuf)
|
||||
log.Println("streambuf:", config.StreamBuf)
|
||||
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("quiet:", config.Quiet)
|
||||
log.Println("tcp:", config.TCP)
|
||||
// kcp server
|
||||
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
|
||||
|
||||
// parameters check
|
||||
if config.SmuxVer > maxSmuxVer {
|
||||
log.Fatal("unsupported smux version:", config.SmuxVer)
|
||||
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
|
||||
}
|
||||
|
||||
log.Println("initiating key derivation")
|
||||
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
|
||||
log.Println("key derivation done")
|
||||
crypt := c.String("crypt")
|
||||
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
||||
var block kcp.BlockCrypt
|
||||
switch config.Crypt {
|
||||
case "sm4":
|
||||
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
|
||||
switch c.String("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)
|
||||
}
|
||||
|
||||
createConn := func() (generic.Mux, error) {
|
||||
kcpconn, err := dial(&config, block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "dial()")
|
||||
}
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetWriteDelay(false)
|
||||
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
kcpconn.SetMtu(config.MTU)
|
||||
kcpconn.SetACKNoDelay(config.AckNodelay)
|
||||
remoteaddr := c.String("remoteaddr")
|
||||
datashard, parityshard := c.Int("datashard"), c.Int("parityshard")
|
||||
mtu, sndwnd, rcvwnd := c.Int("mtu"), c.Int("sndwnd"), c.Int("rcvwnd")
|
||||
nocomp, acknodelay := c.Bool("nocomp"), c.Bool("acknodelay")
|
||||
dscp, sockbuf, keepalive, conn := c.Int("dscp"), c.Int("sockbuf"), c.Int("keepalive"), c.Int("conn")
|
||||
|
||||
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
|
||||
log.Println("listening on:", listener.Addr())
|
||||
log.Println("encryption:", crypt)
|
||||
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
|
||||
log.Println("remote address:", remoteaddr)
|
||||
log.Println("sndwnd:", sndwnd, "rcvwnd:", rcvwnd)
|
||||
log.Println("compression:", !nocomp)
|
||||
log.Println("mtu:", mtu)
|
||||
log.Println("datashard:", datashard, "parityshard:", parityshard)
|
||||
log.Println("acknodelay:", acknodelay)
|
||||
log.Println("dscp:", dscp)
|
||||
log.Println("sockbuf:", sockbuf)
|
||||
log.Println("keepalive:", keepalive)
|
||||
log.Println("conn:", conn)
|
||||
|
||||
createConn := func() *yamux.Session {
|
||||
kcpconn, err := kcp.DialWithOptions(remoteaddr, block, datashard, parityshard)
|
||||
checkError(err)
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetNoDelay(nodelay, interval, resend, nc)
|
||||
kcpconn.SetWindowSize(sndwnd, rcvwnd)
|
||||
kcpconn.SetMtu(mtu)
|
||||
kcpconn.SetACKNoDelay(acknodelay)
|
||||
kcpconn.SetKeepAlive(keepalive)
|
||||
|
||||
if err := kcpconn.SetDSCP(dscp); err != nil {
|
||||
log.Println("SetDSCP:", err)
|
||||
}
|
||||
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
|
||||
if err := kcpconn.SetReadBuffer(sockbuf); err != nil {
|
||||
log.Println("SetReadBuffer:", err)
|
||||
}
|
||||
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
if err := kcpconn.SetWriteBuffer(sockbuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
}
|
||||
log.Println("smux version:", config.SmuxVer, "on connection:", kcpconn.LocalAddr(), "->", kcpconn.RemoteAddr())
|
||||
switch config.SmuxVer {
|
||||
case 1:
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
// stream multiplex
|
||||
var session *smux.Session
|
||||
if config.NoComp {
|
||||
session, err = smux.Client(kcpconn, smuxConfig)
|
||||
} else {
|
||||
session, err = smux.Client(generic.NewCompStream(kcpconn), smuxConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "createConn()")
|
||||
}
|
||||
return session, nil
|
||||
case 2:
|
||||
smuxConfig := smuxv2.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.MaxStreamBuffer = config.StreamBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
// stream multiplex
|
||||
var session *smuxv2.Session
|
||||
if config.NoComp {
|
||||
session, err = smuxv2.Client(kcpconn, smuxConfig)
|
||||
} else {
|
||||
session, err = smuxv2.Client(generic.NewCompStream(kcpconn), smuxConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "createConn()")
|
||||
}
|
||||
return session, nil
|
||||
default:
|
||||
panic("incorrect smux version")
|
||||
// stream multiplex
|
||||
config := &yamux.Config{
|
||||
AcceptBacklog: 256,
|
||||
EnableKeepAlive: true,
|
||||
KeepAliveInterval: 30 * time.Second,
|
||||
ConnectionWriteTimeout: 30 * time.Second,
|
||||
MaxStreamWindowSize: uint32(sockbuf),
|
||||
LogOutput: os.Stderr,
|
||||
}
|
||||
}
|
||||
|
||||
// wait until a connection is ready
|
||||
waitConn := func() generic.Mux {
|
||||
for {
|
||||
if session, err := createConn(); err == nil {
|
||||
return session
|
||||
} else {
|
||||
log.Println("re-connecting:", err)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
var session *yamux.Session
|
||||
if nocomp {
|
||||
session, err = yamux.Client(kcpconn, config)
|
||||
} else {
|
||||
session, err = yamux.Client(newCompStream(kcpconn), config)
|
||||
}
|
||||
checkError(err)
|
||||
return session
|
||||
}
|
||||
|
||||
numconn := uint16(config.Conn)
|
||||
muxes := make([]struct {
|
||||
session generic.Mux
|
||||
ttl time.Time
|
||||
}, numconn)
|
||||
|
||||
for k := range muxes {
|
||||
muxes[k].session = waitConn()
|
||||
muxes[k].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
numconn := uint16(conn)
|
||||
var muxes []*yamux.Session
|
||||
for i := uint16(0); i < numconn; i++ {
|
||||
muxes = append(muxes, createConn())
|
||||
}
|
||||
|
||||
chScavenger := make(chan generic.Mux, 128)
|
||||
go scavenger(chScavenger, config.ScavengeTTL)
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
rr := uint16(0)
|
||||
for {
|
||||
p1, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
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
|
||||
}
|
||||
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, config.Quiet)
|
||||
go handleClient(p1, p2)
|
||||
rr++
|
||||
}
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
type scavengeSession struct {
|
||||
session generic.Mux
|
||||
ts time.Time
|
||||
}
|
||||
|
||||
func scavenger(ch chan generic.Mux, 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", sess.RemoteAddr())
|
||||
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.RemoteAddr())
|
||||
s.session.Close()
|
||||
} else if ttl >= 0 && time.Since(s.ts) >= time.Duration(ttl)*time.Second {
|
||||
log.Println("session reached scavenge ttl", s.session.RemoteAddr())
|
||||
s.session.Close()
|
||||
} else {
|
||||
newList = append(newList, sessionList[k])
|
||||
}
|
||||
}
|
||||
sessionList = newList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ func init() {
|
||||
func sigHandler() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR1)
|
||||
signal.Ignore(syscall.SIGPIPE)
|
||||
|
||||
for {
|
||||
switch <-ch {
|
||||
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"localaddr": ":2000",
|
||||
"remoteaddr": "11.22.33.44:2000",
|
||||
"key": "PASSWORD",
|
||||
"crypt": "aes-128",
|
||||
"mode": "fast3",
|
||||
"mtu": 1400,
|
||||
"sndwnd": 128,
|
||||
"rcvwnd": 1024,
|
||||
"datashard": 10,
|
||||
"parityshard": 3,
|
||||
"dscp": 46,
|
||||
"nocomp": true,
|
||||
"acknodelay": false,
|
||||
"nodelay": 1,
|
||||
"interval": 40,
|
||||
"resend": 2,
|
||||
"nc": 1,
|
||||
"sockbuf": 16777217,
|
||||
"smuxver": 1,
|
||||
"smuxbuf": 16777217,
|
||||
"streambuf": 2097152,
|
||||
"keepalive": 10,
|
||||
"autoexpire": 1800,
|
||||
"quiet": false,
|
||||
"tcp": false
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"listen": ":2000",
|
||||
"target": "127.0.0.1:9999",
|
||||
"key": "PASSWORD",
|
||||
"crypt": "aes-128",
|
||||
"mode": "fast3",
|
||||
"mtu": 1400,
|
||||
"sndwnd": 2048,
|
||||
"rcvwnd": 2048,
|
||||
"datashard": 10,
|
||||
"parityshard": 3,
|
||||
"dscp": 46,
|
||||
"nocomp": true,
|
||||
"acknodelay": false,
|
||||
"nodelay": 1,
|
||||
"interval": 40,
|
||||
"resend": 2,
|
||||
"nc": 1,
|
||||
"sockbuf": 16777217,
|
||||
"smuxver": 1,
|
||||
"smuxbuf": 16777217,
|
||||
"streambuf": 2097152,
|
||||
"keepalive": 10,
|
||||
"pprof":false,
|
||||
"quiet":false,
|
||||
"tcp":false
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CompStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
func (c *CompStream) Read(p []byte) (n int, err error) {
|
||||
return c.r.Read(p)
|
||||
}
|
||||
|
||||
func (c *CompStream) Write(p []byte) (n int, err error) {
|
||||
if _, err := c.w.Write(p); err != nil {
|
||||
return 0, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if err := c.w.Flush(); err != nil {
|
||||
return 0, errors.WithStack(err)
|
||||
}
|
||||
return len(p), err
|
||||
}
|
||||
|
||||
func (c *CompStream) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *CompStream) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *CompStream) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *CompStream) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *CompStream) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *CompStream) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func NewCompStream(conn net.Conn) *CompStream {
|
||||
c := new(CompStream)
|
||||
c.conn = conn
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package generic
|
||||
|
||||
import "io"
|
||||
|
||||
// io.CopyBuffer has extra tests for interface like io.ReaderFrom and io.WriterTo
|
||||
// which is not efficient in memory management from tests
|
||||
func CopyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
|
||||
if buf != nil && len(buf) == 0 {
|
||||
panic("empty buffer in copyBuffer")
|
||||
}
|
||||
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Mux interface {
|
||||
Open() (io.ReadWriteCloser, error)
|
||||
Accept() (io.ReadWriteCloser, error)
|
||||
IsClosed() bool
|
||||
NumStreams() int
|
||||
RemoteAddr() net.Addr
|
||||
Close() error
|
||||
}
|
||||
|
||||
type Stream interface {
|
||||
io.ReadWriteCloser
|
||||
ID() uint32
|
||||
RemoteAddr() net.Addr
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
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:
|
||||
// split path into dirname and filename
|
||||
logdir, logfile := filepath.Split(path)
|
||||
// only format logfile
|
||||
f, err := os.OpenFile(logdir+time.Now().Format(logfile), 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,24 +0,0 @@
|
||||
module github.com/xtaci/kcptun
|
||||
|
||||
require (
|
||||
github.com/coreos/go-iptables v0.4.2 // indirect
|
||||
github.com/golang/snappy v0.0.1
|
||||
github.com/google/gopacket v1.1.17 // indirect
|
||||
github.com/klauspost/cpuid v1.2.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.2 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
||||
github.com/tjfoc/gmsm v1.0.1 // indirect
|
||||
github.com/urfave/cli v1.21.0
|
||||
github.com/xtaci/kcp-go v5.4.19+incompatible
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
|
||||
github.com/xtaci/smux v1.4.4
|
||||
github.com/xtaci/smux/v2 v2.0.11
|
||||
github.com/xtaci/tcpraw v1.2.25
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83
|
||||
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b // indirect
|
||||
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b // indirect
|
||||
)
|
||||
|
||||
go 1.13
|
||||
@@ -1,57 +0,0 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
|
||||
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
|
||||
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/reedsolomon v1.9.2 h1:E9CMS2Pqbv+C7tsrYad4YC9MfhnMVWhMRsTi7U0UB18=
|
||||
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
|
||||
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
|
||||
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
|
||||
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
|
||||
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
|
||||
github.com/xtaci/kcp-go v5.4.13+incompatible h1:s6ba2XTw8lAj+s6AQNob25dCvWDgwE+U1QpEVBUoYy8=
|
||||
github.com/xtaci/kcp-go v5.4.13+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.14+incompatible h1:kQZr/ngKQtYrgXSUxwF4A59mTMzUp0BDmtWIRuXYoqg=
|
||||
github.com/xtaci/kcp-go v5.4.14+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.15+incompatible h1:QLDulPaKjT4k4cGeviyC1mt00gwJ3r5epx8yCw6ACEc=
|
||||
github.com/xtaci/kcp-go v5.4.15+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.16+incompatible h1:/L7UP4P4H/oXpMnrb2W9oOxCMVpjPi3FJeMJmgN+SUE=
|
||||
github.com/xtaci/kcp-go v5.4.16+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.17+incompatible h1:RudP76JCx062JSxPxSjBl+457+fS0M7T8zEZPLpC0o8=
|
||||
github.com/xtaci/kcp-go v5.4.17+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.18+incompatible h1:zxzRP8V54vhJ8QAKEjf1b9g96R01prybCRchx6rEmtg=
|
||||
github.com/xtaci/kcp-go v5.4.18+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go v5.4.19+incompatible h1:vv7Ar1D9WZGiv6deIOluxrC26Oin/2jFtx8sFU5tlvw=
|
||||
github.com/xtaci/kcp-go v5.4.19+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
github.com/xtaci/smux v1.4.4 h1:FukIfahko+KHhS9Gxppkp6756opZymvPOLNmpny1is4=
|
||||
github.com/xtaci/smux v1.4.4/go.mod h1:LuA3S0xssf4fmGRJ7ow3EehgmDUzib4EcobaFNKvlMA=
|
||||
github.com/xtaci/smux/v2 v2.0.11 h1:thVWmgGRciZ8iaATwpY2B/51aHzmMI6wrF7DfcJSckU=
|
||||
github.com/xtaci/smux/v2 v2.0.11/go.mod h1:Iqy5a3Gax2p7WCKHOHkSNo/COthNFXd3/vqrcKNtzqI=
|
||||
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
|
||||
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83 h1:mgAKeshyNqWKdENOnQsg+8dRTwZFIwFaO3HNl52sweA=
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
|
||||
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI=
|
||||
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 62 KiB |
@@ -1,48 +0,0 @@
|
||||
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"`
|
||||
SmuxBuf int `json:"smuxbuf"`
|
||||
StreamBuf int `json:"streambuf"`
|
||||
SmuxVer int `json:"smuxver"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
Log string `json:"log"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
SnmpPeriod int `json:"snmpperiod"`
|
||||
Pprof bool `json:"pprof"`
|
||||
Quiet bool `json:"quiet"`
|
||||
TCP bool `json:"tcp"`
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -2,174 +2,114 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"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"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/kcptun/generic"
|
||||
"github.com/xtaci/smux"
|
||||
smuxv2 "github.com/xtaci/smux/v2"
|
||||
"github.com/xtaci/tcpraw"
|
||||
)
|
||||
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
const SALT = "kcp-go"
|
||||
var (
|
||||
// VERSION is injected by buildflags
|
||||
VERSION = "SELFBUILD"
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
// maximum supported smux version
|
||||
const maxSmuxVer = 2
|
||||
type compStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
// VERSION is injected by buildflags
|
||||
var VERSION = "SELFBUILD"
|
||||
func (c *compStream) Read(p []byte) (n int, err error) {
|
||||
return c.r.Read(p)
|
||||
}
|
||||
|
||||
// A pool for stream copying
|
||||
var xmitBuf sync.Pool
|
||||
func (c *compStream) Write(p []byte) (n int, err error) {
|
||||
n, err = c.w.Write(p)
|
||||
err = c.w.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *compStream) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func newCompStream(conn net.Conn) *compStream {
|
||||
c := new(compStream)
|
||||
c.conn = conn
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
// handle multiplex-ed connection
|
||||
func handleMux(conn net.Conn, config *Config) {
|
||||
// check if target is unix domain socket
|
||||
var isUnix bool
|
||||
if _, _, err := net.SplitHostPort(config.Target); err != nil {
|
||||
isUnix = true
|
||||
}
|
||||
log.Println("smux version:", config.SmuxVer, "on connection:", conn.LocalAddr(), "->", conn.RemoteAddr())
|
||||
|
||||
func handleMux(conn io.ReadWriteCloser, target string, config *yamux.Config) {
|
||||
// stream multiplex
|
||||
var muxer generic.Mux
|
||||
switch config.SmuxVer {
|
||||
case 1:
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
mux, err := smux.Server(conn, smuxConfig)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer mux.Close()
|
||||
muxer = mux
|
||||
case 2:
|
||||
smuxConfig := smuxv2.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||
smuxConfig.MaxStreamBuffer = config.StreamBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
mux, err := smuxv2.Server(conn, smuxConfig)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer mux.Close()
|
||||
muxer = mux
|
||||
default:
|
||||
panic("incorrect smux version")
|
||||
var mux *yamux.Session
|
||||
m, err := yamux.Server(conn, config)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
mux = m
|
||||
defer mux.Close()
|
||||
|
||||
for {
|
||||
stream, err := muxer.Accept()
|
||||
p1, err := mux.Accept()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
go func(p1 io.ReadWriteCloser) {
|
||||
var p2 net.Conn
|
||||
var err error
|
||||
if !isUnix {
|
||||
p2, err = net.Dial("tcp", config.Target)
|
||||
} else {
|
||||
p2, err = net.Dial("unix", config.Target)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
p1.Close()
|
||||
return
|
||||
}
|
||||
handleClient(p1, p2, config.Quiet)
|
||||
}(stream)
|
||||
p2, err := net.DialTimeout("tcp", target, 5*time.Second)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
go handleClient(p1, p2)
|
||||
}
|
||||
}
|
||||
|
||||
func handleClient(p1 io.ReadWriteCloser, p2 net.Conn, quiet bool) {
|
||||
logln := func(v ...interface{}) {
|
||||
if !quiet {
|
||||
log.Println(v...)
|
||||
}
|
||||
}
|
||||
|
||||
func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
log.Println("stream opened")
|
||||
defer log.Println("stream closed")
|
||||
defer p1.Close()
|
||||
defer p2.Close()
|
||||
|
||||
if s1, ok := p1.(generic.Stream); ok {
|
||||
logln("stream opened", "in:", fmt.Sprint(s1.RemoteAddr(), "(", s1.ID(), ")"), "out:", p2.RemoteAddr())
|
||||
defer logln("stream closed", "in:", fmt.Sprint(s1.RemoteAddr(), "(", s1.ID(), ")"), "out:", p2.RemoteAddr())
|
||||
}
|
||||
// start tunnel
|
||||
p1die := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(p1, p2)
|
||||
close(p1die)
|
||||
}()
|
||||
|
||||
// start tunnel & wait for tunnel termination
|
||||
streamCopy := func(dst io.Writer, src io.ReadCloser) chan struct{} {
|
||||
die := make(chan struct{})
|
||||
go func() {
|
||||
buf := xmitBuf.Get().([]byte)
|
||||
if _, err := generic.CopyBuffer(dst, src, buf); err != nil {
|
||||
if s1, ok := p1.(generic.Stream); ok {
|
||||
// verbose error handling
|
||||
cause := err
|
||||
if e, ok := err.(interface{ Cause() error }); ok {
|
||||
cause = e.Cause()
|
||||
}
|
||||
|
||||
switch cause {
|
||||
case smux.ErrInvalidProtocol:
|
||||
log.Println("smux version:1", err, "in:", fmt.Sprint(s1.RemoteAddr(), "(", s1.ID(), ")"), "out:", p2.RemoteAddr())
|
||||
case smuxv2.ErrInvalidProtocol:
|
||||
log.Println("smux version:2", err, "in:", fmt.Sprint(s1.RemoteAddr(), "(", s1.ID(), ")"), "out:", p2.RemoteAddr())
|
||||
}
|
||||
}
|
||||
}
|
||||
xmitBuf.Put(buf)
|
||||
close(die)
|
||||
}()
|
||||
return die
|
||||
}
|
||||
p2die := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(p2, p1)
|
||||
close(p2die)
|
||||
}()
|
||||
|
||||
// wait for tunnel termination
|
||||
select {
|
||||
case <-streamCopy(p1, p2):
|
||||
case <-streamCopy(p2, p1):
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Printf("%+v\n", err)
|
||||
os.Exit(-1)
|
||||
case <-p1die:
|
||||
case <-p2die:
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
if VERSION == "SELFBUILD" {
|
||||
// add more log flags for debugging
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
xmitBuf.New = func() interface{} {
|
||||
return make([]byte, 32768)
|
||||
}
|
||||
|
||||
myApp := cli.NewApp()
|
||||
myApp.Name = "kcptun"
|
||||
myApp.Usage = "server(with SMUX)"
|
||||
myApp.Usage = "kcptun server"
|
||||
myApp.Version = VERSION
|
||||
myApp.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@@ -180,28 +120,28 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "target, t",
|
||||
Value: "127.0.0.1:12948",
|
||||
Usage: "target server address, or path/to/unix_socket",
|
||||
Usage: "target server address",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "it's a secrect",
|
||||
Usage: "pre-shared secret between client and server",
|
||||
Usage: "key for communcation, must be the same as kcptun client",
|
||||
EnvVar: "KCPTUN_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none",
|
||||
Usage: "methods for encryption: aes, tea, xor, none",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
Value: "fast",
|
||||
Usage: "profiles: fast3, fast2, fast, normal, manual",
|
||||
Usage: "mode for communication: fast3, fast2, fast, normal",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
Value: 1350,
|
||||
Usage: "set maximum transmission unit for UDP packets",
|
||||
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sndwnd",
|
||||
@@ -213,30 +153,30 @@ func main() {
|
||||
Value: 1024,
|
||||
Usage: "set receive window size(num of packets)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "datashard,ds",
|
||||
Name: "datashard",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard,ps",
|
||||
Name: "parityshard",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
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: "dscp",
|
||||
Value: 0,
|
||||
Usage: "set DSCP(6bit)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "nodelay",
|
||||
Value: 0,
|
||||
@@ -244,7 +184,7 @@ func main() {
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "interval",
|
||||
Value: 50,
|
||||
Value: 40,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
@@ -258,238 +198,103 @@ func main() {
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sockbuf",
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Usage: "per-socket buffer in bytes",
|
||||
Name: "sockbuf",
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxver",
|
||||
Value: 1,
|
||||
Usage: "specify smux version, available 1,2",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "smuxbuf",
|
||||
Value: 4194304,
|
||||
Usage: "the overall de-mux buffer in bytes",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "streambuf",
|
||||
Value: 2097152,
|
||||
Usage: "per stream receive buffer in bytes, smux v2+",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
Usage: "seconds between heartbeats",
|
||||
},
|
||||
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.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "to suppress the 'stream open/close' messages",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tcp",
|
||||
Usage: "to emulate a TCP connection(linux)",
|
||||
},
|
||||
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",
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
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.SmuxBuf = c.Int("smuxbuf")
|
||||
config.StreamBuf = c.Int("streambuf")
|
||||
config.SmuxVer = c.Int("smuxver")
|
||||
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")
|
||||
config.Quiet = c.Bool("quiet")
|
||||
config.TCP = c.Bool("tcp")
|
||||
|
||||
if c.String("c") != "" {
|
||||
//Now only support json config file
|
||||
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, 40, 2, 1
|
||||
case "fast":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 30, 2, 1
|
||||
case "fast2":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1
|
||||
case "fast3":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 10, 2, 1
|
||||
}
|
||||
|
||||
log.Println("version:", VERSION)
|
||||
log.Println("smux version:", config.SmuxVer)
|
||||
log.Println("listening on:", config.Listen)
|
||||
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("smuxbuf:", config.SmuxBuf)
|
||||
log.Println("streambuf:", config.StreamBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
log.Println("pprof:", config.Pprof)
|
||||
log.Println("quiet:", config.Quiet)
|
||||
log.Println("tcp:", config.TCP)
|
||||
|
||||
// parameters check
|
||||
if config.SmuxVer > maxSmuxVer {
|
||||
log.Fatal("unsupported smux version:", config.SmuxVer)
|
||||
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
|
||||
}
|
||||
|
||||
log.Println("initiating key derivation")
|
||||
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
|
||||
log.Println("key derivation done")
|
||||
crypt := c.String("crypt")
|
||||
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
||||
var block kcp.BlockCrypt
|
||||
switch config.Crypt {
|
||||
case "sm4":
|
||||
block, _ = kcp.NewSM4BlockCrypt(pass[:16])
|
||||
switch 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)
|
||||
}
|
||||
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
if config.Pprof {
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
datashard, parityshard := c.Int("datashard"), c.Int("parityshard")
|
||||
lis, err := kcp.ListenWithOptions(c.String("listen"), block, datashard, parityshard)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// main loop
|
||||
var wg sync.WaitGroup
|
||||
loop := func(lis *kcp.Listener) {
|
||||
defer wg.Done()
|
||||
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)
|
||||
}
|
||||
mtu, sndwnd, rcvwnd := c.Int("mtu"), c.Int("sndwnd"), c.Int("rcvwnd")
|
||||
nocomp, acknodelay := c.Bool("nocomp"), c.Bool("acknodelay")
|
||||
dscp, sockbuf, keepalive := c.Int("dscp"), c.Int("sockbuf"), c.Int("keepalive")
|
||||
target := c.String("target")
|
||||
|
||||
for {
|
||||
if conn, err := lis.AcceptKCP(); err == nil {
|
||||
log.Println("remote address:", conn.RemoteAddr())
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetWriteDelay(false)
|
||||
conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
conn.SetMtu(config.MTU)
|
||||
conn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
conn.SetACKNoDelay(config.AckNodelay)
|
||||
log.Println("listening on ", lis.Addr())
|
||||
log.Println("encryption:", crypt)
|
||||
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
|
||||
log.Println("sndwnd:", sndwnd, "rcvwnd:", rcvwnd)
|
||||
log.Println("compression:", !nocomp)
|
||||
log.Println("mtu:", mtu)
|
||||
log.Println("datashard:", datashard, "parityshard:", parityshard)
|
||||
log.Println("acknodelay:", acknodelay)
|
||||
log.Println("dscp:", dscp)
|
||||
log.Println("sockbuf:", sockbuf)
|
||||
log.Println("keepalive:", keepalive)
|
||||
|
||||
if config.NoComp {
|
||||
go handleMux(conn, &config)
|
||||
} else {
|
||||
go handleMux(generic.NewCompStream(conn), &config)
|
||||
}
|
||||
if err := lis.SetDSCP(dscp); err != nil {
|
||||
log.Println("SetDSCP:", err)
|
||||
}
|
||||
if err := lis.SetReadBuffer(sockbuf); err != nil {
|
||||
log.Println("SetReadBuffer:", err)
|
||||
}
|
||||
if err := lis.SetWriteBuffer(sockbuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
}
|
||||
config := &yamux.Config{
|
||||
AcceptBacklog: 256,
|
||||
EnableKeepAlive: true,
|
||||
KeepAliveInterval: 30 * time.Second,
|
||||
ConnectionWriteTimeout: 30 * time.Second,
|
||||
MaxStreamWindowSize: uint32(sockbuf),
|
||||
LogOutput: os.Stderr,
|
||||
}
|
||||
for {
|
||||
if conn, err := lis.Accept(); err == nil {
|
||||
log.Println("remote address:", conn.RemoteAddr())
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetNoDelay(nodelay, interval, resend, nc)
|
||||
conn.SetMtu(mtu)
|
||||
conn.SetWindowSize(sndwnd, rcvwnd)
|
||||
conn.SetACKNoDelay(acknodelay)
|
||||
conn.SetKeepAlive(keepalive)
|
||||
|
||||
if nocomp {
|
||||
go handleMux(conn, target, config)
|
||||
} else {
|
||||
log.Printf("%+v", err)
|
||||
go handleMux(newCompStream(conn), target, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if config.TCP { // tcp dual stack
|
||||
if conn, err := tcpraw.Listen("tcp", config.Listen); err == nil {
|
||||
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// udp stack
|
||||
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ func init() {
|
||||
func sigHandler() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR1)
|
||||
signal.Ignore(syscall.SIGPIPE)
|
||||
|
||||
for {
|
||||
switch <-ch {
|
||||
|
||||