Compare commits
336 Commits
20170809.0
...
place_hold
Author | SHA1 | Date | |
---|---|---|---|
|
abe55c02d5 | ||
|
f96e8c9a94 | ||
|
4d3b713b05 | ||
|
5f0a16cd1f | ||
|
a8ed6c8b5e | ||
|
eaa0e2b54f | ||
|
6a069f7dc2 | ||
|
0348fcedf6 | ||
|
4a4b3dc693 | ||
|
9336246f00 | ||
|
a1d5a3805a | ||
|
baebad1e93 | ||
|
9a23775dfa | ||
|
daab328cf2 | ||
|
0e3dee9920 | ||
|
53e580b876 | ||
|
fd076e0a2e | ||
|
057d5262a4 | ||
|
214d3afa98 | ||
|
cb2e337c94 | ||
|
cf24f76bf8 | ||
|
ec63c61e09 | ||
|
d83ae88716 | ||
|
e5548693df | ||
|
feeb0f1e45 | ||
|
5e6890a52c | ||
|
db7b726987 | ||
|
b0614beffa | ||
|
2dbb26e394 | ||
|
d166432f37 | ||
|
bcdb08691b | ||
|
f6dba6da1f | ||
|
2419801654 | ||
|
5672891485 | ||
|
7f18ccee94 | ||
|
2f0cfe41b1 | ||
|
7081c02171 | ||
|
1c03463a82 | ||
|
b0476f5a3a | ||
|
96fd058985 | ||
|
12c65a4564 | ||
|
36c8a36976 | ||
|
7de806f57a | ||
|
a3b5187b1f | ||
|
ba81aa14d4 | ||
|
d1c88bbc07 | ||
|
caa8320743 | ||
|
f64108867c | ||
|
716e5458f9 | ||
|
91c9a9e705 | ||
|
e633b5b3ce | ||
|
47d762958f | ||
|
0a34cb47ab | ||
|
cdba2f8c2c | ||
|
8f9deafce7 | ||
|
d6e445f220 | ||
|
3d2ec1bdf2 | ||
|
c0450160d6 | ||
|
b23dd44aa7 | ||
|
fc540c6fe2 | ||
|
c0b57e66fd | ||
|
00f8ddf48e | ||
|
63cc9a3c5d | ||
|
47c5823f52 | ||
|
5351095f42 | ||
|
64b0420ff4 | ||
|
695b0e46a8 | ||
|
1798529d28 | ||
|
f50e0d4dc6 | ||
|
c0817fba47 | ||
|
0a6e9f500a | ||
|
c8fb35cf53 | ||
|
9d62ea410f | ||
|
d1c36cc197 | ||
|
a99791c98a | ||
|
0e1b86b237 | ||
|
a1cddaf671 | ||
|
0f3a7cf3b6 | ||
|
a2f9afd369 | ||
|
7bb66e9ce7 | ||
|
8e7a8aed92 | ||
|
696396cf79 | ||
|
e11141e036 | ||
|
d6fc5dc072 | ||
|
4dd37700e6 | ||
|
cf08bb735d | ||
|
1ca82311ec | ||
|
cdf74b780a | ||
|
9e54fc8a3b | ||
|
16efd9d4a1 | ||
|
597b28f05f | ||
|
5192438851 | ||
|
13084620e5 | ||
|
6a58f4d38c | ||
|
5b40129505 | ||
|
d20627f5c0 | ||
|
0c016e8013 | ||
|
0f5155051b | ||
|
bf7c0a5dc1 | ||
|
bfe3c9cb00 | ||
|
2ae70d49a2 | ||
|
67529a041b | ||
|
385aa66e15 | ||
|
20a5547b27 | ||
|
605787bdd6 | ||
|
7cf14a39c7 | ||
|
8b267f811b | ||
|
9714332658 | ||
|
30896c8110 | ||
|
9b1999ec11 | ||
|
e71e200f5b | ||
|
5391077b94 | ||
|
0a44043884 | ||
|
586eae7c34 | ||
|
841c387fcb | ||
|
592efb300a | ||
|
bd8cb2076d | ||
|
8ad40532bb | ||
|
ebb6cf1cea | ||
|
3737491145 | ||
|
257f8231b7 | ||
|
3e7df45d24 | ||
|
fd8deba3ed | ||
|
eda1360d56 | ||
|
39f2ead0af | ||
|
77b908a663 | ||
|
6d8904d144 | ||
|
be9d0270d1 | ||
|
a5612072ea | ||
|
aec423012c | ||
|
a8120dde23 | ||
|
0009531a6f | ||
|
06510d2e21 | ||
|
c4a84b5d66 | ||
|
1f366d56f8 | ||
|
43c6afbd16 | ||
|
5d6d904ff7 | ||
|
bbf202a438 | ||
|
27801fe6c0 | ||
|
de173447ba | ||
|
cb352c911c | ||
|
0351bd0e59 | ||
|
15f77da7d9 | ||
|
bc92925b30 | ||
|
d9b8664709 | ||
|
e5e7c302d6 | ||
|
dc5c7c4e05 | ||
|
3db1b7c068 | ||
|
a0d23221a6 | ||
|
7dbd5b2575 | ||
|
a48e9a08d9 | ||
|
919a6022d0 | ||
|
e158c3922e | ||
|
3ad201ba56 | ||
|
7adf9d7cde | ||
|
2d08c6f7ec | ||
|
8b2957818a | ||
|
55516c16d5 | ||
|
2e52b163ec | ||
|
34523fee11 | ||
|
e8fe62300d | ||
|
dbed5c3e6b | ||
|
5228a3c44d | ||
|
0d4c59552f | ||
|
43f8bb2367 | ||
|
60aa420a99 | ||
|
9bd3707301 | ||
|
f99a84a5a0 | ||
|
87972b8801 | ||
|
adb3a8890f | ||
|
2b0e64d1ae | ||
|
d407af5bd1 | ||
|
df34c723bc | ||
|
f83075390b | ||
|
bb88b2e6b6 | ||
|
801b6d563d | ||
|
99f6099e86 | ||
|
709732a2ec | ||
|
104b095092 | ||
|
0718f33822 | ||
|
125b5ce80b | ||
|
e951f8cf52 | ||
|
009948961f | ||
|
609100bb3e | ||
|
eb50ba51e2 | ||
|
90b244e6b0 | ||
|
263b7c827d | ||
|
b726c49536 | ||
|
8cc21a69a0 | ||
|
5d8e8f25ff | ||
|
e0a7eea4bc | ||
|
063df2ccf7 | ||
|
aac268f87c | ||
|
25d894cbb2 | ||
|
7ebc3a1884 | ||
|
13a4b095ac | ||
|
115a52df55 | ||
|
9c294bd35e | ||
|
940004a131 | ||
|
34cb20a464 | ||
|
a5540c9d6d | ||
|
e981fad9d4 | ||
|
f3ebdc3fc3 | ||
|
b3711b7eee | ||
|
d27e9924bb | ||
|
f479ca2779 | ||
|
c0ab4c1ee8 | ||
|
4cba1d36de | ||
|
fa8ac0ac3b | ||
|
67e2ed7457 | ||
|
a5c26c5814 | ||
|
f992434063 | ||
|
d1ab4dc26f | ||
|
fad8d9599a | ||
|
2f99275b4c | ||
|
d05ed65b17 | ||
|
93cbe528cf | ||
|
9e96a9432b | ||
|
74e088d383 | ||
|
f07dae4513 | ||
|
45a8cef2f5 | ||
|
dc86523464 | ||
|
8a31ba5255 | ||
|
c688189035 | ||
|
e03f13831e | ||
|
3d7391b2fa | ||
|
7b939a4a64 | ||
|
c12a1326fc | ||
|
11730a8cbf | ||
|
0b8fcbe803 | ||
|
44e7fb94f4 | ||
|
4ab16d096a | ||
|
321afdb627 | ||
|
4680b1a3a1 | ||
|
5b36ef7f23 | ||
|
3b7619e081 | ||
|
fa25a60c08 | ||
|
d3beacf586 | ||
|
8fd5118e6c | ||
|
1a4149eef2 | ||
|
d5483d4984 | ||
|
fb3edca8e4 | ||
|
a3d3cf9577 | ||
|
a035c7477f | ||
|
4273a5fb0d | ||
|
707cc56562 | ||
|
479e60883c | ||
|
3d70fd9cca | ||
|
844eac0d3d | ||
|
ee4e5ad4c7 | ||
|
a71576c180 | ||
|
bf121f6f73 | ||
|
3790eaf67e | ||
|
507b960ba3 | ||
|
57710a043a | ||
|
36445720bb | ||
|
5a4ef7d94d | ||
|
44e9f5cb8b | ||
|
45032e4a95 | ||
|
da48f6e003 | ||
|
f8efcaf48b | ||
|
fc5cbc9b0c | ||
|
e01aecd78b | ||
|
bc7a36858b | ||
|
b9b14f90f5 | ||
|
27135e55cf | ||
|
761de18ba5 | ||
|
acebde7de5 | ||
|
ffebe086ca | ||
|
a13358cdb3 | ||
|
a4b8c5f5b9 | ||
|
916b5e94d8 | ||
|
19553d1bb8 | ||
|
b4bd385e88 | ||
|
d985eb6634 | ||
|
aa42887a67 | ||
|
0cafbff271 | ||
|
a04f976c20 | ||
|
2837f3b955 | ||
|
83ec1296a3 | ||
|
edfc23e273 | ||
|
32c51decad | ||
|
9ce560d9d1 | ||
|
3c333b59e6 | ||
|
073eb5fc2d | ||
|
8388c5dbce | ||
|
24e980069e | ||
|
9a42db3200 | ||
|
7e0d9f99f2 | ||
|
88564a1f56 | ||
|
a95c383cc6 | ||
|
f99de500ba | ||
|
aa7c520642 | ||
|
a7e31940a0 | ||
|
244f28cb1d | ||
|
39231d7939 | ||
|
e16d993c2e | ||
|
47ae2d94e7 | ||
|
c5f7c74717 | ||
|
70e3de7f41 | ||
|
f8fde9b630 | ||
|
f79ca90513 | ||
|
03bcf99340 | ||
|
143a25236d | ||
|
1a4933f38e | ||
|
b65df8bc7f | ||
|
c7ff4f7bdd | ||
|
11ddf3f586 | ||
|
42ea567303 | ||
|
a850b4471f | ||
|
797c90952f | ||
|
d441326f8d | ||
|
42b8853b35 | ||
|
72585b1cf3 | ||
|
dacda01ce1 | ||
|
0d6b29188f | ||
|
f04f5a724a | ||
|
e477d66731 | ||
|
d6a541dc0d | ||
|
898df9d11a | ||
|
da16288d89 | ||
|
222f66b8de | ||
|
d363ac6ce6 | ||
|
297758f856 | ||
|
71c2eea7d3 | ||
|
0c0692b867 | ||
|
7b400b0a7b | ||
|
961bb411ca | ||
|
fbcd88fbe8 | ||
|
52a1b3ba15 | ||
|
fe17d82a40 | ||
|
d5cf49ee66 | ||
|
847a9a3663 | ||
|
672efc5831 | ||
|
295ba4f727 | ||
|
675121c768 |
6
ISSUE_TEMPLATE.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
For English speaking user:
|
||||||
|
https://github.com/wangyu-/UDPspeeder/wiki/Issue-Guide
|
||||||
|
|
||||||
|
中文用户请看:
|
||||||
|
https://github.com/wangyu-/UDPspeeder/wiki/发Issue前请看
|
||||||
|
(否则Issue可能被忽略,或被直接关掉)
|
183
README.md
@@ -1,105 +1,150 @@
|
|||||||
# UDPspeeder
|
# UDPspeeder
|
||||||

|
Network Speed-Up Tool. Boost your Connection on a High Lantency High Packet-Loss Link by using Forward Error Correction.
|
||||||
UDP加速器,降低UDP传输的丢包率。尤其适用于游戏和语音。
|
|
||||||
|
|
||||||
这个是我自己稳定用了一个月的项目,用来玩服务器在美国的brawl stars和服务器在亚洲国外的mobile legend,效果不错。
|
When used alone, UDPspeeder speeds-up only UDP connection. Nevertheless, if you used UDPspeeder + any UDP-based VPN together,
|
||||||
|
you can speed-up any traffic(include TCP/UDP/ICMP), currently OpenVPN/L2TP/ShadowVPN are confirmed to be supported。
|
||||||
|
|
||||||
#### 原理简介
|

|
||||||
目前原理就是简单粗暴的多倍发包(以后会做Reed–Solomon code)。
|
|
||||||
|
|
||||||
跟net-speeder比,优势在于client和server会把收到的多余包自动去掉,这个过程对上层透明,没有兼容性问题。而且发出的冗余数据包会做长度和内容的随机化,抓包是看不出发了冗余数据的,所以不用担心vps被封的问题。
|
or
|
||||||
|
|
||||||
每个冗余数据包都是间隔数毫秒(可配置)以后延迟发出的,可以避开中间路由器因为瞬时buffer长度过长而连续丢掉所有副本。
|

|
||||||
|
|
||||||
可以模拟延迟抖动,这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于过早重传。
|
Assume your local network to your server is lossy. Just establish a VPN connection to your server with UDPspeeder + any UDP-based VPN, access your server via this VPN connection, then your connection quality will be significantly improved. With well-tuned parameters , you can easily reduce IP or UDP/ICMP packet-loss-rate to less than 0.01% . Besides reducing packet-loss-rate, UDPspeeder can also significantly improve your TCP latency and TCP single-thread download speed.
|
||||||
|
|
||||||
#### 适用场景
|
[简体中文](/doc/README.zh-cn.md)(内容更丰富)
|
||||||
绝大部分流量不高的情况。程序本身加速udp,但是配合openvpn可以加速任何流量。网络状况不好时,游戏卡得没法玩,或者网页卡得没法打开,使用起来效果最好。但是不适合大流量的场景,比如BT下载和在线看视频。
|
|
||||||
|
|
||||||
#### 其他功能
|
###### Note
|
||||||
输出UDP收发情况报告,便于分析网络。
|
You can use udp2raw with UDPspeeder together to get better speed on some ISP with UDP QoS(UDP throttling).
|
||||||
|
|
||||||
模拟丢包,模拟延迟,模拟jitter。便于通过实验找出应用卡顿的原因。
|
udp2raw's repo:
|
||||||
|
|
||||||
重复包过滤功能可以关掉,模拟网络本身有重复包的情况。用来测试应用对重复报的支持情况。
|
https://github.com/wangyu-/udp2raw-tunnel
|
||||||
|
|
||||||
目前有amd64,x86,ar71xx的binary
|
You can also try tinyFecVPN, a lightweight high-performance VPN with build-in FEC support:
|
||||||
|
|
||||||
# 简明操作说明
|
tinyFecVPN's repo:
|
||||||
|
|
||||||
### 环境要求
|
https://github.com/wangyu-/tinyFecVPN
|
||||||
Linux主机,可以使是openwrt路由器,也可以是树莓派。在windows和mac上可以开虚拟机(桥接模式测试可用)。
|
|
||||||
|
|
||||||
### 安装
|
|
||||||
下载编译好的二进制文件,解压到本地和服务器的任意目录。
|
|
||||||
|
|
||||||
https://github.com/wangyu-/UDPspeeder/releases
|
# Efficacy
|
||||||
|
tested on a link with 100ms latency and 10% packet loss at both direction
|
||||||
|
|
||||||
### 运行
|
### Ping Packet Loss
|
||||||
假设你有一个server,ip为44.55.66.77,有一个服务监听在udp 7777端口。 假设你需要加速本地到44.55.66.77:7777的流量
|

|
||||||
|
|
||||||
```
|
### SCP Copy Speed
|
||||||
在client端运行:
|

|
||||||
./speeder_ar71xx -l0.0.0.0:3323 -r 44.55.66.77:8855 -c -d2
|
|
||||||
|
|
||||||
在server端运行:
|
# Supported Platforms
|
||||||
./speeder_amd64 -l0.0.0.0:8855 -r127.0.0.1:7777 -s -d2
|
Linux host (including desktop Linux,Android phone/tablet, OpenWRT router, or Raspberry PI).
|
||||||
|
|
||||||
|
For Windows and MacOS You can run UDPspeeder inside [this](https://github.com/wangyu-/udp2raw-tunnel/releases/download/20171108.0/lede-17.01.2-x86_virtual_machine_image.zip) 7.5mb virtual machine image.
|
||||||
|
|
||||||
|
# How does it work
|
||||||
|
|
||||||
|
UDPspeeder uses FEC(Forward Error Correction) to reduce packet loss rate, at the cost of addtional bandwidth. The algorithm for FEC is called Reed-Solomon.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Reed-Solomon
|
||||||
|
|
||||||
|
`
|
||||||
|
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. Reed–Solomon codes are also 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.
|
||||||
|
`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Check wikipedia for more info, https://en.wikipedia.org/wiki/Reed–Solomon_error_correction
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
Download binary release from https://github.com/wangyu-/UDPspeeder/releases
|
||||||
|
|
||||||
|
### Running (speed-up UDP only)
|
||||||
|
Assume your server ip is 44.55.66.77, you have a service listening on udp port 7777.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run at server side:
|
||||||
|
./speederv2 -s -l0.0.0.0:4096 -r 127.0.0.1:7777 -f20:10 -k "passwd"
|
||||||
|
|
||||||
|
# Run at client side
|
||||||
|
./speederv2 -c -l0.0.0.0:3333 -r44.55.66.77:4096 -f20:10 -k "passwd"
|
||||||
```
|
```
|
||||||
|
|
||||||
现在client和server之间建立起了tunnel。想要连接44.55.66.77:7777,只需要连接 127.0.0.1:3333。来回的所有的udp流量会被加速。
|
Now connecting to UDP port 3333 at the client side is equivalent to connecting to port 7777 at the server side, and the connection has been boosted by UDPspeeder.
|
||||||
|
|
||||||
# 效果
|
##### Note
|
||||||
在模拟丢包的网络环境下,架设了vpn服务器(udp模式)。收发各有10%的丢包率
|
|
||||||
|
|
||||||
#### 使用前
|
`-f20:10` means sending 10 redundant packets for every 20 original packets.
|
||||||
<img src="images/Capture4.PNG" width="500">
|
|
||||||
#### 使用后
|
|
||||||
3倍冗余数据。
|
|
||||||
|
|
||||||
<img src="images/Capture6.PNG" width="500">
|
`-k` enables simple XOR encryption
|
||||||
|
|
||||||
# 进阶操作说明
|
# Advanced Topic
|
||||||
|
### Full Options
|
||||||
### 命令选项
|
|
||||||
```
|
```
|
||||||
UDPspeeder
|
UDPspeeder V2
|
||||||
version: Aug 9 2017 18:13:09
|
git version: 6f55b8a2fc build date: Nov 19 2017 06:11:23
|
||||||
repository: https://github.com/wangyu-/UDPspeeder
|
repository: https://github.com/wangyu-/UDPspeeder
|
||||||
|
|
||||||
usage:
|
usage:
|
||||||
run as client : ./this_program -c -l local_listen_ip:local_port -r server_ip:server_port [options]
|
run as client: ./this_program -c -l local_listen_ip:local_port -r server_ip:server_port [options]
|
||||||
run as server : ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]
|
run as server: ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]
|
||||||
|
|
||||||
common option,must be same on both sides:
|
common options, must be same on both sides:
|
||||||
-k,--key <string> key for simple xor encryption,default:"secret key"
|
-k,--key <string> key for simple xor encryption. if not set, xor is disabled
|
||||||
main options:
|
main options:
|
||||||
-d <number> duplicated packet number, -d 0 means no duplicate. default value:0
|
-f,--fec x:y forward error correction, send y redundant packets for every x packets
|
||||||
-t <number> duplicated packet delay time, unit: 0.1ms,default value:20(2ms)
|
--timeout <number> how long could a packet be held in queue before doing fec, unit: ms, default: 8ms
|
||||||
-j <number> simulated jitter.randomly delay first packet for 0~jitter_value*0.1 ms,to
|
--report <number> turn on send/recv report, and set a period for reporting, unit: s
|
||||||
create simulated jitter.default value:0.do not use if you dont
|
|
||||||
know what it means
|
|
||||||
--report <number> turn on udp send/recv report,and set a time interval for reporting,unit:s
|
|
||||||
advanced options:
|
advanced options:
|
||||||
-t tmin:tmax simliar to -t above,but delay randomly between tmin and tmax
|
--mode <number> fec-mode,available values: 0,1; mode 0(default) costs less bandwidth,no mtu problem.
|
||||||
-j jmin:jmax simliar to -j above,but create jitter randomly between jmin and jmax
|
mode 1 usually introduces less latency, but you have to care about mtu.
|
||||||
--random-drop <number> simulate packet loss ,unit 0.01%
|
--mtu <number> mtu. for mode 0, the program will split packet to segment smaller than mtu value.
|
||||||
-m <number> max pending packets,to prevent the program from eating up all your memory.
|
for mode 1, no packet will be split, the program just check if the mtu is exceed.
|
||||||
other options:
|
default value: 1250. you typically shouldnt change this value.
|
||||||
--log-level <number> 0:never 1:fatal 2:error 3:warn
|
-q,--queue-len <number> fec queue len, only for mode 0, fec will be performed immediately after queue is full.
|
||||||
4:info (default) 5:debug 6:trace
|
default value: 200.
|
||||||
--log-position enable file name,function name,line number in log
|
-j,--jitter <number> simulated jitter. randomly delay first packet for 0~<number> ms, default value: 0.
|
||||||
|
do not use if you dont know what it means.
|
||||||
|
-i,--interval <number> scatter each fec group to a interval of <number> ms, to protect burst packet loss.
|
||||||
|
default value: 0. do not use if you dont know what it means.
|
||||||
|
--random-drop <number> simulate packet loss, unit: 0.01%. default value: 0.
|
||||||
|
--disable-obscure <number> disable obscure, to save a bit bandwidth and cpu.
|
||||||
|
developer options:
|
||||||
|
--fifo <string> use a fifo(named pipe) for sending commands to the running program, so that you
|
||||||
|
can change fec encode parameters dynamically, check readme.md in repository for
|
||||||
|
supported commands.
|
||||||
|
-j ,--jitter jmin:jmax similiar to -j above, but create jitter randomly between jmin and jmax
|
||||||
|
-i,--interval imin:imax similiar to -i above, but scatter randomly between imin and imax
|
||||||
|
--decode-buf <number> size of buffer of fec decoder,u nit: packet, default: 2000
|
||||||
|
--fix-latency <number> try to stabilize latency, only for mode 0
|
||||||
|
--delay-capacity <number> max number of delayed packets
|
||||||
|
--disable-fec <number> completely disable fec, turn the program into a normal udp tunnel
|
||||||
|
--sock-buf <number> buf size for socket, >=10 and <=10240, unit: kbyte, default: 1024
|
||||||
|
log and help options:
|
||||||
|
--log-level <number> 0: never 1: fatal 2: error 3: warn
|
||||||
|
4: info (default) 5: debug 6: trace
|
||||||
|
--log-position enable file name, function name, line number in log
|
||||||
--disable-color disable log color
|
--disable-color disable log color
|
||||||
--sock-buf <number> buf size for socket,>=10 and <=10240,unit:kbyte,default:512
|
|
||||||
-h,--help print this help message
|
-h,--help print this help message
|
||||||
|
|
||||||
```
|
```
|
||||||
|
#### `--fifo` option
|
||||||
|
Use a fifo(named pipe) for sending commands to the running program. For example `--fifo fifo.file`, you can use following commands to change parameters dynamically:
|
||||||
|
```
|
||||||
|
echo fec 19:9 > fifo.file
|
||||||
|
echo mtu 1100 > fifo.file
|
||||||
|
echo timeout 5 > fifo.file
|
||||||
|
echo queue-len 100 > fifo.file
|
||||||
|
echo mode 0 > fifo.file
|
||||||
|
```
|
||||||
|
### Speed-Up any traffic with OpenVPN + UDPspeeder
|
||||||
|
|
||||||
# 应用
|
Check [UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
|
||||||
|
|
||||||
#### UDPspeeder + openvpn加速任何流量
|
|
||||||
如果你只是需要玩游戏,效果(可能/大概)会比kcp/finalspeed方案更好。可以优化tcp游戏的延迟(通过冗余发包,避免了上层的重传)。比如魔兽世界。
|
|
||||||

|
|
||||||
#### UDPspeeder + kcptun/finalspeed同时加速tcp和udp流量
|
|
||||||
如果你需要用加速的tcp看视频和下载文件,这样效果比vpn方案更好。不论是速度,还是流量的耗费上。
|
|
||||||

|
|
||||||
|
|
||||||
|
355
common.cpp
@@ -14,16 +14,45 @@ int about_to_exit=0;
|
|||||||
|
|
||||||
raw_mode_t raw_mode=mode_faketcp;
|
raw_mode_t raw_mode=mode_faketcp;
|
||||||
unordered_map<int, const char*> raw_mode_tostring = {{mode_faketcp, "faketcp"}, {mode_udp, "udp"}, {mode_icmp, "icmp"}};
|
unordered_map<int, const char*> raw_mode_tostring = {{mode_faketcp, "faketcp"}, {mode_udp, "udp"}, {mode_icmp, "icmp"}};
|
||||||
int socket_buf_size=1024*1024;
|
|
||||||
static int random_number_fd=-1;
|
|
||||||
char iptables_rule[200]="";
|
|
||||||
program_mode_t program_mode=unset_mode;//0 unset; 1client 2server
|
|
||||||
|
|
||||||
u64_t get_current_time()
|
|
||||||
|
//static int random_number_fd=-1;
|
||||||
|
char iptables_rule[200]="";
|
||||||
|
//int is_client = 0, is_server = 0;
|
||||||
|
|
||||||
|
program_mode_t client_or_server=unset_mode;//0 unset; 1client 2server
|
||||||
|
|
||||||
|
working_mode_t working_mode=tunnel_mode;
|
||||||
|
|
||||||
|
int socket_buf_size=1024*1024;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct random_fd_t
|
||||||
|
{
|
||||||
|
int random_number_fd;
|
||||||
|
random_fd_t()
|
||||||
|
{
|
||||||
|
random_number_fd=open("/dev/urandom",O_RDONLY);
|
||||||
|
|
||||||
|
if(random_number_fd==-1)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"error open /dev/urandom\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
setnonblocking(random_number_fd);
|
||||||
|
}
|
||||||
|
int get_fd()
|
||||||
|
{
|
||||||
|
return random_number_fd;
|
||||||
|
}
|
||||||
|
}random_fd;
|
||||||
|
|
||||||
|
u64_t get_current_time()//ms
|
||||||
{
|
{
|
||||||
timespec tmp_time;
|
timespec tmp_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &tmp_time);
|
clock_gettime(CLOCK_MONOTONIC, &tmp_time);
|
||||||
return tmp_time.tv_sec*1000+tmp_time.tv_nsec/(1000*1000l);
|
return ((u64_t)tmp_time.tv_sec)*1000llu+((u64_t)tmp_time.tv_nsec)/(1000*1000llu);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64_t get_current_time_us()
|
u64_t get_current_time_us()
|
||||||
@@ -49,6 +78,47 @@ u32_t get_u64_l(u64_t a)
|
|||||||
return (a<<32u)>>32u;
|
return (a<<32u)>>32u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write_u16(char * p,u16_t w)
|
||||||
|
{
|
||||||
|
*(unsigned char*)(p + 1) = (w & 0xff);
|
||||||
|
*(unsigned char*)(p + 0) = (w >> 8);
|
||||||
|
}
|
||||||
|
u16_t read_u16(char * p)
|
||||||
|
{
|
||||||
|
u16_t res;
|
||||||
|
res = *(const unsigned char*)(p + 0);
|
||||||
|
res = *(const unsigned char*)(p + 1) + (res << 8);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_u32(char * p,u32_t l)
|
||||||
|
{
|
||||||
|
*(unsigned char*)(p + 3) = (unsigned char)((l >> 0) & 0xff);
|
||||||
|
*(unsigned char*)(p + 2) = (unsigned char)((l >> 8) & 0xff);
|
||||||
|
*(unsigned char*)(p + 1) = (unsigned char)((l >> 16) & 0xff);
|
||||||
|
*(unsigned char*)(p + 0) = (unsigned char)((l >> 24) & 0xff);
|
||||||
|
}
|
||||||
|
u32_t read_u32(char * p)
|
||||||
|
{
|
||||||
|
u32_t res;
|
||||||
|
res = *(const unsigned char*)(p + 0);
|
||||||
|
res = *(const unsigned char*)(p + 1) + (res << 8);
|
||||||
|
res = *(const unsigned char*)(p + 2) + (res << 8);
|
||||||
|
res = *(const unsigned char*)(p + 3) + (res << 8);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_u64(char * s,u64_t a)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
u64_t read_u64(char * s)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
char * my_ntoa(u32_t ip)
|
char * my_ntoa(u32_t ip)
|
||||||
{
|
{
|
||||||
in_addr a;
|
in_addr a;
|
||||||
@@ -94,22 +164,11 @@ int clear_iptables_rule()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void init_random_number_fd()
|
|
||||||
{
|
|
||||||
|
|
||||||
random_number_fd=open("/dev/urandom",O_RDONLY);
|
|
||||||
|
|
||||||
if(random_number_fd==-1)
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"error open /dev/urandom\n");
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
setnonblocking(random_number_fd);
|
|
||||||
}
|
|
||||||
u64_t get_true_random_number_64()
|
u64_t get_true_random_number_64()
|
||||||
{
|
{
|
||||||
u64_t ret;
|
u64_t ret;
|
||||||
int size=read(random_number_fd,&ret,sizeof(ret));
|
int size=read(random_fd.get_fd(),&ret,sizeof(ret));
|
||||||
if(size!=sizeof(ret))
|
if(size!=sizeof(ret))
|
||||||
{
|
{
|
||||||
mylog(log_fatal,"get random number failed %d\n",size);
|
mylog(log_fatal,"get random number failed %d\n",size);
|
||||||
@@ -122,7 +181,7 @@ u64_t get_true_random_number_64()
|
|||||||
u32_t get_true_random_number()
|
u32_t get_true_random_number()
|
||||||
{
|
{
|
||||||
u32_t ret;
|
u32_t ret;
|
||||||
int size=read(random_number_fd,&ret,sizeof(ret));
|
int size=read(random_fd.get_fd(),&ret,sizeof(ret));
|
||||||
if(size!=sizeof(ret))
|
if(size!=sizeof(ret))
|
||||||
{
|
{
|
||||||
mylog(log_fatal,"get random number failed %d\n",size);
|
mylog(log_fatal,"get random number failed %d\n",size);
|
||||||
@@ -201,28 +260,75 @@ unsigned short csum(const unsigned short *ptr,int nbytes) {
|
|||||||
|
|
||||||
return(answer);
|
return(answer);
|
||||||
}
|
}
|
||||||
int set_buf_size(int fd,int size)
|
|
||||||
{
|
|
||||||
//int socket_buf_size=1024*1024;
|
unsigned short tcp_csum(const pseudo_header & ph,const unsigned short *ptr,int nbytes) {//works both for big and little endian
|
||||||
if(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &socket_buf_size, sizeof(socket_buf_size))<0)
|
|
||||||
//if(setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
|
register long sum;
|
||||||
|
unsigned short oddbyte;
|
||||||
|
register short answer;
|
||||||
|
|
||||||
|
sum=0;
|
||||||
|
unsigned short * tmp= (unsigned short *)&ph;
|
||||||
|
for(int i=0;i<6;i++)
|
||||||
{
|
{
|
||||||
printf("set SO_SNDBUF fail\n");
|
sum+=*tmp++;
|
||||||
exit(1);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while(nbytes>1) {
|
||||||
|
sum+=*ptr++;
|
||||||
|
nbytes-=2;
|
||||||
|
}
|
||||||
|
if(nbytes==1) {
|
||||||
|
oddbyte=0;
|
||||||
|
*((u_char*)&oddbyte)=*(u_char*)ptr;
|
||||||
|
sum+=oddbyte;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = (sum>>16)+(sum & 0xffff);
|
||||||
|
sum = sum + (sum>>16);
|
||||||
|
answer=(short)~sum;
|
||||||
|
|
||||||
|
return(answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_buf_size(int fd,int socket_buf_size,int force_socket_buf)
|
||||||
|
{
|
||||||
|
if(force_socket_buf)
|
||||||
|
{
|
||||||
|
if(setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"SO_SNDBUFFORCE fail socket_buf_size=%d errno=%s\n",socket_buf_size,strerror(errno));
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
if(setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"SO_RCVBUFFORCE fail socket_buf_size=%d errno=%s\n",socket_buf_size,strerror(errno));
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &socket_buf_size, sizeof(socket_buf_size))<0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"SO_SNDBUF fail socket_buf_size=%d errno=%s\n",socket_buf_size,strerror(errno));
|
||||||
|
myexit(1);
|
||||||
}
|
}
|
||||||
//if(setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
|
|
||||||
if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &socket_buf_size, sizeof(socket_buf_size))<0)
|
if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &socket_buf_size, sizeof(socket_buf_size))<0)
|
||||||
{
|
{
|
||||||
printf("set SO_RCVBUF fail\n");
|
mylog(log_fatal,"SO_RCVBUF fail socket_buf_size=%d errno=%s\n",socket_buf_size,strerror(errno));
|
||||||
exit(1);
|
myexit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void myexit(int a)
|
void myexit(int a)
|
||||||
{
|
{
|
||||||
if(enable_log_color)
|
if(enable_log_color)
|
||||||
printf("%s\n",RESET);
|
printf("%s\n",RESET);
|
||||||
clear_iptables_rule();
|
// clear_iptables_rule();
|
||||||
exit(a);
|
exit(a);
|
||||||
}
|
}
|
||||||
void signal_handler(int sig)
|
void signal_handler(int sig)
|
||||||
@@ -322,10 +428,197 @@ bool larger_than_u16(uint16_t a,uint16_t b)
|
|||||||
|
|
||||||
void get_true_random_chars(char * s,int len)
|
void get_true_random_chars(char * s,int len)
|
||||||
{
|
{
|
||||||
int size=read(random_number_fd,s,len);
|
int size=read(random_fd.get_fd(),s,len);
|
||||||
if(size!=len)
|
if(size!=len)
|
||||||
{
|
{
|
||||||
printf("get random number failed\n");
|
printf("get random number failed\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int random_between(u32_t a,u32_t b)
|
||||||
|
{
|
||||||
|
if(a>b)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"min >max?? %d %d\n",a ,b);
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
if(a==b)return a;
|
||||||
|
else return a+get_true_random_number()%(b+1-a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int set_timer_ms(int epollfd,int &timer_fd,u32_t timer_interval)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
epoll_event ev;
|
||||||
|
|
||||||
|
itimerspec its;
|
||||||
|
memset(&its,0,sizeof(its));
|
||||||
|
|
||||||
|
if((timer_fd=timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK)) < 0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"timer_fd create error\n");
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
its.it_interval.tv_sec=(timer_interval/1000);
|
||||||
|
its.it_interval.tv_nsec=(timer_interval%1000)*1000ll*1000ll;
|
||||||
|
its.it_value.tv_nsec=1; //imidiately
|
||||||
|
timerfd_settime(timer_fd,0,&its,0);
|
||||||
|
|
||||||
|
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.fd = timer_fd;
|
||||||
|
|
||||||
|
ret=epoll_ctl(epollfd, EPOLL_CTL_ADD, timer_fd, &ev);
|
||||||
|
if (ret < 0) {
|
||||||
|
mylog(log_fatal,"epoll_ctl return %d\n", ret);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
int create_new_udp(int &new_udp_fd,int remote_address_uint32,int remote_port)
|
||||||
|
{
|
||||||
|
struct sockaddr_in remote_addr_in;
|
||||||
|
|
||||||
|
socklen_t slen = sizeof(sockaddr_in);
|
||||||
|
memset(&remote_addr_in, 0, sizeof(remote_addr_in));
|
||||||
|
remote_addr_in.sin_family = AF_INET;
|
||||||
|
remote_addr_in.sin_port = htons(remote_port);
|
||||||
|
remote_addr_in.sin_addr.s_addr = remote_address_uint32;
|
||||||
|
|
||||||
|
new_udp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (new_udp_fd < 0) {
|
||||||
|
mylog(log_warn, "create udp_fd error\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
setnonblocking(new_udp_fd);
|
||||||
|
set_buf_size(new_udp_fd);
|
||||||
|
|
||||||
|
mylog(log_debug, "created new udp_fd %d\n", new_udp_fd);
|
||||||
|
int ret = connect(new_udp_fd, (struct sockaddr *) &remote_addr_in, slen);
|
||||||
|
if (ret != 0) {
|
||||||
|
mylog(log_warn, "udp fd connect fail %d %s\n",ret,strerror(errno));
|
||||||
|
close(new_udp_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}*/
|
||||||
|
void ip_port_t::from_u64(u64_t u64)
|
||||||
|
{
|
||||||
|
ip=get_u64_h(u64);
|
||||||
|
port=get_u64_l(u64);
|
||||||
|
}
|
||||||
|
u64_t ip_port_t::to_u64()
|
||||||
|
{
|
||||||
|
return pack_u64(ip,port);
|
||||||
|
}
|
||||||
|
char * ip_port_t::to_s()
|
||||||
|
{
|
||||||
|
static char res[40];
|
||||||
|
sprintf(res,"%s:%d",my_ntoa(ip),port);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int round_up_div(int a,int b)
|
||||||
|
{
|
||||||
|
return (a+b-1)/b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_fifo(char * file)
|
||||||
|
{
|
||||||
|
if(mkfifo (file, 0666)!=0)
|
||||||
|
{
|
||||||
|
if(errno==EEXIST)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"warning fifo file %s exist\n",file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"create fifo file %s failed\n",file);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int fifo_fd=open (file, O_RDWR);
|
||||||
|
if(fifo_fd<0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"create fifo file %s failed\n",file);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fifo_fd, &st)!=0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"fstat failed for fifo file %s\n",file);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!S_ISFIFO(st.st_mode))
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"%s is not a fifo\n",file);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setnonblocking(fifo_fd);
|
||||||
|
return fifo_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int new_listen_socket(int &fd,u32_t ip,int port)
|
||||||
|
{
|
||||||
|
fd =socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
|
||||||
|
int yes = 1;
|
||||||
|
//setsockopt(udp_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||||
|
|
||||||
|
struct sockaddr_in local_me={0};
|
||||||
|
|
||||||
|
socklen_t slen = sizeof(sockaddr_in);
|
||||||
|
//memset(&local_me, 0, sizeof(local_me));
|
||||||
|
local_me.sin_family = AF_INET;
|
||||||
|
local_me.sin_port = htons(port);
|
||||||
|
local_me.sin_addr.s_addr = ip;
|
||||||
|
|
||||||
|
if (bind(fd, (struct sockaddr*) &local_me, slen) == -1) {
|
||||||
|
mylog(log_fatal,"socket bind error\n");
|
||||||
|
//perror("socket bind error");
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
setnonblocking(fd);
|
||||||
|
set_buf_size(fd,socket_buf_size);
|
||||||
|
|
||||||
|
mylog(log_debug,"local_listen_fd=%d\n,",fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int new_connected_socket(int &fd,u32_t ip,int port)
|
||||||
|
{
|
||||||
|
char ip_port[40];
|
||||||
|
sprintf(ip_port,"%s:%d",my_ntoa(ip),port);
|
||||||
|
|
||||||
|
struct sockaddr_in remote_addr_in = { 0 };
|
||||||
|
|
||||||
|
socklen_t slen = sizeof(sockaddr_in);
|
||||||
|
//memset(&remote_addr_in, 0, sizeof(remote_addr_in));
|
||||||
|
remote_addr_in.sin_family = AF_INET;
|
||||||
|
remote_addr_in.sin_port = htons(port);
|
||||||
|
remote_addr_in.sin_addr.s_addr = ip;
|
||||||
|
|
||||||
|
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (fd < 0) {
|
||||||
|
mylog(log_warn, "[%s]create udp_fd error\n", ip_port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
setnonblocking(fd);
|
||||||
|
set_buf_size(fd, socket_buf_size);
|
||||||
|
|
||||||
|
mylog(log_debug, "[%s]created new udp_fd %d\n", ip_port, fd);
|
||||||
|
int ret = connect(fd, (struct sockaddr *) &remote_addr_in, slen);
|
||||||
|
if (ret != 0) {
|
||||||
|
mylog(log_warn, "[%s]fd connect fail\n",ip_port);
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
126
common.h
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#ifndef COMMON_H_
|
#ifndef COMMON_H_
|
||||||
#define COMMON_H_
|
#define COMMON_H_
|
||||||
#define __STDC_FORMAT_MACROS 1
|
//#define __STDC_FORMAT_MACROS 1
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include<stdio.h>
|
#include<stdio.h>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/socket.h> //for socket ofcourse
|
#include <sys/socket.h> //for socket ofcourse
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <stdlib.h> //for exit(0);
|
#include <stdlib.h> //for exit(0);
|
||||||
#include <errno.h> //For errno - the error number
|
#include <errno.h> //For errno - the error number
|
||||||
#include <netinet/tcp.h> //Provides declarations for tcp header
|
#include <netinet/tcp.h> //Provides declarations for tcp header
|
||||||
@@ -43,12 +44,11 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <linux/if_packet.h>
|
#include <linux/if_packet.h>
|
||||||
|
#include <linux/if_tun.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include<unordered_map>
|
#include<unordered_map>
|
||||||
#include<unordered_set>
|
#include<unordered_set>
|
||||||
|
#include<map>
|
||||||
#include<list>
|
#include<list>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -59,16 +59,21 @@ typedef long long i64_t;
|
|||||||
typedef unsigned int u32_t;
|
typedef unsigned int u32_t;
|
||||||
typedef int i32_t;
|
typedef int i32_t;
|
||||||
|
|
||||||
|
typedef unsigned short u16_t;
|
||||||
|
typedef short i16_t;
|
||||||
|
|
||||||
const int max_data_len=1600;
|
typedef u64_t my_time_t;
|
||||||
|
|
||||||
|
const int max_data_len=2200;
|
||||||
const int buf_len=max_data_len+200;
|
const int buf_len=max_data_len+200;
|
||||||
|
|
||||||
const u32_t conv_clear_interval=200;
|
|
||||||
const u32_t timer_interval=400;
|
//const u32_t timer_interval=400;
|
||||||
const int conv_clear_ratio=40;
|
////const u32_t conv_timeout=180000;
|
||||||
const int conv_clear_min=5;
|
//const u32_t conv_timeout=40000;//for test
|
||||||
const u32_t conv_timeout=20000;
|
const u32_t conv_timeout=180000;
|
||||||
const int max_conv_num=10000;
|
const int max_conv_num=10000;
|
||||||
|
const int max_conn_num=200;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const u32_t max_handshake_conn_num=10000;
|
const u32_t max_handshake_conn_num=10000;
|
||||||
@@ -79,14 +84,14 @@ const u32_t max_ready_conn_num=1000;
|
|||||||
const u32_t client_handshake_timeout=5000;
|
const u32_t client_handshake_timeout=5000;
|
||||||
const u32_t client_retry_interval=1000;
|
const u32_t client_retry_interval=1000;
|
||||||
|
|
||||||
const u32_t server_handshake_timeout=10000;// this should be much longer than clients. client retry initially ,server retry passtively
|
const u32_t server_handshake_timeout=10000;// this should be much longer than clients. client retry initially ,server retry passtively*/
|
||||||
|
|
||||||
const int conv_clear_ratio=10; //conv grabage collecter check 1/10 of all conv one time
|
const int conv_clear_ratio=30; //conv grabage collecter check 1/30 of all conv one time
|
||||||
const int conn_clear_ratio=10;
|
const int conn_clear_ratio=50;
|
||||||
const int conv_clear_min=5;
|
const int conv_clear_min=1;
|
||||||
const int conn_clear_min=1;
|
const int conn_clear_min=1;
|
||||||
|
|
||||||
|
const u32_t conv_clear_interval=1000;
|
||||||
const u32_t conn_clear_interval=1000;
|
const u32_t conn_clear_interval=1000;
|
||||||
|
|
||||||
|
|
||||||
@@ -97,24 +102,29 @@ const u32_t heartbeat_interval=1000;
|
|||||||
const u32_t timer_interval=400;//this should be smaller than heartbeat_interval and retry interval;
|
const u32_t timer_interval=400;//this should be smaller than heartbeat_interval and retry interval;
|
||||||
|
|
||||||
//const uint32_t conv_timeout=120000; //120 second
|
//const uint32_t conv_timeout=120000; //120 second
|
||||||
const u32_t conv_timeout=120000; //for test
|
//const u32_t conv_timeout=120000; //for test
|
||||||
|
|
||||||
const u32_t client_conn_timeout=10000;
|
const u32_t client_conn_timeout=10000;
|
||||||
const u32_t client_conn_uplink_timeout=client_conn_timeout+2000;
|
const u32_t client_conn_uplink_timeout=client_conn_timeout+2000;
|
||||||
|
|
||||||
//const uint32_t server_conn_timeout=conv_timeout+60000;//this should be 60s+ longer than conv_timeout,so that conv_manager can destruct convs gradually,to avoid latency glicth
|
//const uint32_t server_conn_timeout=conv_timeout+60000;//this should be 60s+ longer than conv_timeout,so that conv_manager can destruct convs gradually,to avoid latency glicth
|
||||||
const u32_t server_conn_timeout=conv_timeout+60000;//for test
|
const u32_t server_conn_timeout=conv_timeout+20000;//for test
|
||||||
*/
|
|
||||||
|
|
||||||
extern int about_to_exit;
|
extern int about_to_exit;
|
||||||
|
|
||||||
enum raw_mode_t{mode_faketcp=0,mode_udp,mode_icmp,mode_end};
|
enum raw_mode_t{mode_faketcp=0,mode_udp,mode_icmp,mode_end};
|
||||||
extern raw_mode_t raw_mode;
|
extern raw_mode_t raw_mode;
|
||||||
enum program_mode_t {unset_mode=0,client_mode,server_mode};
|
enum program_mode_t {unset_mode=0,client_mode,server_mode};
|
||||||
extern program_mode_t program_mode;
|
extern program_mode_t client_or_server;
|
||||||
extern unordered_map<int, const char*> raw_mode_tostring ;
|
extern unordered_map<int, const char*> raw_mode_tostring ;
|
||||||
|
|
||||||
|
enum working_mode_t {unset_working_mode=0,tunnel_mode,tun_dev_mode};
|
||||||
|
extern working_mode_t working_mode;
|
||||||
|
|
||||||
extern int socket_buf_size;
|
extern int socket_buf_size;
|
||||||
|
|
||||||
|
|
||||||
typedef u32_t id_t;
|
typedef u32_t id_t;
|
||||||
|
|
||||||
typedef u64_t iv_t;
|
typedef u64_t iv_t;
|
||||||
@@ -123,6 +133,58 @@ typedef u64_t padding_t;
|
|||||||
|
|
||||||
typedef u64_t anti_replay_seq_t;
|
typedef u64_t anti_replay_seq_t;
|
||||||
|
|
||||||
|
typedef u64_t fd64_t;
|
||||||
|
|
||||||
|
//enum dest_type{none=0,type_fd64_ip_port,type_fd64,type_fd64_ip_port_conv,type_fd64_conv/*,type_fd*/};
|
||||||
|
enum dest_type{none=0,type_fd64_ip_port,type_fd64,type_fd,type_write_fd,type_fd_ip_port/*,type_fd*/};
|
||||||
|
|
||||||
|
struct ip_port_t
|
||||||
|
{
|
||||||
|
u32_t ip;
|
||||||
|
int port;
|
||||||
|
void from_u64(u64_t u64);
|
||||||
|
u64_t to_u64();
|
||||||
|
char * to_s();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fd64_ip_port_t
|
||||||
|
{
|
||||||
|
fd64_t fd64;
|
||||||
|
ip_port_t ip_port;
|
||||||
|
};
|
||||||
|
struct fd_ip_port_t
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
ip_port_t ip_port;
|
||||||
|
};
|
||||||
|
union inner_t
|
||||||
|
{
|
||||||
|
fd64_t fd64;
|
||||||
|
int fd;
|
||||||
|
fd64_ip_port_t fd64_ip_port;
|
||||||
|
fd_ip_port_t fd_ip_port;
|
||||||
|
};
|
||||||
|
struct dest_t
|
||||||
|
{
|
||||||
|
dest_type type;
|
||||||
|
inner_t inner;
|
||||||
|
u32_t conv;
|
||||||
|
int cook=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fd_info_t
|
||||||
|
{
|
||||||
|
ip_port_t ip_port;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pseudo_header {
|
||||||
|
u_int32_t source_address;
|
||||||
|
u_int32_t dest_address;
|
||||||
|
u_int8_t placeholder;
|
||||||
|
u_int8_t protocol;
|
||||||
|
u_int16_t tcp_length;
|
||||||
|
};
|
||||||
|
|
||||||
u64_t get_current_time();
|
u64_t get_current_time();
|
||||||
u64_t get_current_time_us();
|
u64_t get_current_time_us();
|
||||||
u64_t pack_u64(u32_t a,u32_t b);
|
u64_t pack_u64(u32_t a,u32_t b);
|
||||||
@@ -131,6 +193,15 @@ u32_t get_u64_h(u64_t a);
|
|||||||
|
|
||||||
u32_t get_u64_l(u64_t a);
|
u32_t get_u64_l(u64_t a);
|
||||||
|
|
||||||
|
void write_u16(char *,u16_t a);
|
||||||
|
u16_t read_u16(char *);
|
||||||
|
|
||||||
|
void write_u32(char *,u32_t a);
|
||||||
|
u32_t read_u32(char *);
|
||||||
|
|
||||||
|
void write_u64(char *,u64_t a);
|
||||||
|
u64_t read_uu64(char *);
|
||||||
|
|
||||||
char * my_ntoa(u32_t ip);
|
char * my_ntoa(u32_t ip);
|
||||||
|
|
||||||
void myexit(int a);
|
void myexit(int a);
|
||||||
@@ -143,9 +214,10 @@ u64_t hton64(u64_t a);
|
|||||||
bool larger_than_u16(uint16_t a,uint16_t b);
|
bool larger_than_u16(uint16_t a,uint16_t b);
|
||||||
bool larger_than_u32(u32_t a,u32_t b);
|
bool larger_than_u32(u32_t a,u32_t b);
|
||||||
void setnonblocking(int sock);
|
void setnonblocking(int sock);
|
||||||
int set_buf_size(int fd,int size=socket_buf_size);
|
int set_buf_size(int fd,int socket_buf_size,int force_socket_buf=0);
|
||||||
|
|
||||||
unsigned short csum(const unsigned short *ptr,int nbytes);
|
unsigned short csum(const unsigned short *ptr,int nbytes);
|
||||||
|
unsigned short tcp_csum(const pseudo_header & ph,const unsigned short *ptr,int nbytes);
|
||||||
|
|
||||||
void signal_handler(int sig);
|
void signal_handler(int sig);
|
||||||
int numbers_to_char(id_t id1,id_t id2,id_t id3,char * &data,int &len);
|
int numbers_to_char(id_t id1,id_t id2,id_t id3,char * &data,int &len);
|
||||||
@@ -157,5 +229,19 @@ int add_iptables_rule(char *);
|
|||||||
|
|
||||||
int clear_iptables_rule();
|
int clear_iptables_rule();
|
||||||
void get_true_random_chars(char * s,int len);
|
void get_true_random_chars(char * s,int len);
|
||||||
|
int random_between(u32_t a,u32_t b);
|
||||||
|
|
||||||
|
int set_timer_ms(int epollfd,int &timer_fd,u32_t timer_interval);
|
||||||
|
|
||||||
|
int round_up_div(int a,int b);
|
||||||
|
|
||||||
|
int create_fifo(char * file);
|
||||||
|
/*
|
||||||
|
int create_new_udp(int &new_udp_fd,int remote_address_uint32,int remote_port);
|
||||||
|
*/
|
||||||
|
|
||||||
|
int new_listen_socket(int &fd,u32_t ip,int port);
|
||||||
|
|
||||||
|
int new_connected_socket(int &fd,u32_t ip,int port);
|
||||||
|
|
||||||
#endif /* COMMON_H_ */
|
#endif /* COMMON_H_ */
|
||||||
|
293
connection.cpp
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* connection.cpp
|
||||||
|
*
|
||||||
|
* Created on: Sep 23, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
const int disable_conv_clear=0;//a udp connection in the multiplexer is called conversation in this program,conv for short.
|
||||||
|
|
||||||
|
const int disable_conn_clear=0;//a raw connection is called conn.
|
||||||
|
|
||||||
|
int report_interval=0;
|
||||||
|
|
||||||
|
void server_clear_function(u64_t u64)//used in conv_manager in server mode.for server we have to use one udp fd for one conv(udp connection),
|
||||||
|
//so we have to close the fd when conv expires
|
||||||
|
{
|
||||||
|
fd64_t fd64=u64;
|
||||||
|
assert(fd_manager.exist(fd64));
|
||||||
|
fd_manager.fd64_close(fd64);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
conv_manager_t::conv_manager_t()
|
||||||
|
{
|
||||||
|
clear_it=conv_last_active_time.begin();
|
||||||
|
long long last_clear_time=0;
|
||||||
|
reserve();
|
||||||
|
}
|
||||||
|
conv_manager_t::~conv_manager_t()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
int conv_manager_t::get_size()
|
||||||
|
{
|
||||||
|
return conv_to_u64.size();
|
||||||
|
}
|
||||||
|
void conv_manager_t::reserve()
|
||||||
|
{
|
||||||
|
u64_to_conv.reserve(10007);
|
||||||
|
conv_to_u64.reserve(10007);
|
||||||
|
conv_last_active_time.reserve(10007);
|
||||||
|
}
|
||||||
|
void conv_manager_t::clear()
|
||||||
|
{
|
||||||
|
//if(disable_conv_clear) return ;//////what was the purpose of this code?
|
||||||
|
|
||||||
|
if(client_or_server==server_mode)
|
||||||
|
{
|
||||||
|
for(auto it=conv_to_u64.begin();it!=conv_to_u64.end();it++)
|
||||||
|
{
|
||||||
|
//int fd=int((it->second<<32u)>>32u);
|
||||||
|
server_clear_function( it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u64_to_conv.clear();
|
||||||
|
conv_to_u64.clear();
|
||||||
|
conv_last_active_time.clear();
|
||||||
|
|
||||||
|
clear_it=conv_last_active_time.begin();
|
||||||
|
|
||||||
|
}
|
||||||
|
u32_t conv_manager_t::get_new_conv()
|
||||||
|
{
|
||||||
|
u32_t conv=get_true_random_number_nz();
|
||||||
|
while(conv_to_u64.find(conv)!=conv_to_u64.end())
|
||||||
|
{
|
||||||
|
conv=get_true_random_number_nz();
|
||||||
|
}
|
||||||
|
return conv;
|
||||||
|
}
|
||||||
|
int conv_manager_t::is_conv_used(u32_t conv)
|
||||||
|
{
|
||||||
|
return conv_to_u64.find(conv)!=conv_to_u64.end();
|
||||||
|
}
|
||||||
|
int conv_manager_t::is_u64_used(u64_t u64)
|
||||||
|
{
|
||||||
|
return u64_to_conv.find(u64)!=u64_to_conv.end();
|
||||||
|
}
|
||||||
|
u32_t conv_manager_t::find_conv_by_u64(u64_t u64)
|
||||||
|
{
|
||||||
|
assert(is_u64_used(u64));
|
||||||
|
return u64_to_conv[u64];
|
||||||
|
}
|
||||||
|
u64_t conv_manager_t::find_u64_by_conv(u32_t conv)
|
||||||
|
{
|
||||||
|
assert(is_conv_used(conv));
|
||||||
|
return conv_to_u64[conv];
|
||||||
|
}
|
||||||
|
int conv_manager_t::update_active_time(u32_t conv)
|
||||||
|
{
|
||||||
|
assert(is_conv_used(conv));
|
||||||
|
return conv_last_active_time[conv]=get_current_time();
|
||||||
|
}
|
||||||
|
int conv_manager_t::insert_conv(u32_t conv,u64_t u64)//////todo add capacity ///done at upper level
|
||||||
|
{
|
||||||
|
assert(!is_conv_used(conv));
|
||||||
|
int bucket_size_before=conv_last_active_time.bucket_count();
|
||||||
|
u64_to_conv[u64]=conv;
|
||||||
|
conv_to_u64[conv]=u64;
|
||||||
|
conv_last_active_time[conv]=get_current_time();
|
||||||
|
int bucket_size_after=conv_last_active_time.bucket_count();
|
||||||
|
if(bucket_size_after!=bucket_size_before)
|
||||||
|
clear_it=conv_last_active_time.begin();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int conv_manager_t::erase_conv(u32_t conv)
|
||||||
|
{
|
||||||
|
//if(disable_conv_clear) return 0;
|
||||||
|
assert(conv_last_active_time.find(conv)!=conv_last_active_time.end());
|
||||||
|
u64_t u64=conv_to_u64[conv];
|
||||||
|
if(client_or_server==server_mode)
|
||||||
|
{
|
||||||
|
server_clear_function(u64);
|
||||||
|
}
|
||||||
|
assert(conv_to_u64.find(conv)!=conv_to_u64.end());
|
||||||
|
conv_to_u64.erase(conv);
|
||||||
|
u64_to_conv.erase(u64);
|
||||||
|
conv_last_active_time.erase(conv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int conv_manager_t::clear_inactive(char * ip_port)
|
||||||
|
{
|
||||||
|
if(get_current_time()-last_clear_time>conv_clear_interval)
|
||||||
|
{
|
||||||
|
last_clear_time=get_current_time();
|
||||||
|
return clear_inactive0(ip_port);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int conv_manager_t::clear_inactive0(char * ip_port)
|
||||||
|
{
|
||||||
|
if(disable_conv_clear) return 0;
|
||||||
|
|
||||||
|
//map<uint32_t,uint64_t>::iterator it;
|
||||||
|
int cnt=0;
|
||||||
|
auto it=clear_it;
|
||||||
|
int size=conv_last_active_time.size();
|
||||||
|
int num_to_clean=size/conv_clear_ratio+conv_clear_min; //clear 1/10 each time,to avoid latency glitch
|
||||||
|
|
||||||
|
num_to_clean=min(num_to_clean,size);
|
||||||
|
|
||||||
|
u64_t current_time=get_current_time();
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(cnt>=num_to_clean) break;
|
||||||
|
if(conv_last_active_time.begin()==conv_last_active_time.end()) break;
|
||||||
|
|
||||||
|
if(it==conv_last_active_time.end())
|
||||||
|
{
|
||||||
|
it=conv_last_active_time.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( current_time -it->second >conv_timeout )
|
||||||
|
{
|
||||||
|
//mylog(log_info,"inactive conv %u cleared \n",it->first);
|
||||||
|
//auto old_it=it;
|
||||||
|
//it++;
|
||||||
|
u32_t conv= it->first;
|
||||||
|
it++;
|
||||||
|
erase_conv(conv);
|
||||||
|
if(ip_port==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"conv %x cleared\n",conv);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_info,"[%s]conv %x cleared\n",ip_port,conv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
clear_it=it;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
conn_manager_t::conn_manager_t()
|
||||||
|
{
|
||||||
|
//ready_num=0;
|
||||||
|
mp.reserve(10007);
|
||||||
|
//fd64_mp.reserve(100007);
|
||||||
|
clear_it=mp.begin();
|
||||||
|
last_clear_time=0;
|
||||||
|
}
|
||||||
|
int conn_manager_t::exist(ip_port_t ip_port)
|
||||||
|
{
|
||||||
|
u64_t u64=ip_port.to_u64();
|
||||||
|
if(mp.find(u64)!=mp.end())
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
conn_info_t *& conn_manager_t::find_p(ip_port_t ip_port) //todo capacity ///done at upper level
|
||||||
|
//be aware,the adress may change after rehash
|
||||||
|
{
|
||||||
|
assert(exist(ip_port));
|
||||||
|
u64_t u64=ip_port.to_u64();
|
||||||
|
return mp[u64];
|
||||||
|
}
|
||||||
|
conn_info_t & conn_manager_t::find(ip_port_t ip_port) //be aware,the adress may change after rehash
|
||||||
|
{
|
||||||
|
assert(exist(ip_port));
|
||||||
|
u64_t u64=ip_port.to_u64();
|
||||||
|
return *mp[u64];
|
||||||
|
}
|
||||||
|
int conn_manager_t::insert(ip_port_t ip_port)
|
||||||
|
{
|
||||||
|
assert(!exist(ip_port));
|
||||||
|
int bucket_size_before=mp.bucket_count();
|
||||||
|
mp[ip_port.to_u64()]=new conn_info_t;
|
||||||
|
int bucket_size_after=mp.bucket_count();
|
||||||
|
if(bucket_size_after!=bucket_size_before)
|
||||||
|
clear_it=mp.begin();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int conn_manager_t::erase(unordered_map<u64_t,conn_info_t*>::iterator erase_it)
|
||||||
|
{
|
||||||
|
////////todo close and erase timer_fd ,check fd64 empty ///dont need
|
||||||
|
delete(erase_it->second);
|
||||||
|
mp.erase(erase_it->first);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int conn_manager_t::clear_inactive()
|
||||||
|
{
|
||||||
|
if(get_current_time()-last_clear_time>conn_clear_interval)
|
||||||
|
{
|
||||||
|
last_clear_time=get_current_time();
|
||||||
|
return clear_inactive0();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int conn_manager_t::clear_inactive0()
|
||||||
|
{
|
||||||
|
//mylog(log_info,"called\n");
|
||||||
|
unordered_map<u64_t,conn_info_t*>::iterator it;
|
||||||
|
unordered_map<u64_t,conn_info_t*>::iterator old_it;
|
||||||
|
|
||||||
|
if(disable_conn_clear) return 0;
|
||||||
|
|
||||||
|
//map<uint32_t,uint64_t>::iterator it;
|
||||||
|
int cnt=0;
|
||||||
|
it=clear_it;//TODO,write it back
|
||||||
|
int size=mp.size();
|
||||||
|
int num_to_clean=size/conn_clear_ratio+conn_clear_min; //clear 1/10 each time,to avoid latency glitch
|
||||||
|
|
||||||
|
//mylog(log_trace,"mp.size() %d\n", size);
|
||||||
|
|
||||||
|
num_to_clean=min(num_to_clean,(int)mp.size());
|
||||||
|
u64_t current_time=get_current_time();
|
||||||
|
|
||||||
|
//mylog(log_info,"here size=%d\n",(int)mp.size());
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(cnt>=num_to_clean) break;
|
||||||
|
if(mp.begin()==mp.end()) break;
|
||||||
|
if(it==mp.end())
|
||||||
|
{
|
||||||
|
it=mp.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(it->second->conv_manager.get_size() >0)
|
||||||
|
{
|
||||||
|
//mylog(log_info,"[%s:%d]size %d \n",my_ntoa(get_u64_h(it->first)),get_u64_l(it->first),(int)it->second->conv_manager.get_size());
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
else if(current_time<it->second->last_active_time+server_conn_timeout)
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_info,"[%s:%d]inactive conn cleared \n",my_ntoa(get_u64_h(it->first)),get_u64_l(it->first));
|
||||||
|
old_it=it;
|
||||||
|
it++;
|
||||||
|
erase(old_it);
|
||||||
|
}
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
clear_it=it;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
152
connection.h
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* connection.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 23, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTION_H_
|
||||||
|
#define CONNECTION_H_
|
||||||
|
|
||||||
|
extern int disable_anti_replay;
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "delay_manager.h"
|
||||||
|
#include "fd_manager.h"
|
||||||
|
#include "fec_manager.h"
|
||||||
|
|
||||||
|
extern int report_interval;
|
||||||
|
|
||||||
|
struct conv_manager_t // manage the udp connections
|
||||||
|
{
|
||||||
|
//typedef hash_map map;
|
||||||
|
unordered_map<u64_t,u32_t> u64_to_conv; //conv and u64 are both supposed to be uniq
|
||||||
|
unordered_map<u32_t,u64_t> conv_to_u64;
|
||||||
|
unordered_map<u32_t,u64_t> conv_last_active_time;
|
||||||
|
|
||||||
|
unordered_map<u32_t,u64_t>::iterator clear_it;
|
||||||
|
|
||||||
|
//void (*clear_function)(uint64_t u64) ;
|
||||||
|
|
||||||
|
long long last_clear_time;
|
||||||
|
|
||||||
|
conv_manager_t();
|
||||||
|
conv_manager_t(const conv_manager_t &b)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
~conv_manager_t();
|
||||||
|
int get_size();
|
||||||
|
void reserve();
|
||||||
|
void clear();
|
||||||
|
u32_t get_new_conv();
|
||||||
|
int is_conv_used(u32_t conv);
|
||||||
|
int is_u64_used(u64_t u64);
|
||||||
|
u32_t find_conv_by_u64(u64_t u64);
|
||||||
|
u64_t find_u64_by_conv(u32_t conv);
|
||||||
|
int update_active_time(u32_t conv);
|
||||||
|
int insert_conv(u32_t conv,u64_t u64);
|
||||||
|
int erase_conv(u32_t conv);
|
||||||
|
int clear_inactive(char * ip_port=0);
|
||||||
|
int clear_inactive0(char * ip_port);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct inner_stat_t
|
||||||
|
{
|
||||||
|
u64_t input_packet_num;
|
||||||
|
u64_t input_packet_size;
|
||||||
|
u64_t output_packet_num;
|
||||||
|
u64_t output_packet_size;
|
||||||
|
};
|
||||||
|
struct stat_t
|
||||||
|
{
|
||||||
|
u64_t last_report_time;
|
||||||
|
inner_stat_t normal_to_fec;
|
||||||
|
inner_stat_t fec_to_normal;
|
||||||
|
stat_t()
|
||||||
|
{
|
||||||
|
memset(this,0,sizeof(stat_t));
|
||||||
|
}
|
||||||
|
void report_as_client()
|
||||||
|
{
|
||||||
|
if(report_interval!=0 &&get_current_time()-last_report_time>u64_t(report_interval)*1000)
|
||||||
|
{
|
||||||
|
last_report_time=get_current_time();
|
||||||
|
inner_stat_t &a=normal_to_fec;
|
||||||
|
inner_stat_t &b=fec_to_normal;
|
||||||
|
mylog(log_info,"[report]client-->server:(original:%llu pkt;%llu byte) (fec:%llu pkt,%llu byte) server-->client:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte)\n",
|
||||||
|
a.input_packet_num,a.input_packet_size,a.output_packet_num,a.output_packet_size,
|
||||||
|
b.output_packet_num,b.output_packet_size,b.input_packet_num,b.input_packet_size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void report_as_server(ip_port_t &ip_port)
|
||||||
|
{
|
||||||
|
if(report_interval!=0 &&get_current_time()-last_report_time>u64_t(report_interval)*1000)
|
||||||
|
{
|
||||||
|
last_report_time=get_current_time();
|
||||||
|
inner_stat_t &a=fec_to_normal;
|
||||||
|
inner_stat_t &b=normal_to_fec;
|
||||||
|
mylog(log_info,"[report][%s]client-->server:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte) server-->client:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte)\n",
|
||||||
|
ip_port.to_s(),
|
||||||
|
a.output_packet_num,a.output_packet_size,a.input_packet_num,a.input_packet_size,
|
||||||
|
b.input_packet_num,b.input_packet_size,b.output_packet_num,b.output_packet_size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct conn_info_t //stores info for a raw connection.for client ,there is only one connection,for server there can be thousand of connection since server can
|
||||||
|
//handle multiple clients
|
||||||
|
{
|
||||||
|
conv_manager_t conv_manager;
|
||||||
|
fec_encode_manager_t fec_encode_manager;
|
||||||
|
fec_decode_manager_t fec_decode_manager;
|
||||||
|
my_timer_t timer;
|
||||||
|
//ip_port_t ip_port;
|
||||||
|
u64_t last_active_time;
|
||||||
|
stat_t stat;
|
||||||
|
conn_info_t()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void update_active_time()
|
||||||
|
{
|
||||||
|
last_active_time=get_current_time();
|
||||||
|
}
|
||||||
|
conn_info_t(const conn_info_t &b)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct conn_manager_t //manager for connections. for client,we dont need conn_manager since there is only one connection.for server we use one conn_manager for all connections
|
||||||
|
{
|
||||||
|
|
||||||
|
unordered_map<u64_t,conn_info_t*> mp;//<ip,port> to conn_info_t;
|
||||||
|
unordered_map<u64_t,conn_info_t*>::iterator clear_it;
|
||||||
|
long long last_clear_time;
|
||||||
|
|
||||||
|
conn_manager_t();
|
||||||
|
conn_manager_t(const conn_info_t &b)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
int exist(ip_port_t);
|
||||||
|
conn_info_t *& find_p(ip_port_t); //be aware,the adress may change after rehash
|
||||||
|
conn_info_t & find(ip_port_t) ; //be aware,the adress may change after rehash
|
||||||
|
int insert(ip_port_t);
|
||||||
|
|
||||||
|
int erase(unordered_map<u64_t,conn_info_t*>::iterator erase_it);
|
||||||
|
int clear_inactive();
|
||||||
|
int clear_inactive0();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern conn_manager_t conn_manager;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CONNECTION_H_ */
|
123
delay_manager.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* delay_manager.cpp
|
||||||
|
*
|
||||||
|
* Created on: Sep 15, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
#include "delay_manager.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
int delay_data_t::handle()
|
||||||
|
{
|
||||||
|
return my_send(dest,data,len)>=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
delay_manager_t::delay_manager_t()
|
||||||
|
{
|
||||||
|
capacity=0;
|
||||||
|
|
||||||
|
if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"timer_fd create error");
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
itimerspec zero_its;
|
||||||
|
memset(&zero_its, 0, sizeof(zero_its));
|
||||||
|
|
||||||
|
timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
delay_manager_t::~delay_manager_t()
|
||||||
|
{
|
||||||
|
//TODO ,we currently dont need to deconstruct it
|
||||||
|
}
|
||||||
|
|
||||||
|
int delay_manager_t::get_timer_fd()
|
||||||
|
{
|
||||||
|
return timer_fd;
|
||||||
|
}
|
||||||
|
//int add(my_time_t delay,const dest_t &dest,const char *data,int len);
|
||||||
|
int delay_manager_t::add(my_time_t delay,const dest_t &dest,char *data,int len)
|
||||||
|
{
|
||||||
|
delay_data_t delay_data;
|
||||||
|
delay_data.dest=dest;
|
||||||
|
//delay_data.data=data;
|
||||||
|
delay_data.len=len;
|
||||||
|
|
||||||
|
if(capacity!=0&&int(delay_mp.size()) >=capacity)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"max pending packet reached,ignored\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(delay==0)
|
||||||
|
{
|
||||||
|
static char buf[buf_len];
|
||||||
|
delay_data.data=buf;
|
||||||
|
memcpy(buf,data,len);
|
||||||
|
int ret=delay_data.handle();
|
||||||
|
if (ret != 0) {
|
||||||
|
mylog(log_trace, "handle() return %d\n", ret);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_data_t tmp=delay_data;
|
||||||
|
tmp.data=(char *)malloc(delay_data.len+100);
|
||||||
|
if(!tmp.data)
|
||||||
|
{
|
||||||
|
mylog(log_warn, "malloc() returned null in delay_manager_t::add()");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(tmp.data,data,delay_data.len);
|
||||||
|
|
||||||
|
my_time_t tmp_time=get_current_time_us();
|
||||||
|
tmp_time+=delay;
|
||||||
|
|
||||||
|
delay_mp.insert(make_pair(tmp_time,tmp));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int delay_manager_t::check()
|
||||||
|
{
|
||||||
|
if(!delay_mp.empty())
|
||||||
|
{
|
||||||
|
my_time_t current_time;
|
||||||
|
|
||||||
|
multimap<my_time_t,delay_data_t>::iterator it;
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
int ret=0;
|
||||||
|
it=delay_mp.begin();
|
||||||
|
if(it==delay_mp.end()) break;
|
||||||
|
|
||||||
|
current_time=get_current_time_us();
|
||||||
|
if(it->first <= current_time)
|
||||||
|
{
|
||||||
|
ret=it->second.handle();
|
||||||
|
if (ret != 0) {
|
||||||
|
mylog(log_trace, "handle() return %d\n", ret);
|
||||||
|
}
|
||||||
|
free(it->second.data);
|
||||||
|
delay_mp.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(!delay_mp.empty())
|
||||||
|
{
|
||||||
|
itimerspec its;
|
||||||
|
memset(&its.it_interval,0,sizeof(its.it_interval));
|
||||||
|
its.it_value.tv_sec=delay_mp.begin()->first/1000000llu;
|
||||||
|
its.it_value.tv_nsec=(delay_mp.begin()->first%1000000llu)*1000llu;
|
||||||
|
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
131
delay_manager.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* delay_manager.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 15, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DELAY_MANAGER_H_
|
||||||
|
#define DELAY_MANAGER_H_
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
//enum delay_type_t {none=0,enum_sendto_u64,enum_send_fd,client_to_local,client_to_remote,server_to_local,server_to_remote};
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct fd_ip_port_t
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
u32_t ip;
|
||||||
|
u32_t port;
|
||||||
|
};
|
||||||
|
union dest_t
|
||||||
|
{
|
||||||
|
fd_ip_port_t fd_ip_port;
|
||||||
|
int fd;
|
||||||
|
u64_t u64;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct my_timer_t
|
||||||
|
{
|
||||||
|
int timer_fd;
|
||||||
|
fd64_t timer_fd64;
|
||||||
|
my_timer_t()
|
||||||
|
{
|
||||||
|
if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"timer_fd create error");
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
timer_fd64=fd_manager.create(timer_fd);
|
||||||
|
}
|
||||||
|
my_timer_t(const my_timer_t &b)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
~my_timer_t()
|
||||||
|
{
|
||||||
|
fd_manager.fd64_close(timer_fd64);
|
||||||
|
}
|
||||||
|
int add_fd_to_epoll(int epoll_fd)
|
||||||
|
{
|
||||||
|
epoll_event ev;;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = timer_fd;
|
||||||
|
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int add_fd64_to_epoll(int epoll_fd)
|
||||||
|
{
|
||||||
|
epoll_event ev;;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = timer_fd64;
|
||||||
|
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int get_timer_fd()
|
||||||
|
{
|
||||||
|
return timer_fd;
|
||||||
|
}
|
||||||
|
fd64_t get_timer_fd64()
|
||||||
|
{
|
||||||
|
return timer_fd64;
|
||||||
|
}
|
||||||
|
int set_timer_repeat_us(my_time_t my_time)
|
||||||
|
{
|
||||||
|
itimerspec its;
|
||||||
|
memset(&its,0,sizeof(its));
|
||||||
|
its.it_interval.tv_sec=my_time/1000000llu;
|
||||||
|
its.it_interval.tv_nsec=my_time%1000000llu*1000llu;
|
||||||
|
its.it_value.tv_nsec=1; //imidiately
|
||||||
|
timerfd_settime(timer_fd,0,&its,0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int set_timer_abs_us(my_time_t my_time)
|
||||||
|
{
|
||||||
|
itimerspec its;
|
||||||
|
memset(&its,0,sizeof(its));
|
||||||
|
its.it_value.tv_sec=my_time/1000000llu;
|
||||||
|
its.it_value.tv_nsec=my_time%1000000llu*1000llu;
|
||||||
|
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct delay_data_t
|
||||||
|
{
|
||||||
|
dest_t dest;
|
||||||
|
//int left_time;//
|
||||||
|
char * data;
|
||||||
|
int len;
|
||||||
|
int handle();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct delay_manager_t
|
||||||
|
{
|
||||||
|
int timer_fd;
|
||||||
|
int capacity;
|
||||||
|
multimap<my_time_t,delay_data_t> delay_mp; //unit us,1 us=0.001ms
|
||||||
|
delay_manager_t();
|
||||||
|
delay_manager_t(delay_manager_t &b)
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
int set_capacity(int a){capacity=a;return 0;}
|
||||||
|
~delay_manager_t();
|
||||||
|
int get_timer_fd();
|
||||||
|
int check();
|
||||||
|
int add(my_time_t delay,const dest_t &dest,char *data,int len);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DELAY_MANAGER_H_ */
|
285
doc/README.zh-cn.md
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
# UDPspeeder
|
||||||
|

|
||||||
|
|
||||||
|
双边网络加速工具,软件本身的功能是加速UDP;不过,配合vpn可以加速全流量(包括TCP/UDP/ICMP)。通过合理配置,可以加速游戏,降低游戏的丢包和延迟;也可以加速下载和看视频这种大流量的应用。用1.5倍的流量,就可以把10%的丢包率降低到万分之一以下。跟 kcptun/finalspeed/BBR 等现有方案比,主要优势是可以加速 UDP 和 ICMP,现有方案几乎都只能加速 TCP。
|
||||||
|
|
||||||
|
我自己稳定用了几个月,用来加速美服的Brawl Stars和亚服的Mobile Legend,效果不错,加速前卡得几乎没法玩,加速后就没怎么卡过了。用来看视频也基本满速。
|
||||||
|
|
||||||
|
最新的版本是v2版,在v1版的基础上增加了FEC功能,更省流量。如果你用的是v1版(路由器固件里自带的集成版很可能是v1版的),请看[v1版主页](/doc/README.zh-cn.v1.md)
|
||||||
|
|
||||||
|
配合vpn加速全流量的原理图(已测试支持VPN的有OpenVPN、L2TP、$\*\*\*VPN):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
对于某些运营商,UDPspeeder跟udp2raw配合可以达到更好的速度,udp2raw负责把UDP伪装成TCP,来绕过运营商的UDP限速。
|
||||||
|
|
||||||
|
udp2raw的repo:
|
||||||
|
|
||||||
|
https://github.com/wangyu-/udp2raw-tunnel
|
||||||
|
|
||||||
|
如果你嫌UDPspeeder+OpenVPN麻烦,你可以尝试tinyFecVPN,一个集成了UDPspeeder功能的VPN:
|
||||||
|
|
||||||
|
tinyFecVPN的repo:
|
||||||
|
|
||||||
|
https://github.com/wangyu-/tinyFecVPN
|
||||||
|
|
||||||
|
|
||||||
|
#### 效果
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
#### 原理简介
|
||||||
|
主要原理是通过冗余数据来对抗网络的丢包,发送冗余数据的方式支持FEC(Forward Error Correction)和多倍发包,其中FEC算法是Reed-Solomon。
|
||||||
|
|
||||||
|
FEC方式的原理图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 其他功能
|
||||||
|
对包的内容和长度做随机化(可以理解为混淆),从抓包看不出你发送了冗余数据,不用担心vps被封。
|
||||||
|
|
||||||
|
在多个冗余包之间引入延迟(时间可配)来对抗突发性的丢包,避开中间路由器因为瞬时buffer长度过长而连续丢掉所有副本。
|
||||||
|
|
||||||
|
模拟一定的延迟抖动(时间可配),这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。
|
||||||
|
|
||||||
|
输出UDP收发情况报告,可以看出丢包率。
|
||||||
|
|
||||||
|
模拟丢包,模拟延迟,模拟jitter。便于通过实验找出应用卡顿的原因。
|
||||||
|
|
||||||
|
client支持多个udp连接,server也支持多个client
|
||||||
|
|
||||||
|
# 简明操作说明
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
Linux主机,可以是桌面版,可以是android手机/平板,可以是openwrt路由器,也可以是树莓派。Release中提供了`amd64`、`x86`、`arm`、`mips_be`、`mips_le`的预编译binary.
|
||||||
|
|
||||||
|
对于windows和mac用户,在虚拟机中可以稳定使用(speeder跑在Linux里,其他应用照常跑在window里,桥接模式测试可用)。可以使用[这个](https://github.com/wangyu-/udp2raw-tunnel/releases/download/20171108.0/lede-17.01.2-x86_virtual_machine_image.zip)虚拟机镜像,大小只有7.5mb,免去在虚拟机里装系统的麻烦;虚拟机自带ssh server,可以scp拷贝文件,可以ssh进去,可以复制粘贴,root密码123456。
|
||||||
|
|
||||||
|
android版需要通过terminal运行。
|
||||||
|
|
||||||
|
##### 注意
|
||||||
|
在使用虚拟机时,建议手动指定桥接到哪个网卡,不要设置成自动。否则可能会桥接到错误的网卡。
|
||||||
|
|
||||||
|
### 安装
|
||||||
|
下载编译好的二进制文件,解压到本地和服务器的任意目录。
|
||||||
|
|
||||||
|
https://github.com/wangyu-/UDPspeeder/releases
|
||||||
|
|
||||||
|
### 运行
|
||||||
|
假设你有一个server,ip为44.55.66.77,有一个服务监听在udp 7777端口。 假设你需要加速本地到44.55.66.77:7777的流量。
|
||||||
|
```
|
||||||
|
在server端运行:
|
||||||
|
./speederv2 -s -l0.0.0.0:4096 -r127.0.0.1:7777 -f20:10 -k "passwd" --mode 0
|
||||||
|
|
||||||
|
在client端运行:
|
||||||
|
./speederv2 -c -l0.0.0.0:3333 -r44.55.66.77:4096 -f20:10 -k "passwd" --mode 0
|
||||||
|
```
|
||||||
|
|
||||||
|
现在client和server之间建立起了tunnel。想要连接44.55.66.77:7777,只需要连接 127.0.0.1:3333。来回的所有的udp流量会被加速。
|
||||||
|
|
||||||
|
##### 备注:
|
||||||
|
|
||||||
|
`-f20:10`表示对每20个原始数据发送10个冗余包。`-f20:10` 和`-f 20:10`都是可以的,空格可以省略,对于所有的单字节option都是如此。对于双字节option,例如后面会提到的`--mode 0`,空格不可以省略。
|
||||||
|
|
||||||
|
`-k` 指定一个字符串,开启简单的异或加密
|
||||||
|
|
||||||
|
推荐使用`--mode 0`选项,否则你可能需要考虑MTU问题。
|
||||||
|
|
||||||
|
##### 注意
|
||||||
|
|
||||||
|
这里推荐的参数是给日常/非游戏情况下使用的;玩游戏请用 [使用经验](https://github.com/wangyu-/UDPspeeder/wiki/使用经验) 里推荐的参数。
|
||||||
|
|
||||||
|
# 进阶操作说明
|
||||||
|
|
||||||
|
### 命令选项
|
||||||
|
```
|
||||||
|
UDPspeeder V2
|
||||||
|
git version: 6f55b8a2fc build date: Nov 19 2017 06:11:23
|
||||||
|
repository: https://github.com/wangyu-/UDPspeeder
|
||||||
|
|
||||||
|
usage:
|
||||||
|
run as client: ./this_program -c -l local_listen_ip:local_port -r server_ip:server_port [options]
|
||||||
|
run as server: ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]
|
||||||
|
|
||||||
|
common options, must be same on both sides:
|
||||||
|
-k,--key <string> key for simple xor encryption. if not set, xor is disabled
|
||||||
|
main options:
|
||||||
|
-f,--fec x:y forward error correction, send y redundant packets for every x packets
|
||||||
|
--timeout <number> how long could a packet be held in queue before doing fec, unit: ms, default: 8ms
|
||||||
|
--report <number> turn on send/recv report, and set a period for reporting, unit: s
|
||||||
|
advanced options:
|
||||||
|
--mode <number> fec-mode,available values: 0,1; mode 0(default) costs less bandwidth,no mtu problem.
|
||||||
|
mode 1 usually introduces less latency, but you have to care about mtu.
|
||||||
|
--mtu <number> mtu. for mode 0, the program will split packet to segment smaller than mtu value.
|
||||||
|
for mode 1, no packet will be split, the program just check if the mtu is exceed.
|
||||||
|
default value: 1250. you typically shouldnt change this value.
|
||||||
|
-q,--queue-len <number> fec queue len, only for mode 0, fec will be performed immediately after queue is full.
|
||||||
|
default value: 200.
|
||||||
|
-j,--jitter <number> simulated jitter. randomly delay first packet for 0~<number> ms, default value: 0.
|
||||||
|
do not use if you dont know what it means.
|
||||||
|
-i,--interval <number> scatter each fec group to a interval of <number> ms, to protect burst packet loss.
|
||||||
|
default value: 0. do not use if you dont know what it means.
|
||||||
|
--random-drop <number> simulate packet loss, unit: 0.01%. default value: 0.
|
||||||
|
--disable-obscure <number> disable obscure, to save a bit bandwidth and cpu.
|
||||||
|
developer options:
|
||||||
|
--fifo <string> use a fifo(named pipe) for sending commands to the running program, so that you
|
||||||
|
can change fec encode parameters dynamically, check readme.md in repository for
|
||||||
|
supported commands.
|
||||||
|
-j ,--jitter jmin:jmax similiar to -j above, but create jitter randomly between jmin and jmax
|
||||||
|
-i,--interval imin:imax similiar to -i above, but scatter randomly between imin and imax
|
||||||
|
--decode-buf <number> size of buffer of fec decoder,u nit: packet, default: 2000
|
||||||
|
--fix-latency <number> try to stabilize latency, only for mode 0
|
||||||
|
--delay-capacity <number> max number of delayed packets
|
||||||
|
--disable-fec <number> completely disable fec, turn the program into a normal udp tunnel
|
||||||
|
--sock-buf <number> buf size for socket, >=10 and <=10240, unit: kbyte, default: 1024
|
||||||
|
log and help options:
|
||||||
|
--log-level <number> 0: never 1: fatal 2: error 3: warn
|
||||||
|
4: info (default) 5: debug 6: trace
|
||||||
|
--log-position enable file name, function name, line number in log
|
||||||
|
--disable-color disable log color
|
||||||
|
-h,--help print this help message
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
### 包发送选项,两端设置可以不同。 只影响本地包发送。
|
||||||
|
##### `-f` 选项
|
||||||
|
设置fec参数,影响数据的冗余度。
|
||||||
|
##### `--timeout` 选项
|
||||||
|
指定fec编码器在编码时候最多可以引入多大的延迟。越高fec越有效率,调低可以降低延迟,但是会牺牲效率。
|
||||||
|
##### `--mode` 选项 和 `--mtu`选项
|
||||||
|
|
||||||
|
简单来说`--mode 0`更省流量,没有mtu问题;`--mode 1`可以稍微降低一点延迟,需要考虑mtu;另外还有个`--mode 0 -q1`模式,多倍发包专用,没有延迟,也没有mtu问题,适合游戏,但是最耗流量。
|
||||||
|
|
||||||
|
具体见,https://github.com/wangyu-/UDPspeeder/wiki/mode和mtu选项
|
||||||
|
|
||||||
|
对于新手,建议不要纠结这些参数的具体含义,就用我在`使用经验`里推荐的设置,不要乱改参数,尤其是不要改`--mtu`。
|
||||||
|
|
||||||
|
##### `--report` 选项
|
||||||
|
数据发送和接受报告。开启后可以根据此数据推测出包速和丢包率等特征。`--report 10`表示每10秒生成一次报告。
|
||||||
|
|
||||||
|
##### `-i` 选项
|
||||||
|
指定一个时间窗口,长度为n毫秒。同一个fec分组的数据在发送时候会被均匀分散到这n毫秒中。可以对抗突发性的丢包。默认值是0,因为这个功能需要用到时钟,在某些虚拟机里时钟不稳定,可能会导致个别包出现非常大的延迟,所以默认关掉了。这个功能很有用,默认参数效果不理想时可以尝试打开,比如用`-i 10`。这个选项的跟通信原理上常说的`交错fec` `交织fec`的原理是差不多的。
|
||||||
|
|
||||||
|
##### `-j` 选项
|
||||||
|
为原始数据的发送,增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大,可以不用设-j。这个功能也需要时钟,默认关掉了,不过一般情况应该不需要这个功能。
|
||||||
|
|
||||||
|
-j选项不但可以模拟延迟抖动,也可以模拟延迟。
|
||||||
|
|
||||||
|
##### `--random-drop` 选项
|
||||||
|
随机丢包。模拟高丢包的网络环境时使用。 `--random-drop`和`-j`选项一起用,可以模拟高延迟(或者高延迟抖动)高丢包的网络,可用于测试FEC参数在各种网络环境下的表现。
|
||||||
|
|
||||||
|
##### `-q` 选项
|
||||||
|
仅对mode 0模式有用。设置fec编码器的最大队列长度。 比如`-q5`的意思是,在编码器积攒了5个数据包后,就立即发送。合理使用可以改善延迟。在下文的`使用经验`里有提到用`--mode 0 -q1` 来多倍发包。
|
||||||
|
|
||||||
|
`-q`和 `--timeout`的作用类似。`-q`决定fec编码器积攒了多少个数据包之后,立即发送。`--timeout`决定编码器收到第一个数据包以后,最多延迟多少毫秒后发送。
|
||||||
|
|
||||||
|
默认值是200,也就是尽可能多得积攒数据。
|
||||||
|
|
||||||
|
建议不要自己调整这个参数,除非是用我在`使用经验`里推荐给你的形式。
|
||||||
|
|
||||||
|
#### `--fifo` option
|
||||||
|
用fifo(命名管道)向运行中的程序发送command。例如`--fifo fifo.file`,可用的command有:
|
||||||
|
```
|
||||||
|
echo fec 19:9 > fifo.file
|
||||||
|
echo mtu 1100 > fifo.file
|
||||||
|
echo timeout 5 > fifo.file
|
||||||
|
echo queue-len 100 > fifo.file
|
||||||
|
echo mode 0 > fifo.file
|
||||||
|
```
|
||||||
|
可以动态改变fec编码器参数。可以从程序的log里看到command是否发送成功。
|
||||||
|
|
||||||
|
### 以下设置两端必须相同。
|
||||||
|
|
||||||
|
##### `-k`选项
|
||||||
|
指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
||||||
|
|
||||||
|
##### `--disable-obscure`
|
||||||
|
UDPspeeder默认情况下会对每个发出的数据包随机填充和异或一些字节(4~32字节),这样通过抓包难以发现你发了冗余数据,防止VPS被封。这个功能只是为了小心谨慎,即使你关掉这个功能,基本上也没问题,关掉可以省一些带宽和CPU。`--disable-obscure`可以关掉这个功能。
|
||||||
|
|
||||||
|
# 使用经验
|
||||||
|
|
||||||
|
https://github.com/wangyu-/UDPspeeder/wiki/使用经验
|
||||||
|
|
||||||
|
# 应用
|
||||||
|
|
||||||
|
#### UDPspeeder + OpenVPN加速任何流量,也适用于其他VPN
|
||||||
|

|
||||||
|
|
||||||
|
可以和BBR/锐速叠加,不过BBR/锐速部署在VPS上只对本地和VPS间的流量有效,对从本地和第三方服务器间的流量无效。
|
||||||
|
|
||||||
|
需要在服务端开启ipforward和NAT。在客户端改路由表(可以手动修改,也可以由OpenVPN的redirect-gateway选项自动加好)。
|
||||||
|
|
||||||
|
Linux具体配置: [UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
|
||||||
|
|
||||||
|
Windows具体配置: [win10系统UDPspeeder+OpenVPN的完整设置](https://github.com/wangyu-/UDPspeeder/wiki/win10系统UDPspeeder-OpenVPN的完整设置)
|
||||||
|
|
||||||
|
如果UDPspeeder + OpenVPN对你来说显得太麻烦了,你可以尝试一下tinyFecVPN,一个集成了UDPspeeder功能的VPN:
|
||||||
|
|
||||||
|
https://github.com/wangyu-/tinyFecVPN/
|
||||||
|
|
||||||
|
#### UDPspeeder + kcptun/finalspeed + $*** 同时加速tcp和udp流量
|
||||||
|
如果你需要用加速的tcp看视频和下载文件,这样效果可能比没有BBR的UDPspeeder+vpn方案更好。另外,如果你需要玩游戏,但是嫌配VPN麻烦,也可以用这种方案。
|
||||||
|

|
||||||
|
|
||||||
|
具体配置方法简介:
|
||||||
|
|
||||||
|
假设$\*\*\* server监听在在44.55.66.77的443端口(tcp和udp同时)。用kcptun把tcp 443映射到本地的tcp 1234;用UDPspeeder把udp 443的映射到本地的udp 1234。
|
||||||
|
然后让$\*\*\* client 去连127.0.0.1:1234就可以了,tcp和udp都被加速了。完整命令:
|
||||||
|
```
|
||||||
|
run at server side:
|
||||||
|
./kcp_server -l ":4000" -t "127.0.0.1:443" -mode fast2
|
||||||
|
./speederv2 -s -l0.0.0.0:4001 -r127.0.0.1:443 -f20:10 -k "passwd"
|
||||||
|
|
||||||
|
run at client side:
|
||||||
|
./kcp_client -l ":1234" -r "44.55.66.77:4000" -mode fast2
|
||||||
|
./speederv2 -c -l0.0.0.0:1234 -r44.55.66.77:4001 -f20:10 -k "passwd"
|
||||||
|
```
|
||||||
|
|
||||||
|
这就是全部的命令了。issue里有很多人困惑于怎么把tcp和udp流量"分开",其实很简单就可以做到。
|
||||||
|
|
||||||
|
如果只需要加速UDP,不需要加速TCP,可以把kcptun换成其他的任意端口转发方式,比如ncat/socat/ssh tunnel/iptables/[tinyPortMapper](https://github.com/wangyu-/tinyPortMapper/releases)。
|
||||||
|
|
||||||
|
如果你没有kcptun只有BBR/锐速的话,也可以把kcptun换成ncat/socat/ssh tunnel/iptables/[tinyPortMapper](https://github.com/wangyu-/tinyPortMapper/releases)。这样,TCP流量由锐速/BBR加速,UDP由UDPspeeder加速。
|
||||||
|
|
||||||
|
另外,即使你不想使用$\*\*\*的TCP功能,你也必须把$\*\*\*的TCP端口转发过来,否则无法使用UDP功能,这是socks5协议的工作方式决定的。($\*\*\*-redir方式不受此限制)
|
||||||
|
|
||||||
|
#### UDPspeeder + openvpn + $*** 混合方案,也适用于其他VPN
|
||||||
|
也是我正在用的方案。优点是可以随时在vpn和$\*\*\*方案间快速切换。
|
||||||
|
实际部署起来比图中看起来的还要简单。不需要改路由表,不需要写iptables规则和开启NAT,需要做的只是用openvpn分配的ip访问$*** server。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
(也可以把图中的$*** server换成其他的socks5 server,这样就不需要$*** client了)
|
||||||
|
|
||||||
|
可以和BBR/锐速叠加,BBR/锐速只要部署在VPS上就有效。
|
||||||
|
|
||||||
|
也可以用[tinyFecVPN](https://github.com/wangyu-/tinyFecVPN/) + $\*\*\* ,配置起来更简单。
|
||||||
|
|
||||||
|
# 应用实例
|
||||||
|
|
||||||
|
#### win10系统UDPspeeder+OpenVPN的完整设置
|
||||||
|
|
||||||
|
https://github.com/wangyu-/UDPspeeder/wiki/win10系统UDPspeeder-OpenVPN的完整设置
|
||||||
|
|
||||||
|
|
||||||
|
#### UDPspeeder+OpenVPN运行在linux上,透明加速linux本机的网络
|
||||||
|
|
||||||
|
https://github.com/wangyu-/tinyFecVPN/wiki/tinyFecVPN运行在linux上,透明加速linux本机的网络
|
||||||
|
|
||||||
|
|
||||||
|
#### UDPspeeder+OpenVPN运行在虚拟机中,加速windows和局域网内其他主机的网络
|
||||||
|
|
||||||
|
https://github.com/wangyu-/tinyFecVPN/wiki/tinyFecVPN运行在虚拟机中,加速windows和局域网内其他主机的网络
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 用树莓派做路由器,搭建透明代理,加速游戏主机的网络
|
||||||
|
|
||||||
|
https://github.com/wangyu-/UDPspeeder/wiki/用树莓派做路由器,搭建透明代理,加速游戏主机的网络
|
||||||
|
|
||||||
|
# 编译教程
|
||||||
|
暂时先参考udp2raw的这篇教程,几乎一样的过程。
|
||||||
|
|
||||||
|
https://github.com/wangyu-/udp2raw-tunnel/blob/master/doc/build_guide.zh-cn.md
|
||||||
|
|
154
doc/README.zh-cn.v1.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# UDPspeeder (v1)
|
||||||
|

|
||||||
|
UDP双边加速工具,降低丢包率,配合vpn可以加速任何协议,尤其适用于加速游戏和网页打开速度;同时也是一个UDP连接的调试和统计工具。
|
||||||
|
|
||||||
|
这个是我自己稳定用了一个月的项目,用来加速美服的Brawl Stars和亚服的Mobile Legend,效果不错。加速前卡得几乎没法玩,加速后就没怎么卡过了。
|
||||||
|
|
||||||
|
注:目前最新版是v2版,这个是v1版的主页
|
||||||
|
|
||||||
|
#### 效果
|
||||||
|

|
||||||
|
#### 原理简介
|
||||||
|
目前原理是多倍发包。以后会做各种优化,比如:对高频率的短包先合并再冗余;FEC(Forward Error Correction),在包速低的时候多倍发包,包速高时用FEC(这些功能在v2版里已经实现)。
|
||||||
|
|
||||||
|
跟net-speeder比,优势在于client和server会把收到的多余包自动去掉,这个过程对上层透明,没有兼容性问题。而且发出的冗余数据包会做长度和内容的随机化,抓包是看不出发了冗余数据的,所以不用担心vps被封的问题。
|
||||||
|
|
||||||
|
每个冗余数据包都是间隔数毫秒(可配置)以后延迟发出的,可以避开中间路由器因为瞬时buffer长度过长而连续丢掉所有副本。
|
||||||
|
|
||||||
|
模拟一定的延迟抖动,这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。
|
||||||
|
|
||||||
|
#### 适用场景
|
||||||
|
绝大部分流量不高的情况。程序本身加速udp,但是配合openvpn可以加速任何流量。网络状况不好时,游戏卡得没法玩,或者网页卡得没法打开,使用起来效果最好。对于解决语音通话的断断续续效果也不错。不适合大流量的场景,比如BT下载和在线看视频。 无论从自己使用效果的角度,还是从国际出口带宽占用的角度,都建议不要在大流量环境使用。
|
||||||
|
|
||||||
|
#### 其他功能
|
||||||
|
输出UDP收发情况报告,可以看出丢包率。
|
||||||
|
|
||||||
|
模拟丢包,模拟延迟,模拟jitter。便于通过实验找出应用卡顿的原因。
|
||||||
|
|
||||||
|
重复包过滤功能可以关掉,模拟网络本身有重复包的情况。用来测试应用对重复报的支持情况。
|
||||||
|
|
||||||
|
client支持多个udp连接,server也支持多个client
|
||||||
|
|
||||||
|
目前有amd64,x86,ar71xx,树莓派armv7和android的binary
|
||||||
|
|
||||||
|
如果你需要绕过UDP屏蔽/QoS,或者需要连接复用/连接保持功能,或者是加密。解决方案在另一个repo(可以跟UDPspeeder一起使用):
|
||||||
|
|
||||||
|
https://github.com/wangyu-/udp2raw-tunnel
|
||||||
|
|
||||||
|
# 简明操作说明
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
Linux主机,可以是桌面版,可以是android手机/平板,可以是openwrt路由器,也可以是树莓派。在windows和mac上配合虚拟机可以稳定使用(speeder跑在Linux里,其他应用照常跑在window里,桥接模式测试可用)。
|
||||||
|
|
||||||
|
android版需要通过terminal运行。
|
||||||
|
|
||||||
|
### 安装
|
||||||
|
下载编译好的二进制文件,解压到本地和服务器的任意目录。
|
||||||
|
|
||||||
|
https://github.com/wangyu-/UDPspeeder/releases
|
||||||
|
|
||||||
|
### 运行
|
||||||
|
假设你有一个server,ip为44.55.66.77,有一个服务监听在udp 7777端口。 假设你需要加速本地到44.55.66.77:7777的流量。
|
||||||
|
```
|
||||||
|
在client端运行:
|
||||||
|
./speeder_ar71xx -l0.0.0.0:3333 -r 44.55.66.77:8855 -c -d2 -k "passwd"
|
||||||
|
|
||||||
|
在server端运行:
|
||||||
|
./speeder_amd64 -l0.0.0.0:8855 -r127.0.0.1:7777 -s -d2 -k "passwd"
|
||||||
|
```
|
||||||
|
|
||||||
|
现在client和server之间建立起了tunnel。想要连接44.55.66.77:7777,只需要连接 127.0.0.1:3333。来回的所有的udp流量会被加速。
|
||||||
|
|
||||||
|
###### 注:
|
||||||
|
|
||||||
|
-d2 表示除了本来的包以外,额外再发2个冗余包。可调。
|
||||||
|
|
||||||
|
-k 指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
||||||
|
|
||||||
|
# 进阶操作说明
|
||||||
|
|
||||||
|
### 命令选项
|
||||||
|
```
|
||||||
|
UDPspeeder
|
||||||
|
git version:b4bd385e88 build date:Sep 11 2017 10:29:25
|
||||||
|
repository: https://github.com/wangyu-/UDPspeeder
|
||||||
|
|
||||||
|
usage:
|
||||||
|
run as client : ./this_program -c -l local_listen_ip:local_port -r server_ip:server_port [options]
|
||||||
|
run as server : ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]
|
||||||
|
|
||||||
|
common option,must be same on both sides:
|
||||||
|
-k,--key <string> key for simple xor encryption,default:"secret key"
|
||||||
|
main options:
|
||||||
|
-d <number> duplicated packet number, -d 0 means no duplicate. default value:0
|
||||||
|
-t <number> duplicated packet delay time, unit: 0.1ms,default value:20(2ms)
|
||||||
|
-j <number> simulated jitter.randomly delay first packet for 0~jitter_value*0.1 ms,to
|
||||||
|
create simulated jitter.default value:0.do not use if you dont
|
||||||
|
know what it means
|
||||||
|
--report <number> turn on udp send/recv report,and set a time interval for reporting,unit:s
|
||||||
|
advanced options:
|
||||||
|
-t tmin:tmax simliar to -t above,but delay randomly between tmin and tmax
|
||||||
|
-j jmin:jmax simliar to -j above,but create jitter randomly between jmin and jmax
|
||||||
|
--random-drop <number> simulate packet loss ,unit:0.01%
|
||||||
|
--disable-filter disable duplicate packet filter.
|
||||||
|
-m <number> max pending packets,to prevent the program from eating up all your memory,
|
||||||
|
default value:0(disabled).
|
||||||
|
other options:
|
||||||
|
--log-level <number> 0:never 1:fatal 2:error 3:warn
|
||||||
|
4:info (default) 5:debug 6:trace
|
||||||
|
--log-position enable file name,function name,line number in log
|
||||||
|
--disable-color disable log color
|
||||||
|
--sock-buf <number> buf size for socket,>=10 and <=10240,unit:kbyte,default:1024
|
||||||
|
-h,--help print this help message
|
||||||
|
|
||||||
|
```
|
||||||
|
### 包发送选项,两端设置可以不同。 只影响本地包发送。
|
||||||
|
##### -d 选项
|
||||||
|
设置冗余包数量。
|
||||||
|
##### -t 选项
|
||||||
|
为冗余包的发送,增加一个延迟.对中间路由buffer做优化,应对瞬时Buffer过长导致的连续丢包.对于多个冗余包,依次在前一个包的基础上增加这个延迟。
|
||||||
|
##### -j 选项
|
||||||
|
为原始数据的发送,增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大。可以不用设-j
|
||||||
|
|
||||||
|
##### --report 选项
|
||||||
|
数据发送和接受报告。开启后可以根据此数据推测出包速和丢包率等特征。
|
||||||
|
|
||||||
|
##### 加强版 -t 选项
|
||||||
|
跟普通-t类似,允许设置最大值最小值,用随机延迟发送冗余包。
|
||||||
|
|
||||||
|
##### 加强版 -j 选项
|
||||||
|
允许给jitter选项设置最大值最小值。在这个区间随机化jitter。如果最大值最小值一样就是模拟延迟。可以模拟高延迟、高jitter的网络环境。
|
||||||
|
|
||||||
|
##### --random-drop 选项
|
||||||
|
随机丢包。模拟恶劣的网络环境时使用。
|
||||||
|
|
||||||
|
### 包接收选项,两端设置可以不同。只影响本地包接受
|
||||||
|
##### --disable-filter
|
||||||
|
关闭重复包过滤器。这样配合-d 选项可以模拟有重复包的网络环境。
|
||||||
|
|
||||||
|
# 应用
|
||||||
|
|
||||||
|
#### UDPspeeder + openvpn加速任何流量
|
||||||
|
如果你只是需要玩游戏,效果预期会kcp/finalspeed方案更好。可以优化tcp游戏的延迟(通过冗余发包,避免了上层的重传)。比如魔兽世界用的是tcp连接。
|
||||||
|

|
||||||
|
|
||||||
|
跟openvpn via kcptun方式的对比:
|
||||||
|
|
||||||
|
kcptun在udp层有RS code,也是一种冗余传输,通过openvpn把流量转成tcp,再通过kcptun加速是有一定效果的。但是tcp只支持按序到达。按序到达的意思是,如果你发了1 2 3 4 5 6,6个包,如果第一个包丢了,那么必须等第一个包重传成功以后 2 3 4 5 6 才能到达;只要有一个包不到,后续数据包就要一直等待。用tcp承载udp流量会破坏udp的实时性。会造成游戏卡顿更严重。
|
||||||
|
|
||||||
|
udp协议本身是ip协议加上了端口之后的直接封装,udp继承了ip协议的实时/乱序到达特性,更适合中转vpn。
|
||||||
|
|
||||||
|
#### UDPspeeder + kcptun/finalspeed + $*** 同时加速tcp和udp流量
|
||||||
|
如果你需要用加速的tcp看视频和下载文件,这样效果比vpn方案更好。不论是速度,还是流量的耗费上。
|
||||||
|

|
||||||
|
|
||||||
|
#### UDPspeeder + openvpn + $*** 混合方案
|
||||||
|
也是我正在用的方案。优点是可以随时在vpn和$*** 方案间快速切换。
|
||||||
|
实际部署起来比图中看起来的还要简单。不需要改路由表,需要做的只是用openvpn的ip访问$*** server。
|
||||||
|
|
||||||
|

|
||||||
|
(也可以把图中的$*** server换成其他的socks5 server,这样连$*** client也不需要了)
|
||||||
|
# 编译教程
|
||||||
|
暂时先参考udp2raw的这篇教程,几乎一样的过程。
|
||||||
|
|
||||||
|
https://github.com/wangyu-/udp2raw-tunnel/blob/master/doc/build_guide.zh-cn.md
|
107
doc/udpspeeder_openvpn.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
|
||||||
|
# UDPspeeder + openvpn config guide
|
||||||
|

|
||||||
|
|
||||||
|
# UDPspeeder command
|
||||||
|
|
||||||
|
#### run at server side
|
||||||
|
```
|
||||||
|
./speederv2 -s -l0.0.0.0:8855 -r 127.0.0.1:7777 -f20:10
|
||||||
|
```
|
||||||
|
|
||||||
|
#### run at client side
|
||||||
|
assume server ip is 45.66.77.88
|
||||||
|
```
|
||||||
|
./speederv2 -c -l0.0.0.0:3333 -r 45.66.77.88:8855 -f20:10
|
||||||
|
```
|
||||||
|
|
||||||
|
# openvpn config
|
||||||
|
|
||||||
|
#### client side config
|
||||||
|
```
|
||||||
|
client
|
||||||
|
dev tun100
|
||||||
|
proto udp
|
||||||
|
|
||||||
|
remote 127.0.0.1 3333
|
||||||
|
resolv-retry infinite
|
||||||
|
nobind
|
||||||
|
persist-key
|
||||||
|
persist-tun
|
||||||
|
|
||||||
|
ca /root/add-on/openvpn/ca.crt
|
||||||
|
cert /root/add-on/openvpn/client.crt
|
||||||
|
key /root/add-on/openvpn/client.key
|
||||||
|
|
||||||
|
keepalive 3 20
|
||||||
|
verb 3
|
||||||
|
mute 20
|
||||||
|
|
||||||
|
comp-lzo no
|
||||||
|
|
||||||
|
fragment 1200 ##### very important you can turn it up a bit. but, the lower the safer
|
||||||
|
mssfix 1200 ##### very important
|
||||||
|
|
||||||
|
sndbuf 2000000 ##### important
|
||||||
|
rcvbuf 2000000 ##### important
|
||||||
|
txqueuelen 4000 ##### suggested
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### server side config
|
||||||
|
```
|
||||||
|
local 0.0.0.0
|
||||||
|
port 7777
|
||||||
|
proto udp
|
||||||
|
dev tun
|
||||||
|
|
||||||
|
ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt
|
||||||
|
cert /etc/openvpn/easy-rsa/2.0/keys/server.crt
|
||||||
|
key /etc/openvpn/easy-rsa/2.0/keys/server.key
|
||||||
|
dh /etc/openvpn/easy-rsa/2.0/keys/dh1024.pem
|
||||||
|
|
||||||
|
server 10.222.2.0 255.255.255.0
|
||||||
|
ifconfig 10.222.2.1 10.222.2.6
|
||||||
|
|
||||||
|
client-to-client
|
||||||
|
duplicate-cn
|
||||||
|
keepalive 10 60
|
||||||
|
|
||||||
|
max-clients 50
|
||||||
|
|
||||||
|
persist-key
|
||||||
|
persist-tun
|
||||||
|
|
||||||
|
status /etc/openvpn/openvpn-status.log
|
||||||
|
|
||||||
|
verb 3
|
||||||
|
mute 20
|
||||||
|
|
||||||
|
comp-lzo no
|
||||||
|
|
||||||
|
fragment 1200 ##### very important you can turn it up a bit. but, the lower the safer
|
||||||
|
mssfix 1200 ##### very important
|
||||||
|
|
||||||
|
sndbuf 2000000 ##### important
|
||||||
|
rcvbuf 2000000 ##### important
|
||||||
|
txqueuelen 4000 ##### suggested
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Note:
|
||||||
|
If you use the `redirect-gateway` option of OpenVPN, you may need to add a route exception for your remote server ip at client side.Otherwise OpenVPN may hijack UDPspeeder 's traffic.
|
||||||
|
|
||||||
|
For example, depend on your network environment, the command may looks like:
|
||||||
|
```
|
||||||
|
ip route add 44.55.66.77 via 44.55.66.1
|
||||||
|
```
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
ip route add 44.55.66.77 dev XXX
|
||||||
|
```
|
||||||
|
(run at client side)
|
||||||
|
|
||||||
|
##### Other Info
|
||||||
|
You can also use tinyFecVPN,a lightweight VPN with build-in FEC support:
|
||||||
|
|
||||||
|
https://github.com/wangyu-/tinyFecVPN
|
63
fd_manager.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* fd_manager.cpp
|
||||||
|
*
|
||||||
|
* Created on: Sep 25, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "fd_manager.h"
|
||||||
|
int fd_manager_t::fd_exist(int fd)
|
||||||
|
{
|
||||||
|
return fd_to_fd64_mp.find(fd)!=fd_to_fd64_mp.end();
|
||||||
|
}
|
||||||
|
int fd_manager_t::exist(fd64_t fd64)
|
||||||
|
{
|
||||||
|
return fd64_to_fd_mp.find(fd64)!=fd64_to_fd_mp.end();
|
||||||
|
}
|
||||||
|
int fd_manager_t::to_fd(fd64_t fd64)
|
||||||
|
{
|
||||||
|
assert(exist(fd64));
|
||||||
|
return fd64_to_fd_mp[fd64];
|
||||||
|
}
|
||||||
|
void fd_manager_t::fd64_close(fd64_t fd64)
|
||||||
|
{
|
||||||
|
assert(exist(fd64));
|
||||||
|
int fd=fd64_to_fd_mp[fd64];
|
||||||
|
fd64_to_fd_mp.erase(fd64);
|
||||||
|
fd_to_fd64_mp.erase(fd);
|
||||||
|
if(exist_info(fd64))
|
||||||
|
{
|
||||||
|
fd_info_mp.erase(fd64);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
void fd_manager_t::reserve(int n)
|
||||||
|
{
|
||||||
|
fd_to_fd64_mp.reserve(n);
|
||||||
|
fd64_to_fd_mp.reserve(n);
|
||||||
|
fd_info_mp.reserve(n);
|
||||||
|
}
|
||||||
|
u64_t fd_manager_t::create(int fd)
|
||||||
|
{
|
||||||
|
assert(!fd_exist(fd));
|
||||||
|
fd64_t fd64=counter++;
|
||||||
|
fd_to_fd64_mp[fd]=fd64;
|
||||||
|
fd64_to_fd_mp[fd64]=fd;
|
||||||
|
return fd64;
|
||||||
|
}
|
||||||
|
fd_manager_t::fd_manager_t()
|
||||||
|
{
|
||||||
|
counter=u32_t(-1);
|
||||||
|
counter+=100;
|
||||||
|
reserve(10007);
|
||||||
|
}
|
||||||
|
fd_info_t & fd_manager_t::get_info(fd64_t fd64)
|
||||||
|
{
|
||||||
|
assert(exist(fd64));
|
||||||
|
return fd_info_mp[fd64];
|
||||||
|
}
|
||||||
|
int fd_manager_t::exist_info(fd64_t fd64)
|
||||||
|
{
|
||||||
|
return fd_info_mp.find(fd64)!=fd_info_mp.end();
|
||||||
|
}
|
38
fd_manager.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* fd_manager.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 25, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FD_MANAGER_H_
|
||||||
|
#define FD_MANAGER_H_
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct fd_manager_t //conver fd to a uniq 64bit number,avoid fd value conflict caused by close and re-create
|
||||||
|
//this class is not strictly necessary,it just makes epoll fd handling easier
|
||||||
|
{
|
||||||
|
fd_info_t & get_info(fd64_t fd64);
|
||||||
|
int exist_info(fd64_t);
|
||||||
|
int exist(fd64_t fd64);
|
||||||
|
int to_fd(fd64_t);
|
||||||
|
void fd64_close(fd64_t fd64);
|
||||||
|
void reserve(int n);
|
||||||
|
u64_t create(int fd);
|
||||||
|
fd_manager_t();
|
||||||
|
private:
|
||||||
|
u64_t counter;
|
||||||
|
unordered_map<int,fd64_t> fd_to_fd64_mp;
|
||||||
|
unordered_map<fd64_t,int> fd64_to_fd_mp;
|
||||||
|
unordered_map<fd64_t,fd_info_t> fd_info_mp;
|
||||||
|
int fd_exist(int fd);
|
||||||
|
//void remove_fd(int fd);
|
||||||
|
//fd64_t fd_to_fd64(int fd);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern fd_manager_t fd_manager;
|
||||||
|
#endif /* FD_MANAGER_H_ */
|
846
fec_manager.cpp
Normal file
@@ -0,0 +1,846 @@
|
|||||||
|
/*
|
||||||
|
* fec_manager.cpp
|
||||||
|
*
|
||||||
|
* Created on: Sep 27, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fec_manager.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "lib/rs.h"
|
||||||
|
#include "fd_manager.h"
|
||||||
|
|
||||||
|
int g_fec_data_num=20;
|
||||||
|
int g_fec_redundant_num=10;
|
||||||
|
int g_fec_mtu=1250;
|
||||||
|
int g_fec_queue_len=200;
|
||||||
|
int g_fec_timeout=8*1000; //8ms
|
||||||
|
int g_fec_mode=0;
|
||||||
|
|
||||||
|
int dynamic_update_fec=1;
|
||||||
|
|
||||||
|
const int encode_fast_send=1;
|
||||||
|
const int decode_fast_send=1;
|
||||||
|
|
||||||
|
int short_packet_optimize=1;
|
||||||
|
int header_overhead=40;
|
||||||
|
|
||||||
|
u32_t fec_buff_num=2000;// how many packet can fec_decode_manager hold. shouldnt be very large,or it will cost huge memory
|
||||||
|
|
||||||
|
blob_encode_t::blob_encode_t()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
int blob_encode_t::clear()
|
||||||
|
{
|
||||||
|
counter=0;
|
||||||
|
current_len=(int)sizeof(u32_t);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blob_encode_t::get_num()
|
||||||
|
{
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
int blob_encode_t::get_shard_len(int n)
|
||||||
|
{
|
||||||
|
return round_up_div(current_len,n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int blob_encode_t::get_shard_len(int n,int next_packet_len)
|
||||||
|
{
|
||||||
|
return round_up_div(current_len+(int)sizeof(u16_t)+next_packet_len,n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int blob_encode_t::input(char *s,int len)
|
||||||
|
{
|
||||||
|
assert(current_len+len+sizeof(u16_t) +100<sizeof(input_buf));
|
||||||
|
assert(len<=65535&&len>=0);
|
||||||
|
counter++;
|
||||||
|
assert(counter<=max_blob_packet_num);
|
||||||
|
write_u16(input_buf+current_len,len);
|
||||||
|
current_len+=sizeof(u16_t);
|
||||||
|
memcpy(input_buf+current_len,s,len);
|
||||||
|
current_len+=len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blob_encode_t::output(int n,char ** &s_arr,int & len)
|
||||||
|
{
|
||||||
|
len=round_up_div(current_len,n);
|
||||||
|
write_u32(input_buf,counter);
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
output_buf[i]=input_buf+len*i;
|
||||||
|
}
|
||||||
|
s_arr=output_buf;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
blob_decode_t::blob_decode_t()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
int blob_decode_t::clear()
|
||||||
|
{
|
||||||
|
current_len=0;
|
||||||
|
last_len=-1;
|
||||||
|
counter=0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int blob_decode_t::input(char *s,int len)
|
||||||
|
{
|
||||||
|
if(last_len!=-1)
|
||||||
|
{
|
||||||
|
assert(last_len==len);
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
assert(counter<=max_fec_packet_num);
|
||||||
|
last_len=len;
|
||||||
|
assert(current_len+len+100<(int)sizeof(input_buf));//avoid overflow
|
||||||
|
memcpy(input_buf+current_len,s,len);
|
||||||
|
current_len+=len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int blob_decode_t::output(int &n,char ** &s_arr,int *&len_arr)
|
||||||
|
{
|
||||||
|
|
||||||
|
int parser_pos=0;
|
||||||
|
|
||||||
|
if(parser_pos+(int)sizeof(u32_t)>current_len) {mylog(log_info,"failed 0\n");return -1;}
|
||||||
|
|
||||||
|
n=(int)read_u32(input_buf+parser_pos);
|
||||||
|
if(n>max_blob_packet_num) {mylog(log_info,"failed 1\n");return -1;}
|
||||||
|
s_arr=output_buf;
|
||||||
|
len_arr=output_len;
|
||||||
|
|
||||||
|
parser_pos+=sizeof(u32_t);
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
if(parser_pos+(int)sizeof(u16_t)>current_len) {mylog(log_info,"failed2 \n");return -1;}
|
||||||
|
len_arr[i]=(int)read_u16(input_buf+parser_pos);
|
||||||
|
parser_pos+=(int)sizeof(u16_t);
|
||||||
|
if(parser_pos+len_arr[i]>current_len) {mylog(log_info,"failed 3 %d %d %d\n",parser_pos,len_arr[i],current_len);return -1;}
|
||||||
|
s_arr[i]=input_buf+parser_pos;
|
||||||
|
parser_pos+=len_arr[i];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fec_encode_manager_t::~fec_encode_manager_t()
|
||||||
|
{
|
||||||
|
fd_manager.fd64_close(timer_fd64);
|
||||||
|
}
|
||||||
|
u64_t fec_encode_manager_t::get_timer_fd64()
|
||||||
|
{
|
||||||
|
return timer_fd64;
|
||||||
|
}
|
||||||
|
fec_encode_manager_t::fec_encode_manager_t()
|
||||||
|
{
|
||||||
|
//int timer_fd;
|
||||||
|
if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"timer_fd create error");
|
||||||
|
myexit(1);
|
||||||
|
}
|
||||||
|
timer_fd64=fd_manager.create(timer_fd);
|
||||||
|
|
||||||
|
reset_fec_parameter(g_fec_data_num,g_fec_redundant_num,g_fec_mtu,g_fec_queue_len,g_fec_timeout,g_fec_mode);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
int fec_encode_manager_t::reset_fec_parameter(int data_num,int redundant_num,int mtu,int queue_len,int timeout,int mode)
|
||||||
|
{
|
||||||
|
fec_data_num=data_num;
|
||||||
|
fec_redundant_num=redundant_num;
|
||||||
|
fec_mtu=mtu;
|
||||||
|
fec_queue_len=queue_len;
|
||||||
|
fec_timeout=timeout;
|
||||||
|
fec_mode=mode;
|
||||||
|
|
||||||
|
assert(data_num+redundant_num<max_fec_packet_num);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int fec_encode_manager_t::append(char *s,int len/*,int &is_first_packet*/)
|
||||||
|
{
|
||||||
|
if(counter==0)
|
||||||
|
{
|
||||||
|
itimerspec its;
|
||||||
|
memset(&its.it_interval,0,sizeof(its.it_interval));
|
||||||
|
first_packet_time=get_current_time_us();
|
||||||
|
my_time_t tmp_time=fec_timeout+first_packet_time;
|
||||||
|
its.it_value.tv_sec=tmp_time/1000000llu;
|
||||||
|
its.it_value.tv_nsec=(tmp_time%1000000llu)*1000llu;
|
||||||
|
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
|
||||||
|
}
|
||||||
|
if(fec_mode==0)//for type 0 use blob
|
||||||
|
{
|
||||||
|
assert(blob_encode.input(s,len)==0);
|
||||||
|
}
|
||||||
|
else if(fec_mode==1)//for tpe 1 use input_buf and counter
|
||||||
|
{
|
||||||
|
mylog(log_trace,"counter=%d\n",counter);
|
||||||
|
assert(len<=65535&&len>=0);
|
||||||
|
//assert(len<=fec_mtu);//relax this limitation
|
||||||
|
char * p=input_buf[counter]+sizeof(u32_t)+4*sizeof(char);//copy directly to final position,avoid unnecessary copy.
|
||||||
|
//remember to change this,if protocol is modified
|
||||||
|
|
||||||
|
write_u16(p,(u16_t)((u32_t)len)); //TODO omit this u16 for data packet while sending
|
||||||
|
p+=sizeof(u16_t);
|
||||||
|
memcpy(p,s,len);
|
||||||
|
input_len[counter]=len+sizeof(u16_t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int fec_encode_manager_t::input(char *s,int len/*,int &is_first_packet*/)
|
||||||
|
{
|
||||||
|
if(counter==0&&dynamic_update_fec)
|
||||||
|
{
|
||||||
|
fec_data_num=g_fec_data_num;
|
||||||
|
fec_redundant_num=g_fec_redundant_num;
|
||||||
|
fec_mtu=g_fec_mtu;
|
||||||
|
fec_queue_len=g_fec_queue_len;
|
||||||
|
fec_timeout=g_fec_timeout;
|
||||||
|
fec_mode=g_fec_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int about_to_fec=0;
|
||||||
|
int delayed_append=0;
|
||||||
|
//int counter_back=counter;
|
||||||
|
assert(fec_mode==0||fec_mode==1);
|
||||||
|
|
||||||
|
if(fec_mode==0&& s!=0 &&counter==0)
|
||||||
|
{
|
||||||
|
int out_len=blob_encode.get_shard_len(fec_data_num,len);
|
||||||
|
if(out_len>fec_mtu)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"message too long ori_len=%d out_len=%d fec_mtu=%d,ignored\n",len,out_len,fec_mtu);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fec_mode==1&&s!=0&&len>fec_mtu)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"mode==1,message len=%d,len>fec_mtu,fec_mtu=%d,packet may not be delivered\n",len,fec_mtu);
|
||||||
|
//return -1;
|
||||||
|
}
|
||||||
|
if(s==0&&counter==0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"unexpected s==0&&counter==0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(s==0) about_to_fec=1;//now
|
||||||
|
|
||||||
|
if(fec_mode==0&& blob_encode.get_shard_len(fec_data_num,len)>fec_mtu) {about_to_fec=1; delayed_append=1;}//fec then add packet
|
||||||
|
|
||||||
|
if(fec_mode==0) assert(counter<fec_queue_len);//counter will never equal fec_pending_num,if that happens fec should already been done.
|
||||||
|
if(fec_mode==1) assert(counter<fec_data_num);
|
||||||
|
|
||||||
|
|
||||||
|
if(s!=0&&!delayed_append)
|
||||||
|
{
|
||||||
|
append(s,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fec_mode==0&& counter==fec_queue_len) about_to_fec=1;
|
||||||
|
|
||||||
|
if(fec_mode==1&& counter==fec_data_num) about_to_fec=1;
|
||||||
|
|
||||||
|
|
||||||
|
if(about_to_fec)
|
||||||
|
{
|
||||||
|
char ** blob_output=0;
|
||||||
|
int fec_len=-1;
|
||||||
|
mylog(log_trace,"counter=%d\n",counter);
|
||||||
|
|
||||||
|
if(counter==0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"unexpected counter==0 here\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int actual_data_num;
|
||||||
|
int actual_redundant_num;
|
||||||
|
|
||||||
|
if(fec_mode==0)
|
||||||
|
{
|
||||||
|
|
||||||
|
actual_data_num=fec_data_num;
|
||||||
|
actual_redundant_num=fec_redundant_num;
|
||||||
|
|
||||||
|
if(short_packet_optimize)
|
||||||
|
{
|
||||||
|
u32_t best_len=(blob_encode.get_shard_len(fec_data_num,0)+header_overhead)*(fec_data_num+fec_redundant_num);
|
||||||
|
int best_data_num=fec_data_num;
|
||||||
|
for(int i=1;i<actual_data_num;i++)
|
||||||
|
{
|
||||||
|
u32_t shard_len=blob_encode.get_shard_len(i,0);
|
||||||
|
if(shard_len>(u32_t)fec_mtu) continue;
|
||||||
|
|
||||||
|
u32_t new_len=(shard_len+header_overhead)*(i+fec_redundant_num);
|
||||||
|
if(new_len<best_len)
|
||||||
|
{
|
||||||
|
best_len=new_len;
|
||||||
|
best_data_num=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual_data_num=best_data_num;
|
||||||
|
actual_redundant_num=fec_redundant_num;
|
||||||
|
mylog(log_trace,"actual_data_num=%d actual_redundant_num=%d\n",best_data_num,fec_redundant_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(blob_encode.output(actual_data_num,blob_output,fec_len)==0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actual_data_num=counter;
|
||||||
|
actual_redundant_num=fec_redundant_num;
|
||||||
|
|
||||||
|
for(int i=0;i<counter;i++)
|
||||||
|
{
|
||||||
|
assert(input_len[i]>=0);
|
||||||
|
if(input_len[i]>fec_len) fec_len=input_len[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
mylog(log_trace,"%d %d %d\n",actual_data_num,actual_redundant_num,fec_len);
|
||||||
|
|
||||||
|
char *tmp_output_buf[max_fec_packet_num+5]={0};
|
||||||
|
for(int i=0;i<actual_data_num+actual_redundant_num;i++)
|
||||||
|
{
|
||||||
|
int tmp_idx=0;
|
||||||
|
|
||||||
|
write_u32(input_buf[i] + tmp_idx, seq);
|
||||||
|
tmp_idx += sizeof(u32_t);
|
||||||
|
input_buf[i][tmp_idx++] = (unsigned char) fec_mode;
|
||||||
|
if (fec_mode == 1 && i < actual_data_num)
|
||||||
|
{
|
||||||
|
input_buf[i][tmp_idx++] = (unsigned char) 0;
|
||||||
|
input_buf[i][tmp_idx++] = (unsigned char) 0;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
input_buf[i][tmp_idx++] = (unsigned char) actual_data_num;
|
||||||
|
input_buf[i][tmp_idx++] = (unsigned char) actual_redundant_num;
|
||||||
|
}
|
||||||
|
input_buf[i][tmp_idx++] = (unsigned char) i;
|
||||||
|
|
||||||
|
tmp_output_buf[i]=input_buf[i]+tmp_idx; //////caution ,trick here.
|
||||||
|
|
||||||
|
if(fec_mode==0)
|
||||||
|
{
|
||||||
|
output_len[i]=tmp_idx+fec_len;
|
||||||
|
if(i<actual_data_num)
|
||||||
|
{
|
||||||
|
memcpy(input_buf[i]+tmp_idx,blob_output[i],fec_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(i<actual_data_num)
|
||||||
|
{
|
||||||
|
output_len[i]=tmp_idx+input_len[i];
|
||||||
|
memset(tmp_output_buf[i]+input_len[i],0,fec_len-input_len[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
output_len[i]=tmp_idx+fec_len;
|
||||||
|
|
||||||
|
}
|
||||||
|
output_buf[i]=input_buf[i];//output_buf points to same block of memory with different offset
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0)
|
||||||
|
{
|
||||||
|
printf("seq=%u,fec_len=%d,%d %d,before fec\n",seq,fec_len,actual_data_num,actual_redundant_num);
|
||||||
|
|
||||||
|
for(int i=0;i<actual_data_num;i++)
|
||||||
|
{
|
||||||
|
printf("{");
|
||||||
|
for(int j=0;j<8+fec_len;j++)
|
||||||
|
{
|
||||||
|
log_bare(log_warn,"0x%02x,",(u32_t)(unsigned char)input_buf[i][j]);
|
||||||
|
}
|
||||||
|
printf("},\n");
|
||||||
|
//log_bare(log_warn,"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//output_len=blob_len+sizeof(u32_t)+4*sizeof(char);/////remember to change this 4,if modified the protocol
|
||||||
|
rs_encode2(actual_data_num,actual_data_num+actual_redundant_num,tmp_output_buf,fec_len);
|
||||||
|
|
||||||
|
if(0)
|
||||||
|
{
|
||||||
|
printf("seq=%u,fec_len=%d,%d %d,after fec\n",seq,fec_len,actual_data_num,actual_redundant_num);
|
||||||
|
for(int i=0;i<actual_data_num+actual_redundant_num;i++)
|
||||||
|
{
|
||||||
|
printf("{");
|
||||||
|
for(int j=0;j<8+fec_len;j++)
|
||||||
|
{
|
||||||
|
log_bare(log_warn,"0x%02x,",(u32_t)(unsigned char)output_buf[i][j]);
|
||||||
|
}
|
||||||
|
printf("},\n");
|
||||||
|
//log_bare(log_warn,"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//mylog(log_trace,"!!! s= %d\n");
|
||||||
|
assert(ready_for_output==0);
|
||||||
|
ready_for_output=1;
|
||||||
|
first_packet_time_for_output=first_packet_time;
|
||||||
|
first_packet_time=0;
|
||||||
|
seq++;
|
||||||
|
counter=0;
|
||||||
|
output_n=actual_data_num+actual_redundant_num;
|
||||||
|
blob_encode.clear();
|
||||||
|
|
||||||
|
itimerspec its;
|
||||||
|
memset(&its,0,sizeof(its));
|
||||||
|
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
|
||||||
|
|
||||||
|
if(encode_fast_send&&fec_mode==1)
|
||||||
|
{
|
||||||
|
int packet_to_send[max_fec_packet_num+5]={0};
|
||||||
|
int packet_to_send_counter=0;
|
||||||
|
|
||||||
|
//assert(counter!=0);
|
||||||
|
if(s!=0)
|
||||||
|
packet_to_send[packet_to_send_counter++]=actual_data_num-1;
|
||||||
|
for(int i=actual_data_num;i<actual_data_num+actual_redundant_num;i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
packet_to_send[packet_to_send_counter++]=i;
|
||||||
|
}
|
||||||
|
output_n=packet_to_send_counter;//re write
|
||||||
|
for(int i=0;i<packet_to_send_counter;i++)
|
||||||
|
{
|
||||||
|
output_buf[i]=output_buf[packet_to_send[i]];
|
||||||
|
output_len[i]=output_len[packet_to_send[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(encode_fast_send&&s!=0&&fec_mode==1)
|
||||||
|
{
|
||||||
|
assert(counter>=1);
|
||||||
|
assert(counter<=255);
|
||||||
|
int input_buf_idx=counter-1;
|
||||||
|
assert(ready_for_output==0);
|
||||||
|
ready_for_output=1;
|
||||||
|
first_packet_time_for_output=0;
|
||||||
|
output_n=1;
|
||||||
|
|
||||||
|
|
||||||
|
int tmp_idx=0;
|
||||||
|
write_u32(input_buf[input_buf_idx]+tmp_idx,seq);
|
||||||
|
tmp_idx+=sizeof(u32_t);
|
||||||
|
|
||||||
|
input_buf[input_buf_idx][tmp_idx++]=(unsigned char)fec_mode;
|
||||||
|
input_buf[input_buf_idx][tmp_idx++]=(unsigned char)0;
|
||||||
|
input_buf[input_buf_idx][tmp_idx++]=(unsigned char)0;
|
||||||
|
input_buf[input_buf_idx][tmp_idx++]=(unsigned char)((u32_t)input_buf_idx);
|
||||||
|
|
||||||
|
output_len[0]=input_len[input_buf_idx]+tmp_idx;
|
||||||
|
output_buf[0]=input_buf[input_buf_idx];
|
||||||
|
|
||||||
|
if(0)
|
||||||
|
{
|
||||||
|
printf("seq=%u,buf_idx=%d\n",seq,input_buf_idx);
|
||||||
|
for(int j=0;j<output_len[0];j++)
|
||||||
|
{
|
||||||
|
log_bare(log_warn,"0x%02x,",(u32_t)(unsigned char)output_buf[0][j]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s!=0&&delayed_append)
|
||||||
|
{
|
||||||
|
assert(fec_mode!=1);
|
||||||
|
append(s,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fec_encode_manager_t::output(int &n,char ** &s_arr,int *&len)
|
||||||
|
{
|
||||||
|
if(!ready_for_output)
|
||||||
|
{
|
||||||
|
n=-1;
|
||||||
|
len=0;
|
||||||
|
s_arr=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
n=output_n;
|
||||||
|
len=output_len;
|
||||||
|
s_arr=output_buf;
|
||||||
|
ready_for_output=0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
int fec_decode_manager_t::re_init()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
return 0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int fec_decode_manager_t::input(char *s,int len)
|
||||||
|
{
|
||||||
|
assert(s!=0);
|
||||||
|
assert(len+100<buf_len);//guarenteed by upper level
|
||||||
|
|
||||||
|
int tmp_idx=0;
|
||||||
|
int tmp_header_len=sizeof(u32_t)+sizeof(char)*4;
|
||||||
|
if(len<tmp_header_len)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"len =%d\n",len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u32_t seq=read_u32(s+tmp_idx);
|
||||||
|
tmp_idx+=sizeof(u32_t);
|
||||||
|
int type=(unsigned char)s[tmp_idx++];
|
||||||
|
int data_num=(unsigned char)s[tmp_idx++];
|
||||||
|
int redundant_num=(unsigned char)s[tmp_idx++];
|
||||||
|
int inner_index=(unsigned char)s[tmp_idx++];
|
||||||
|
len=len-tmp_idx;
|
||||||
|
|
||||||
|
//mylog(log_trace,"input\n");
|
||||||
|
|
||||||
|
if(len<0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"len<0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type==1)
|
||||||
|
{
|
||||||
|
if(len<(int)sizeof(u16_t))
|
||||||
|
{
|
||||||
|
mylog(log_warn,"type==1&&len<2\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(data_num==0&&(int)( read_u16(s+tmp_idx)+sizeof(u16_t))!=len)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"inner_index<data_num&&read_u16(s+tmp_idx)+sizeof(u16_t)!=len %d %d\n",(int)( read_u16(s+tmp_idx)+sizeof(u16_t)),len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type==0&&data_num==0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"unexpected type==0&&data_num==0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(data_num+redundant_num>=max_fec_packet_num)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"data_num+redundant_num>=max_fec_packet_num\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(!anti_replay.is_vaild(seq))
|
||||||
|
{
|
||||||
|
mylog(log_trace,"!anti_replay.is_vaild(seq) ,seq =%u\n",seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mp[seq].group_mp.find(inner_index)!=mp[seq].group_mp.end() )
|
||||||
|
{
|
||||||
|
mylog(log_debug,"dup fec index\n");//duplicate can happen on a normal network, so its just log_debug
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(mp[seq].type==-1)
|
||||||
|
mp[seq].type=type;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mp[seq].type!=type)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"type mismatch\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data_num!=0)
|
||||||
|
{
|
||||||
|
//mp[seq].data_counter++;
|
||||||
|
|
||||||
|
if(mp[seq].data_num==-1)
|
||||||
|
{
|
||||||
|
mp[seq].data_num=data_num;
|
||||||
|
mp[seq].redundant_num=redundant_num;
|
||||||
|
mp[seq].len=len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mp[seq].data_num!=data_num||mp[seq].redundant_num!=redundant_num||mp[seq].len!=len)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"unexpected mp[seq].data_num!=data_num||mp[seq].redundant_num!=redundant_num||mp[seq].len!=len\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//mylog(log_info,"mp.size()=%d index=%d\n",mp.size(),index);
|
||||||
|
|
||||||
|
if(fec_data[index].used!=0)
|
||||||
|
{
|
||||||
|
u32_t tmp_seq=fec_data[index].seq;
|
||||||
|
anti_replay.set_invaild(tmp_seq);
|
||||||
|
|
||||||
|
if(mp.find(tmp_seq)!=mp.end())
|
||||||
|
{
|
||||||
|
mp.erase(tmp_seq);
|
||||||
|
}
|
||||||
|
if(tmp_seq==seq)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"unexpected tmp_seq==seq ,seq=%d\n",seq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fec_data[index].used=1;
|
||||||
|
fec_data[index].seq=seq;
|
||||||
|
fec_data[index].type=type;
|
||||||
|
fec_data[index].data_num=data_num;
|
||||||
|
fec_data[index].redundant_num=redundant_num;
|
||||||
|
fec_data[index].idx=inner_index;
|
||||||
|
fec_data[index].len=len;
|
||||||
|
assert(0<=index&&index<(int)fec_buff_num);
|
||||||
|
assert(len+100<buf_len);
|
||||||
|
memcpy(fec_data[index].buf,s+tmp_idx,len);
|
||||||
|
mp[seq].group_mp[inner_index]=index;
|
||||||
|
//index++ at end of function
|
||||||
|
|
||||||
|
map<int,int> &inner_mp=mp[seq].group_mp;
|
||||||
|
|
||||||
|
|
||||||
|
int about_to_fec=0;
|
||||||
|
if(type==0)
|
||||||
|
{
|
||||||
|
//assert((int)inner_mp.size()<=data_num);
|
||||||
|
if((int)inner_mp.size()>data_num)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"inner_mp.size()>data_num\n");
|
||||||
|
anti_replay.set_invaild(seq);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if((int)inner_mp.size()==data_num)
|
||||||
|
about_to_fec=1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mp[seq].data_num!=-1)
|
||||||
|
{
|
||||||
|
if((int)inner_mp.size()>mp[seq].data_num+1)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"inner_mp.size()>data_num+1\n");
|
||||||
|
anti_replay.set_invaild(seq);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if((int)inner_mp.size()>=mp[seq].data_num)
|
||||||
|
{
|
||||||
|
about_to_fec=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(about_to_fec)
|
||||||
|
{
|
||||||
|
int group_data_num=mp[seq].data_num;
|
||||||
|
int group_redundant_num=mp[seq].redundant_num;
|
||||||
|
|
||||||
|
//mylog(log_error,"fec here!\n");
|
||||||
|
if(type==0)
|
||||||
|
{
|
||||||
|
char *fec_tmp_arr[max_fec_packet_num+5]={0};
|
||||||
|
for(auto it=inner_mp.begin();it!=inner_mp.end();it++)
|
||||||
|
{
|
||||||
|
fec_tmp_arr[it->first]=fec_data[it->second].buf;
|
||||||
|
}
|
||||||
|
assert(rs_decode2(group_data_num,group_data_num+group_redundant_num,fec_tmp_arr,len)==0); //the input data has been modified in-place
|
||||||
|
//this line should always succeed
|
||||||
|
|
||||||
|
blob_decode.clear();
|
||||||
|
for(int i=0;i<group_data_num;i++)
|
||||||
|
{
|
||||||
|
blob_decode.input(fec_tmp_arr[i],len);
|
||||||
|
}
|
||||||
|
if(blob_decode.output(output_n,output_s_arr,output_len_arr)!=0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"blob_decode failed\n");
|
||||||
|
//ready_for_output=0;
|
||||||
|
anti_replay.set_invaild(seq);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
assert(ready_for_output==0);
|
||||||
|
ready_for_output=1;
|
||||||
|
anti_replay.set_invaild(seq);
|
||||||
|
}
|
||||||
|
else//type==1
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
int max_len=-1;
|
||||||
|
int fec_result_ok=1;
|
||||||
|
int data_check_ok=1;
|
||||||
|
int debug_num=inner_mp.size();
|
||||||
|
|
||||||
|
int missed_packet[max_fec_packet_num+5];
|
||||||
|
int missed_packet_counter=0;
|
||||||
|
|
||||||
|
//outupt_s_arr_buf[max_fec_packet_num+5]={0};
|
||||||
|
|
||||||
|
//memset(output_s_arr_buf,0,sizeof(output_s_arr_buf));//in efficient
|
||||||
|
|
||||||
|
for(int i=0;i<group_data_num+group_redundant_num;i++)
|
||||||
|
{
|
||||||
|
output_s_arr_buf[i]=0;
|
||||||
|
}
|
||||||
|
for(auto it=inner_mp.begin();it!=inner_mp.end();it++)
|
||||||
|
{
|
||||||
|
output_s_arr_buf[it->first]=fec_data[it->second].buf;
|
||||||
|
if(fec_data[it->second].len<(int)sizeof(u16_t))
|
||||||
|
{
|
||||||
|
mylog(log_warn,"fec_data[it->second].len<(int)sizeof(u16_t)");
|
||||||
|
data_check_ok=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fec_data[it->second].len > max_len)
|
||||||
|
max_len=fec_data[it->second].len;
|
||||||
|
}
|
||||||
|
if(max_len!=mp[seq].len)
|
||||||
|
{
|
||||||
|
data_check_ok=0;
|
||||||
|
mylog(log_warn,"max_len!=mp[seq].len");
|
||||||
|
}
|
||||||
|
if(data_check_ok==0)
|
||||||
|
{
|
||||||
|
//ready_for_output=0;
|
||||||
|
mylog(log_warn,"data_check_ok==0\n");
|
||||||
|
anti_replay.set_invaild(seq);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
for(auto it=inner_mp.begin();it!=inner_mp.end();it++)
|
||||||
|
{
|
||||||
|
int tmp_idx=it->second;
|
||||||
|
assert(max_len>=fec_data[tmp_idx].len);//guarenteed by data_check_ok
|
||||||
|
memset(fec_data[tmp_idx].buf+fec_data[tmp_idx].len,0,max_len-fec_data[tmp_idx].len);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0;i<group_data_num;i++)
|
||||||
|
{
|
||||||
|
if(output_s_arr_buf[i]==0 ||i==inner_index) //only missed packet +current packet
|
||||||
|
{
|
||||||
|
missed_packet[missed_packet_counter++]=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mylog(log_trace,"fec done,%d %d,missed_packet_counter=%d\n",group_data_num,group_redundant_num,missed_packet_counter);
|
||||||
|
|
||||||
|
assert(rs_decode2(group_data_num,group_data_num+group_redundant_num,output_s_arr_buf,max_len)==0);//this should always succeed
|
||||||
|
|
||||||
|
for(int i=0;i<group_data_num;i++)
|
||||||
|
{
|
||||||
|
output_len_arr_buf[i]=read_u16(output_s_arr_buf[i]);
|
||||||
|
output_s_arr_buf[i]+=sizeof(u16_t);
|
||||||
|
if(output_len_arr_buf[i]>max_data_len)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"invaild len %d,seq= %u,data_num= %d r_num= %d,i= %d\n",output_len_arr_buf[i],seq,group_data_num,group_redundant_num,i);
|
||||||
|
fec_result_ok=0;
|
||||||
|
for(int i=0;i<missed_packet_counter;i++)
|
||||||
|
{
|
||||||
|
log_bare(log_warn,"%d ",missed_packet[i]);
|
||||||
|
}
|
||||||
|
log_bare(log_warn,"\n");
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fec_result_ok)
|
||||||
|
{
|
||||||
|
|
||||||
|
output_n=group_data_num;
|
||||||
|
|
||||||
|
if(decode_fast_send)
|
||||||
|
{
|
||||||
|
output_n=missed_packet_counter;
|
||||||
|
for(int i=0;i<missed_packet_counter;i++)
|
||||||
|
{
|
||||||
|
output_s_arr_buf[i]=output_s_arr_buf[missed_packet[i]];
|
||||||
|
output_len_arr_buf[i]=output_len_arr_buf[missed_packet[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
output_s_arr=output_s_arr_buf;
|
||||||
|
output_len_arr=output_len_arr_buf;
|
||||||
|
assert(ready_for_output==0);
|
||||||
|
ready_for_output=1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fec_not_ok:
|
||||||
|
ready_for_output=0;
|
||||||
|
}
|
||||||
|
anti_replay.set_invaild(seq);
|
||||||
|
}// end of type==1
|
||||||
|
}
|
||||||
|
else //not about_to_fec
|
||||||
|
{
|
||||||
|
|
||||||
|
if(decode_fast_send)
|
||||||
|
{
|
||||||
|
if(type==1&&data_num==0)
|
||||||
|
{
|
||||||
|
assert(ready_for_output==0);
|
||||||
|
output_n=1;
|
||||||
|
int check_len=read_u16(fec_data[index].buf);
|
||||||
|
output_s_arr_buf[0]=fec_data[index].buf+sizeof(u16_t);
|
||||||
|
output_len_arr_buf[0]=fec_data[index].len-sizeof(u16_t);
|
||||||
|
|
||||||
|
if(output_len_arr_buf[0]!=check_len)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"len mismatch %d %d\n",output_len_arr_buf[0],check_len);
|
||||||
|
}
|
||||||
|
output_s_arr=output_s_arr_buf;
|
||||||
|
output_len_arr=output_len_arr_buf;
|
||||||
|
|
||||||
|
ready_for_output=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
index++;
|
||||||
|
if(index==int(fec_buff_num)) index=0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int fec_decode_manager_t::output(int &n,char ** &s_arr,int* &len_arr)
|
||||||
|
{
|
||||||
|
if(!ready_for_output)
|
||||||
|
{
|
||||||
|
n=-1;
|
||||||
|
s_arr=0;
|
||||||
|
len_arr=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ready_for_output=0;
|
||||||
|
n=output_n;
|
||||||
|
s_arr=output_s_arr;
|
||||||
|
len_arr=output_len_arr;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
254
fec_manager.h
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* fec_manager.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 27, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FEC_MANAGER_H_
|
||||||
|
#define FEC_MANAGER_H_
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "lib/rs.h"
|
||||||
|
|
||||||
|
const int max_blob_packet_num=30000;//how many packet can be contain in a blob_t ,can be set very large
|
||||||
|
const u32_t anti_replay_buff_size=30000;//can be set very large
|
||||||
|
|
||||||
|
const int max_fec_packet_num=255;// this is the limitation of the rs lib
|
||||||
|
extern u32_t fec_buff_num;
|
||||||
|
|
||||||
|
|
||||||
|
/*begin for first time init or dynamic update*/
|
||||||
|
extern int g_fec_data_num;
|
||||||
|
extern int g_fec_redundant_num;
|
||||||
|
extern int g_fec_mtu;
|
||||||
|
extern int g_fec_queue_len;
|
||||||
|
extern int g_fec_timeout; //8ms
|
||||||
|
extern int g_fec_mode;
|
||||||
|
extern int dynamic_update_fec;
|
||||||
|
/*end for first time init or dynamic update*/
|
||||||
|
|
||||||
|
|
||||||
|
struct anti_replay_t
|
||||||
|
{
|
||||||
|
|
||||||
|
u64_t replay_buffer[anti_replay_buff_size];
|
||||||
|
unordered_set<u32_t> st;
|
||||||
|
int index;
|
||||||
|
anti_replay_t()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
int clear()
|
||||||
|
{
|
||||||
|
memset(replay_buffer,-1,sizeof(replay_buffer));
|
||||||
|
st.clear();
|
||||||
|
st.rehash(anti_replay_buff_size*3);
|
||||||
|
index=0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void set_invaild(u32_t seq)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(st.find(seq)!=st.end() )
|
||||||
|
{
|
||||||
|
mylog(log_trace,"seq %u exist\n",seq);
|
||||||
|
return;
|
||||||
|
//return 0;
|
||||||
|
}
|
||||||
|
if(replay_buffer[index]!=u64_t(i64_t(-1)))
|
||||||
|
{
|
||||||
|
assert(st.find(replay_buffer[index])!=st.end());
|
||||||
|
st.erase(replay_buffer[index]);
|
||||||
|
}
|
||||||
|
replay_buffer[index]=seq;
|
||||||
|
st.insert(seq);
|
||||||
|
index++;
|
||||||
|
if(index==int(anti_replay_buff_size)) index=0;
|
||||||
|
//return 1; //for complier check
|
||||||
|
}
|
||||||
|
int is_vaild(u32_t seq)
|
||||||
|
{
|
||||||
|
return st.find(seq)==st.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct blob_encode_t
|
||||||
|
{
|
||||||
|
char input_buf[(max_fec_packet_num+5)*buf_len];
|
||||||
|
int current_len;
|
||||||
|
int counter;
|
||||||
|
|
||||||
|
char *output_buf[max_fec_packet_num+100];
|
||||||
|
|
||||||
|
blob_encode_t();
|
||||||
|
|
||||||
|
int clear();
|
||||||
|
|
||||||
|
int get_num();
|
||||||
|
int get_shard_len(int n);
|
||||||
|
int get_shard_len(int n,int next_packet_len);
|
||||||
|
|
||||||
|
int input(char *s,int len); //len=use len=0 for second and following packet
|
||||||
|
int output(int n,char ** &s_arr,int & len);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct blob_decode_t
|
||||||
|
{
|
||||||
|
char input_buf[(max_fec_packet_num+5)*buf_len];
|
||||||
|
int current_len;
|
||||||
|
int last_len;
|
||||||
|
int counter;
|
||||||
|
|
||||||
|
char *output_buf[max_blob_packet_num+100];
|
||||||
|
int output_len[max_blob_packet_num+100];
|
||||||
|
|
||||||
|
blob_decode_t();
|
||||||
|
int clear();
|
||||||
|
int input(char *input,int len);
|
||||||
|
int output(int &n,char ** &output,int *&len_arr);
|
||||||
|
};
|
||||||
|
|
||||||
|
class fec_encode_manager_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
u32_t seq;
|
||||||
|
|
||||||
|
int fec_mode;
|
||||||
|
int fec_data_num,fec_redundant_num;
|
||||||
|
int fec_mtu;
|
||||||
|
int fec_queue_len;
|
||||||
|
int fec_timeout;
|
||||||
|
|
||||||
|
my_time_t first_packet_time;
|
||||||
|
my_time_t first_packet_time_for_output;
|
||||||
|
|
||||||
|
|
||||||
|
blob_encode_t blob_encode;
|
||||||
|
char input_buf[max_fec_packet_num+5][buf_len];
|
||||||
|
int input_len[max_fec_packet_num+100];
|
||||||
|
|
||||||
|
char *output_buf[max_fec_packet_num+100];
|
||||||
|
int output_len[max_fec_packet_num+100];
|
||||||
|
|
||||||
|
int counter;
|
||||||
|
int timer_fd;
|
||||||
|
u64_t timer_fd64;
|
||||||
|
|
||||||
|
int ready_for_output;
|
||||||
|
u32_t output_n;
|
||||||
|
|
||||||
|
|
||||||
|
int append(char *s,int len);
|
||||||
|
|
||||||
|
public:
|
||||||
|
fec_encode_manager_t();
|
||||||
|
~fec_encode_manager_t();
|
||||||
|
|
||||||
|
int clear()
|
||||||
|
{
|
||||||
|
counter=0;
|
||||||
|
blob_encode.clear();
|
||||||
|
ready_for_output=0;
|
||||||
|
|
||||||
|
itimerspec zero_its;
|
||||||
|
memset(&zero_its, 0, sizeof(zero_its));
|
||||||
|
|
||||||
|
timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
|
||||||
|
|
||||||
|
seq=(u32_t)get_true_random_number(); //TODO temp solution for a bug.
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_time_t get_first_packet_time()
|
||||||
|
{
|
||||||
|
return first_packet_time_for_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_pending_time()
|
||||||
|
{
|
||||||
|
return fec_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_type()
|
||||||
|
{
|
||||||
|
return fec_mode;
|
||||||
|
}
|
||||||
|
u64_t get_timer_fd64();
|
||||||
|
int reset_fec_parameter(int data_num,int redundant_num,int mtu,int pending_num,int pending_time,int type);
|
||||||
|
int input(char *s,int len/*,int &is_first_packet*/);
|
||||||
|
int output(int &n,char ** &s_arr,int *&len);
|
||||||
|
};
|
||||||
|
struct fec_data_t
|
||||||
|
{
|
||||||
|
int used;
|
||||||
|
u32_t seq;
|
||||||
|
int type;
|
||||||
|
int data_num;
|
||||||
|
int redundant_num;
|
||||||
|
int idx;
|
||||||
|
char buf[buf_len];
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
struct fec_group_t
|
||||||
|
{
|
||||||
|
int type=-1;
|
||||||
|
int data_num=-1;
|
||||||
|
int redundant_num=-1;
|
||||||
|
int len=-1;
|
||||||
|
//int data_counter=0;
|
||||||
|
map<int,int> group_mp;
|
||||||
|
};
|
||||||
|
class fec_decode_manager_t
|
||||||
|
{
|
||||||
|
anti_replay_t anti_replay;
|
||||||
|
fec_data_t *fec_data;
|
||||||
|
unordered_map<u32_t, fec_group_t> mp;
|
||||||
|
blob_decode_t blob_decode;
|
||||||
|
|
||||||
|
int index;
|
||||||
|
|
||||||
|
int output_n;
|
||||||
|
char ** output_s_arr;
|
||||||
|
int * output_len_arr;
|
||||||
|
int ready_for_output;
|
||||||
|
|
||||||
|
char *output_s_arr_buf[max_fec_packet_num+100];//only for type=1,for type=0 the buf inside blot_t is used
|
||||||
|
int output_len_arr_buf[max_fec_packet_num+100];//same
|
||||||
|
|
||||||
|
public:
|
||||||
|
fec_decode_manager_t()
|
||||||
|
{
|
||||||
|
fec_data=new fec_data_t[fec_buff_num+5];
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
fec_decode_manager_t(const fec_decode_manager_t &b)
|
||||||
|
{
|
||||||
|
assert(0==1);//not allowed to copy
|
||||||
|
}
|
||||||
|
~fec_decode_manager_t()
|
||||||
|
{
|
||||||
|
delete fec_data;
|
||||||
|
}
|
||||||
|
int clear()
|
||||||
|
{
|
||||||
|
anti_replay.clear();
|
||||||
|
mp.clear();
|
||||||
|
mp.rehash(fec_buff_num*3);
|
||||||
|
|
||||||
|
for(int i=0;i<(int)fec_buff_num;i++)
|
||||||
|
fec_data[i].used=0;
|
||||||
|
ready_for_output=0;
|
||||||
|
index=0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//int re_init();
|
||||||
|
int input(char *s,int len);
|
||||||
|
int output(int &n,char ** &s_arr,int* &len_arr);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FEC_MANAGER_H_ */
|
BIN
images/Capture10.PNG
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
images/Capture7.PNG
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
images/Capture8.PNG
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
images/Capture9.PNG
Normal file
After Width: | Height: | Size: 65 KiB |
1
images/cn/11
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
BIN
images/cn/ping_compare_cn.PNG
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
images/cn/scp_compare.PNG
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
images/cn/scp_compare2.PNG
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
images/cn/speeder_kcptun.PNG
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
images/cn/speeder_vpn_s.PNG
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
images/cn/speedercn.PNG
Normal file
After Width: | Height: | Size: 34 KiB |
1
images/en/11
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
BIN
images/en/fec.PNG
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
images/en/ping_compare2.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
images/en/ping_compare3.PNG
Normal file
After Width: | Height: | Size: 669 KiB |
BIN
images/en/ping_compare_mode1.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
images/en/rs.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
images/en/scp_compare.PNG
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
images/en/scp_compare2.PNG
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
images/en/udpspeeder+openvpn.PNG
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
images/en/udpspeeder+openvpn2.PNG
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
images/en/udpspeeder+openvpn3.PNG
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
images/en/udpspeeder.PNG
Normal file
After Width: | Height: | Size: 34 KiB |
917
lib/fec.c
Normal file
@@ -0,0 +1,917 @@
|
|||||||
|
/*
|
||||||
|
* fec.c -- forward error correction based on Vandermonde matrices
|
||||||
|
* 980624
|
||||||
|
* (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
|
||||||
|
*
|
||||||
|
* Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
|
||||||
|
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
|
||||||
|
* Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||||
|
* OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following parameter defines how many bits are used for
|
||||||
|
* field elements. The code supports any value from 2 to 16
|
||||||
|
* but fastest operation is achieved with 8 bit elements
|
||||||
|
* This is the only parameter you may want to change.
|
||||||
|
*/
|
||||||
|
#ifndef GF_BITS
|
||||||
|
#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef unsigned long u_long;
|
||||||
|
/*
|
||||||
|
* compatibility stuff
|
||||||
|
*/
|
||||||
|
#ifdef MSDOS /* but also for others, e.g. sun... */
|
||||||
|
#define NEED_BCOPY
|
||||||
|
#define bcmp(a,b,n) memcmp(a,b,n)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef NEED_BCOPY
|
||||||
|
#define bcopy(s, d, siz) memcpy((d), (s), (siz))
|
||||||
|
#define bzero(d, siz) memset((d), '\0', (siz))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* stuff used for testing purposes only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
#define DEB(x)
|
||||||
|
#define DDB(x) x
|
||||||
|
#define DEBUG 0 /* minimal debugging */
|
||||||
|
#ifdef MSDOS
|
||||||
|
#include <time.h>
|
||||||
|
struct timeval {
|
||||||
|
unsigned long ticks;
|
||||||
|
};
|
||||||
|
#define gettimeofday(x, dummy) { (x)->ticks = clock() ; }
|
||||||
|
#define DIFF_T(a,b) (1+ 1000000*(a.ticks - b.ticks) / CLOCKS_PER_SEC )
|
||||||
|
typedef unsigned long u_long ;
|
||||||
|
typedef unsigned short u_short ;
|
||||||
|
#else /* typically, unix systems */
|
||||||
|
#include <sys/time.h>
|
||||||
|
#define DIFF_T(a,b) \
|
||||||
|
(1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TICK(t) \
|
||||||
|
{struct timeval x ; \
|
||||||
|
gettimeofday(&x, NULL) ; \
|
||||||
|
t = x.tv_usec + 1000000* (x.tv_sec & 0xff ) ; \
|
||||||
|
}
|
||||||
|
#define TOCK(t) \
|
||||||
|
{ u_long t1 ; TICK(t1) ; \
|
||||||
|
if (t1 < t) t = 256000000 + t1 - t ; \
|
||||||
|
else t = t1 - t ; \
|
||||||
|
if (t == 0) t = 1 ;}
|
||||||
|
|
||||||
|
u_long ticks[10]; /* vars for timekeeping */
|
||||||
|
#else
|
||||||
|
#define DEB(x)
|
||||||
|
#define DDB(x)
|
||||||
|
#define TICK(x)
|
||||||
|
#define TOCK(x)
|
||||||
|
#endif /* TEST */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You should not need to change anything beyond this point.
|
||||||
|
* The first part of the file implements linear algebra in GF.
|
||||||
|
*
|
||||||
|
* gf is the type used to store an element of the Galois Field.
|
||||||
|
* Must constain at least GF_BITS bits.
|
||||||
|
*
|
||||||
|
* Note: unsigned char will work up to GF(256) but int seems to run
|
||||||
|
* faster on the Pentium. We use int whenever have to deal with an
|
||||||
|
* index, since they are generally faster.
|
||||||
|
*/
|
||||||
|
#if (GF_BITS < 2 && GF_BITS >16)
|
||||||
|
#error "GF_BITS must be 2 .. 16"
|
||||||
|
#endif
|
||||||
|
#if (GF_BITS <= 8)
|
||||||
|
typedef unsigned char gf;
|
||||||
|
#else
|
||||||
|
typedef unsigned short gf;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Primitive polynomials - see Lin & Costello, Appendix A,
|
||||||
|
* and Lee & Messerschmitt, p. 453.
|
||||||
|
*/
|
||||||
|
static const char *allPp[] = { /* GF_BITS polynomial */
|
||||||
|
NULL, /* 0 no code */
|
||||||
|
NULL, /* 1 no code */
|
||||||
|
"111", /* 2 1+x+x^2 */
|
||||||
|
"1101", /* 3 1+x+x^3 */
|
||||||
|
"11001", /* 4 1+x+x^4 */
|
||||||
|
"101001", /* 5 1+x^2+x^5 */
|
||||||
|
"1100001", /* 6 1+x+x^6 */
|
||||||
|
"10010001", /* 7 1 + x^3 + x^7 */
|
||||||
|
"101110001", /* 8 1+x^2+x^3+x^4+x^8 */
|
||||||
|
"1000100001", /* 9 1+x^4+x^9 */
|
||||||
|
"10010000001", /* 10 1+x^3+x^10 */
|
||||||
|
"101000000001", /* 11 1+x^2+x^11 */
|
||||||
|
"1100101000001", /* 12 1+x+x^4+x^6+x^12 */
|
||||||
|
"11011000000001", /* 13 1+x+x^3+x^4+x^13 */
|
||||||
|
"110000100010001", /* 14 1+x+x^6+x^10+x^14 */
|
||||||
|
"1100000000000001", /* 15 1+x+x^15 */
|
||||||
|
"11010000000010001" /* 16 1+x+x^3+x^12+x^16 */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To speed up computations, we have tables for logarithm, exponent
|
||||||
|
* and inverse of a number. If GF_BITS <= 8, we use a table for
|
||||||
|
* multiplication as well (it takes 64K, no big deal even on a PDA,
|
||||||
|
* especially because it can be pre-initialized an put into a ROM!),
|
||||||
|
* otherwhise we use a table of logarithms.
|
||||||
|
* In any case the macro gf_mul(x,y) takes care of multiplications.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gf gf_exp[2*GF_SIZE]; /* index->poly form conversion table */
|
||||||
|
static int gf_log[GF_SIZE + 1]; /* Poly->index form conversion table */
|
||||||
|
static gf inverse[GF_SIZE+1]; /* inverse of field elem. */
|
||||||
|
/* inv[\alpha**i]=\alpha**(GF_SIZE-i-1) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1,
|
||||||
|
* without a slow divide.
|
||||||
|
*/
|
||||||
|
static inline gf
|
||||||
|
modnn(int x)
|
||||||
|
{
|
||||||
|
while (x >= GF_SIZE) {
|
||||||
|
x -= GF_SIZE;
|
||||||
|
x = (x >> GF_BITS) + (x & GF_SIZE);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much
|
||||||
|
* faster to use a multiplication table.
|
||||||
|
*
|
||||||
|
* USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying
|
||||||
|
* many numbers by the same constant. In this case the first
|
||||||
|
* call sets the constant, and others perform the multiplications.
|
||||||
|
* A value related to the multiplication is held in a local variable
|
||||||
|
* declared with USE_GF_MULC . See usage in addmul1().
|
||||||
|
*/
|
||||||
|
#if (GF_BITS <= 8)
|
||||||
|
static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1];
|
||||||
|
|
||||||
|
#define gf_mul(x,y) gf_mul_table[x][y]
|
||||||
|
|
||||||
|
#define USE_GF_MULC register gf * __gf_mulc_
|
||||||
|
#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c]
|
||||||
|
#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x]
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_mul_table()
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
for (i=0; i< GF_SIZE+1; i++)
|
||||||
|
for (j=0; j< GF_SIZE+1; j++)
|
||||||
|
gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ;
|
||||||
|
|
||||||
|
for (j=0; j< GF_SIZE+1; j++)
|
||||||
|
gf_mul_table[0][j] = gf_mul_table[j][0] = 0;
|
||||||
|
}
|
||||||
|
#else /* GF_BITS > 8 */
|
||||||
|
static inline gf
|
||||||
|
gf_mul(x,y)
|
||||||
|
{
|
||||||
|
if ( (x) == 0 || (y)==0 ) return 0;
|
||||||
|
|
||||||
|
return gf_exp[gf_log[x] + gf_log[y] ] ;
|
||||||
|
}
|
||||||
|
#define init_mul_table()
|
||||||
|
|
||||||
|
#define USE_GF_MULC register gf * __gf_mulc_
|
||||||
|
#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ]
|
||||||
|
#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
|
||||||
|
* Lookup tables:
|
||||||
|
* index->polynomial form gf_exp[] contains j= \alpha^i;
|
||||||
|
* polynomial form -> index form gf_log[ j = \alpha^i ] = i
|
||||||
|
* \alpha=x is the primitive element of GF(2^m)
|
||||||
|
*
|
||||||
|
* For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple
|
||||||
|
* multiplication of two numbers can be resolved without calling modnn
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* i use malloc so many times, it is easier to put checks all in
|
||||||
|
* one place.
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
my_malloc(int sz, const char *err_string)
|
||||||
|
{
|
||||||
|
void *p = malloc( sz );
|
||||||
|
if (p == NULL) {
|
||||||
|
fprintf(stderr, "-- malloc failure allocating %s\n", err_string);
|
||||||
|
exit(1) ;
|
||||||
|
}
|
||||||
|
return p ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NEW_GF_MATRIX(rows, cols) \
|
||||||
|
(gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize the data structures used for computations in GF.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
generate_gf(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
gf mask;
|
||||||
|
const char *Pp = allPp[GF_BITS] ;
|
||||||
|
|
||||||
|
mask = 1; /* x ** 0 = 1 */
|
||||||
|
gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */
|
||||||
|
/*
|
||||||
|
* first, generate the (polynomial representation of) powers of \alpha,
|
||||||
|
* which are stored in gf_exp[i] = \alpha ** i .
|
||||||
|
* At the same time build gf_log[gf_exp[i]] = i .
|
||||||
|
* The first GF_BITS powers are simply bits shifted to the left.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < GF_BITS; i++, mask <<= 1 ) {
|
||||||
|
gf_exp[i] = mask;
|
||||||
|
gf_log[gf_exp[i]] = i;
|
||||||
|
/*
|
||||||
|
* If Pp[i] == 1 then \alpha ** i occurs in poly-repr
|
||||||
|
* gf_exp[GF_BITS] = \alpha ** GF_BITS
|
||||||
|
*/
|
||||||
|
if ( Pp[i] == '1' )
|
||||||
|
gf_exp[GF_BITS] ^= mask;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als
|
||||||
|
* compute its inverse.
|
||||||
|
*/
|
||||||
|
gf_log[gf_exp[GF_BITS]] = GF_BITS;
|
||||||
|
/*
|
||||||
|
* Poly-repr of \alpha ** (i+1) is given by poly-repr of
|
||||||
|
* \alpha ** i shifted left one-bit and accounting for any
|
||||||
|
* \alpha ** GF_BITS term that may occur when poly-repr of
|
||||||
|
* \alpha ** i is shifted.
|
||||||
|
*/
|
||||||
|
mask = 1 << (GF_BITS - 1 ) ;
|
||||||
|
for (i = GF_BITS + 1; i < GF_SIZE; i++) {
|
||||||
|
if (gf_exp[i - 1] >= mask)
|
||||||
|
gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1);
|
||||||
|
else
|
||||||
|
gf_exp[i] = gf_exp[i - 1] << 1;
|
||||||
|
gf_log[gf_exp[i]] = i;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* log(0) is not defined, so use a special value
|
||||||
|
*/
|
||||||
|
gf_log[0] = GF_SIZE ;
|
||||||
|
/* set the extended gf_exp values for fast multiply */
|
||||||
|
for (i = 0 ; i < GF_SIZE ; i++)
|
||||||
|
gf_exp[i + GF_SIZE] = gf_exp[i] ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* again special cases. 0 has no inverse. This used to
|
||||||
|
* be initialized to GF_SIZE, but it should make no difference
|
||||||
|
* since noone is supposed to read from here.
|
||||||
|
*/
|
||||||
|
inverse[0] = 0 ;
|
||||||
|
inverse[1] = 1;
|
||||||
|
for (i=2; i<=GF_SIZE; i++)
|
||||||
|
inverse[i] = gf_exp[GF_SIZE-gf_log[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Various linear algebra operations that i use often.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* addmul() computes dst[] = dst[] + c * src[]
|
||||||
|
* This is used often, so better optimize it! Currently the loop is
|
||||||
|
* unrolled 16 times, a good value for 486 and pentium-class machines.
|
||||||
|
* The case c=0 is also optimized, whereas c=1 is not. These
|
||||||
|
* calls are unfrequent in my typical apps so I did not bother.
|
||||||
|
*
|
||||||
|
* Note that gcc on
|
||||||
|
*/
|
||||||
|
#define addmul(dst, src, c, sz) \
|
||||||
|
if (c != 0) addmul1(dst, src, c, sz)
|
||||||
|
|
||||||
|
#define UNROLL 16 /* 1, 4, 8, 16 */
|
||||||
|
static void
|
||||||
|
addmul1(gf *dst1, gf *src1, gf c, int sz)
|
||||||
|
{
|
||||||
|
USE_GF_MULC ;
|
||||||
|
register gf *dst = dst1, *src = src1 ;
|
||||||
|
gf *lim = &dst[sz - UNROLL + 1] ;
|
||||||
|
|
||||||
|
GF_MULC0(c) ;
|
||||||
|
|
||||||
|
#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */
|
||||||
|
for (; dst < lim ; dst += UNROLL, src += UNROLL ) {
|
||||||
|
GF_ADDMULC( dst[0] , src[0] );
|
||||||
|
GF_ADDMULC( dst[1] , src[1] );
|
||||||
|
GF_ADDMULC( dst[2] , src[2] );
|
||||||
|
GF_ADDMULC( dst[3] , src[3] );
|
||||||
|
#if (UNROLL > 4)
|
||||||
|
GF_ADDMULC( dst[4] , src[4] );
|
||||||
|
GF_ADDMULC( dst[5] , src[5] );
|
||||||
|
GF_ADDMULC( dst[6] , src[6] );
|
||||||
|
GF_ADDMULC( dst[7] , src[7] );
|
||||||
|
#endif
|
||||||
|
#if (UNROLL > 8)
|
||||||
|
GF_ADDMULC( dst[8] , src[8] );
|
||||||
|
GF_ADDMULC( dst[9] , src[9] );
|
||||||
|
GF_ADDMULC( dst[10] , src[10] );
|
||||||
|
GF_ADDMULC( dst[11] , src[11] );
|
||||||
|
GF_ADDMULC( dst[12] , src[12] );
|
||||||
|
GF_ADDMULC( dst[13] , src[13] );
|
||||||
|
GF_ADDMULC( dst[14] , src[14] );
|
||||||
|
GF_ADDMULC( dst[15] , src[15] );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
lim += UNROLL - 1 ;
|
||||||
|
for (; dst < lim; dst++, src++ ) /* final components */
|
||||||
|
GF_ADDMULC( *dst , *src );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* computes C = AB where A is n*k, B is k*m, C is n*m
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
matmul(gf *a, gf *b, gf *c, int n, int k, int m)
|
||||||
|
{
|
||||||
|
int row, col, i ;
|
||||||
|
|
||||||
|
for (row = 0; row < n ; row++) {
|
||||||
|
for (col = 0; col < m ; col++) {
|
||||||
|
gf *pa = &a[ row * k ];
|
||||||
|
gf *pb = &b[ col ];
|
||||||
|
gf acc = 0 ;
|
||||||
|
for (i = 0; i < k ; i++, pa++, pb += m )
|
||||||
|
acc ^= gf_mul( *pa, *pb ) ;
|
||||||
|
c[ row * m + col ] = acc ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
/*
|
||||||
|
* returns 1 if the square matrix is identiy
|
||||||
|
* (only for test)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_identity(gf *m, int k)
|
||||||
|
{
|
||||||
|
int row, col ;
|
||||||
|
for (row=0; row<k; row++)
|
||||||
|
for (col=0; col<k; col++)
|
||||||
|
if ( (row==col && *m != 1) ||
|
||||||
|
(row!=col && *m != 0) )
|
||||||
|
return 0 ;
|
||||||
|
else
|
||||||
|
m++ ;
|
||||||
|
return 1 ;
|
||||||
|
}
|
||||||
|
#endif /* debug */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* invert_mat() takes a matrix and produces its inverse
|
||||||
|
* k is the size of the matrix.
|
||||||
|
* (Gauss-Jordan, adapted from Numerical Recipes in C)
|
||||||
|
* Return non-zero if singular.
|
||||||
|
*/
|
||||||
|
DEB( int pivloops=0; int pivswaps=0 ; /* diagnostic */)
|
||||||
|
static int
|
||||||
|
invert_mat(gf *src, int k)
|
||||||
|
{
|
||||||
|
gf c, *p ;
|
||||||
|
int irow, icol, row, col, i, ix ;
|
||||||
|
|
||||||
|
int error = 1 ;
|
||||||
|
int *indxc = (int*)my_malloc(k*sizeof(int), "indxc");
|
||||||
|
int *indxr = (int*)my_malloc(k*sizeof(int), "indxr");
|
||||||
|
int *ipiv = (int*)my_malloc(k*sizeof(int), "ipiv");
|
||||||
|
gf *id_row = NEW_GF_MATRIX(1, k);
|
||||||
|
gf *temp_row = NEW_GF_MATRIX(1, k);
|
||||||
|
|
||||||
|
bzero(id_row, k*sizeof(gf));
|
||||||
|
DEB( pivloops=0; pivswaps=0 ; /* diagnostic */ )
|
||||||
|
/*
|
||||||
|
* ipiv marks elements already used as pivots.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < k ; i++)
|
||||||
|
ipiv[i] = 0 ;
|
||||||
|
|
||||||
|
for (col = 0; col < k ; col++) {
|
||||||
|
gf *pivot_row ;
|
||||||
|
/*
|
||||||
|
* Zeroing column 'col', look for a non-zero element.
|
||||||
|
* First try on the diagonal, if it fails, look elsewhere.
|
||||||
|
*/
|
||||||
|
irow = icol = -1 ;
|
||||||
|
if (ipiv[col] != 1 && src[col*k + col] != 0) {
|
||||||
|
irow = col ;
|
||||||
|
icol = col ;
|
||||||
|
goto found_piv ;
|
||||||
|
}
|
||||||
|
for (row = 0 ; row < k ; row++) {
|
||||||
|
if (ipiv[row] != 1) {
|
||||||
|
for (ix = 0 ; ix < k ; ix++) {
|
||||||
|
DEB( pivloops++ ; )
|
||||||
|
if (ipiv[ix] == 0) {
|
||||||
|
if (src[row*k + ix] != 0) {
|
||||||
|
irow = row ;
|
||||||
|
icol = ix ;
|
||||||
|
goto found_piv ;
|
||||||
|
}
|
||||||
|
} else if (ipiv[ix] > 1) {
|
||||||
|
fprintf(stderr, "singular matrix\n");
|
||||||
|
goto fail ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (icol == -1) {
|
||||||
|
fprintf(stderr, "XXX pivot not found!\n");
|
||||||
|
goto fail ;
|
||||||
|
}
|
||||||
|
found_piv:
|
||||||
|
++(ipiv[icol]) ;
|
||||||
|
/*
|
||||||
|
* swap rows irow and icol, so afterwards the diagonal
|
||||||
|
* element will be correct. Rarely done, not worth
|
||||||
|
* optimizing.
|
||||||
|
*/
|
||||||
|
if (irow != icol) {
|
||||||
|
for (ix = 0 ; ix < k ; ix++ ) {
|
||||||
|
SWAP( src[irow*k + ix], src[icol*k + ix], gf) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indxr[col] = irow ;
|
||||||
|
indxc[col] = icol ;
|
||||||
|
pivot_row = &src[icol*k] ;
|
||||||
|
c = pivot_row[icol] ;
|
||||||
|
if (c == 0) {
|
||||||
|
fprintf(stderr, "singular matrix 2\n");
|
||||||
|
goto fail ;
|
||||||
|
}
|
||||||
|
if (c != 1 ) { /* otherwhise this is a NOP */
|
||||||
|
/*
|
||||||
|
* this is done often , but optimizing is not so
|
||||||
|
* fruitful, at least in the obvious ways (unrolling)
|
||||||
|
*/
|
||||||
|
DEB( pivswaps++ ; )
|
||||||
|
c = inverse[ c ] ;
|
||||||
|
pivot_row[icol] = 1 ;
|
||||||
|
for (ix = 0 ; ix < k ; ix++ )
|
||||||
|
pivot_row[ix] = gf_mul(c, pivot_row[ix] );
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* from all rows, remove multiples of the selected row
|
||||||
|
* to zero the relevant entry (in fact, the entry is not zero
|
||||||
|
* because we know it must be zero).
|
||||||
|
* (Here, if we know that the pivot_row is the identity,
|
||||||
|
* we can optimize the addmul).
|
||||||
|
*/
|
||||||
|
id_row[icol] = 1;
|
||||||
|
if (bcmp(pivot_row, id_row, k*sizeof(gf)) != 0) {
|
||||||
|
for (p = src, ix = 0 ; ix < k ; ix++, p += k ) {
|
||||||
|
if (ix != icol) {
|
||||||
|
c = p[icol] ;
|
||||||
|
p[icol] = 0 ;
|
||||||
|
addmul(p, pivot_row, c, k );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id_row[icol] = 0;
|
||||||
|
} /* done all columns */
|
||||||
|
for (col = k-1 ; col >= 0 ; col-- ) {
|
||||||
|
if (indxr[col] <0 || indxr[col] >= k)
|
||||||
|
fprintf(stderr, "AARGH, indxr[col] %d\n", indxr[col]);
|
||||||
|
else if (indxc[col] <0 || indxc[col] >= k)
|
||||||
|
fprintf(stderr, "AARGH, indxc[col] %d\n", indxc[col]);
|
||||||
|
else
|
||||||
|
if (indxr[col] != indxc[col] ) {
|
||||||
|
for (row = 0 ; row < k ; row++ ) {
|
||||||
|
SWAP( src[row*k + indxr[col]], src[row*k + indxc[col]], gf) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = 0 ;
|
||||||
|
fail:
|
||||||
|
free(indxc);
|
||||||
|
free(indxr);
|
||||||
|
free(ipiv);
|
||||||
|
free(id_row);
|
||||||
|
free(temp_row);
|
||||||
|
return error ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fast code for inverting a vandermonde matrix.
|
||||||
|
* XXX NOTE: It assumes that the matrix
|
||||||
|
* is not singular and _IS_ a vandermonde matrix. Only uses
|
||||||
|
* the second column of the matrix, containing the p_i's.
|
||||||
|
*
|
||||||
|
* Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but
|
||||||
|
* largely revised for my purposes.
|
||||||
|
* p = coefficients of the matrix (p_i)
|
||||||
|
* q = values of the polynomial (known)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
invert_vdm(gf *src, int k)
|
||||||
|
{
|
||||||
|
int i, j, row, col ;
|
||||||
|
gf *b, *c, *p;
|
||||||
|
gf t, xx ;
|
||||||
|
|
||||||
|
if (k == 1) /* degenerate case, matrix must be p^0 = 1 */
|
||||||
|
return 0 ;
|
||||||
|
/*
|
||||||
|
* c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
|
||||||
|
* b holds the coefficient for the matrix inversion
|
||||||
|
*/
|
||||||
|
c = NEW_GF_MATRIX(1, k);
|
||||||
|
b = NEW_GF_MATRIX(1, k);
|
||||||
|
|
||||||
|
p = NEW_GF_MATRIX(1, k);
|
||||||
|
|
||||||
|
for ( j=1, i = 0 ; i < k ; i++, j+=k ) {
|
||||||
|
c[i] = 0 ;
|
||||||
|
p[i] = src[j] ; /* p[i] */
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* construct coeffs. recursively. We know c[k] = 1 (implicit)
|
||||||
|
* and start P_0 = x - p_0, then at each stage multiply by
|
||||||
|
* x - p_i generating P_i = x P_{i-1} - p_i P_{i-1}
|
||||||
|
* After k steps we are done.
|
||||||
|
*/
|
||||||
|
c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */
|
||||||
|
for (i = 1 ; i < k ; i++ ) {
|
||||||
|
gf p_i = p[i] ; /* see above comment */
|
||||||
|
for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ )
|
||||||
|
c[j] ^= gf_mul( p_i, c[j+1] ) ;
|
||||||
|
c[k-1] ^= p_i ;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (row = 0 ; row < k ; row++ ) {
|
||||||
|
/*
|
||||||
|
* synthetic division etc.
|
||||||
|
*/
|
||||||
|
xx = p[row] ;
|
||||||
|
t = 1 ;
|
||||||
|
b[k-1] = 1 ; /* this is in fact c[k] */
|
||||||
|
for (i = k-2 ; i >= 0 ; i-- ) {
|
||||||
|
b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ;
|
||||||
|
t = gf_mul(xx, t) ^ b[i] ;
|
||||||
|
}
|
||||||
|
for (col = 0 ; col < k ; col++ )
|
||||||
|
src[col*k + row] = gf_mul(inverse[t], b[col] );
|
||||||
|
}
|
||||||
|
free(c) ;
|
||||||
|
free(b) ;
|
||||||
|
free(p) ;
|
||||||
|
return 0 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fec_initialized = 0 ;
|
||||||
|
static void
|
||||||
|
init_fec()
|
||||||
|
{
|
||||||
|
TICK(ticks[0]);
|
||||||
|
generate_gf();
|
||||||
|
TOCK(ticks[0]);
|
||||||
|
DDB(fprintf(stderr, "generate_gf took %ldus\n", ticks[0]);)
|
||||||
|
TICK(ticks[0]);
|
||||||
|
init_mul_table();
|
||||||
|
TOCK(ticks[0]);
|
||||||
|
DDB(fprintf(stderr, "init_mul_table took %ldus\n", ticks[0]);)
|
||||||
|
fec_initialized = 1 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This section contains the proper FEC encoding/decoding routines.
|
||||||
|
* The encoding matrix is computed starting with a Vandermonde matrix,
|
||||||
|
* and then transforming it into a systematic matrix.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FEC_MAGIC 0xFECC0DEC
|
||||||
|
|
||||||
|
struct fec_parms {
|
||||||
|
u_long magic ;
|
||||||
|
int k, n ; /* parameters of the code */
|
||||||
|
gf *enc_matrix ;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
void
|
||||||
|
fec_free(void *p0)
|
||||||
|
{
|
||||||
|
struct fec_parms *p= (struct fec_parms *) p0;
|
||||||
|
if (p==NULL ||
|
||||||
|
p->magic != ( ( (FEC_MAGIC ^ p->k) ^ p->n) ^ (int)((long)p->enc_matrix)) ) {
|
||||||
|
fprintf(stderr, "bad parameters to fec_free\n");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
free(p->enc_matrix);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create a new encoder, returning a descriptor. This contains k,n and
|
||||||
|
* the encoding matrix.
|
||||||
|
*/
|
||||||
|
struct fec_parms *
|
||||||
|
fec_new(int k, int n)
|
||||||
|
{
|
||||||
|
int row, col ;
|
||||||
|
gf *p, *tmp_m ;
|
||||||
|
|
||||||
|
struct fec_parms *retval ;
|
||||||
|
|
||||||
|
if (fec_initialized == 0)
|
||||||
|
init_fec();
|
||||||
|
|
||||||
|
if (k > GF_SIZE + 1 || n > GF_SIZE + 1 || k > n ) {
|
||||||
|
fprintf(stderr, "Invalid parameters k %d n %d GF_SIZE %d\n",
|
||||||
|
k, n, GF_SIZE );
|
||||||
|
return NULL ;
|
||||||
|
}
|
||||||
|
retval = (struct fec_parms *)my_malloc(sizeof(struct fec_parms), "new_code");
|
||||||
|
retval->k = k ;
|
||||||
|
retval->n = n ;
|
||||||
|
retval->enc_matrix = NEW_GF_MATRIX(n, k);
|
||||||
|
retval->magic = ( ( FEC_MAGIC ^ k) ^ n) ^ (int)((long)retval->enc_matrix) ;
|
||||||
|
tmp_m = NEW_GF_MATRIX(n, k);
|
||||||
|
/*
|
||||||
|
* fill the matrix with powers of field elements, starting from 0.
|
||||||
|
* The first row is special, cannot be computed with exp. table.
|
||||||
|
*/
|
||||||
|
tmp_m[0] = 1 ;
|
||||||
|
for (col = 1; col < k ; col++)
|
||||||
|
tmp_m[col] = 0 ;
|
||||||
|
for (p = tmp_m + k, row = 0; row < n-1 ; row++, p += k) {
|
||||||
|
for ( col = 0 ; col < k ; col ++ )
|
||||||
|
p[col] = gf_exp[modnn(row*col)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* quick code to build systematic matrix: invert the top
|
||||||
|
* k*k vandermonde matrix, multiply right the bottom n-k rows
|
||||||
|
* by the inverse, and construct the identity matrix at the top.
|
||||||
|
*/
|
||||||
|
TICK(ticks[3]);
|
||||||
|
invert_vdm(tmp_m, k); /* much faster than invert_mat */
|
||||||
|
matmul(tmp_m + k*k, tmp_m, retval->enc_matrix + k*k, n - k, k, k);
|
||||||
|
/*
|
||||||
|
* the upper matrix is I so do not bother with a slow multiply
|
||||||
|
*/
|
||||||
|
bzero(retval->enc_matrix, k*k*sizeof(gf) );
|
||||||
|
for (p = retval->enc_matrix, col = 0 ; col < k ; col++, p += k+1 )
|
||||||
|
*p = 1 ;
|
||||||
|
free(tmp_m);
|
||||||
|
TOCK(ticks[3]);
|
||||||
|
|
||||||
|
DDB(fprintf(stderr, "--- %ld us to build encoding matrix\n",
|
||||||
|
ticks[3]);)
|
||||||
|
DEB(pr_matrix(retval->enc_matrix, n, k, "encoding_matrix");)
|
||||||
|
return retval ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fec_encode accepts as input pointers to n data packets of size sz,
|
||||||
|
* and produces as output a packet pointed to by fec, computed
|
||||||
|
* with index "index".
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fec_encode(void *code0, void *src0[], void *fec0, int index, int sz)
|
||||||
|
//fec_encode(struct fec_parms *code0, gf *src[], gf *fec, int index, int sz)
|
||||||
|
{
|
||||||
|
struct fec_parms * code= (struct fec_parms *)code0;
|
||||||
|
gf **src=(gf**) src0;
|
||||||
|
gf* fec=(gf*)fec0;
|
||||||
|
int i, k = code->k ;
|
||||||
|
gf *p ;
|
||||||
|
|
||||||
|
if (GF_BITS > 8)
|
||||||
|
sz /= 2 ;
|
||||||
|
|
||||||
|
if (index < k)
|
||||||
|
bcopy(src[index], fec, sz*sizeof(gf) ) ;
|
||||||
|
else if (index < code->n) {
|
||||||
|
p = &(code->enc_matrix[index*k] );
|
||||||
|
bzero(fec, sz*sizeof(gf));
|
||||||
|
for (i = 0; i < k ; i++)
|
||||||
|
addmul(fec, src[i], p[i], sz ) ;
|
||||||
|
} else
|
||||||
|
fprintf(stderr, "Invalid index %d (max %d)\n",
|
||||||
|
index, code->n - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* shuffle move src packets in their position
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
shuffle(gf *pkt[], int index[], int k)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for ( i = 0 ; i < k ; ) {
|
||||||
|
if (index[i] >= k || index[i] == i)
|
||||||
|
i++ ;
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* put pkt in the right position (first check for conflicts).
|
||||||
|
*/
|
||||||
|
int c = index[i] ;
|
||||||
|
|
||||||
|
if (index[c] == c) {
|
||||||
|
DEB(fprintf(stderr, "\nshuffle, error at %d\n", i);)
|
||||||
|
return 1 ;
|
||||||
|
}
|
||||||
|
SWAP(index[i], index[c], int) ;
|
||||||
|
SWAP(pkt[i], pkt[c], gf *) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEB( /* just test that it works... */
|
||||||
|
for ( i = 0 ; i < k ; i++ ) {
|
||||||
|
if (index[i] < k && index[i] != i) {
|
||||||
|
fprintf(stderr, "shuffle: after\n");
|
||||||
|
for (i=0; i<k ; i++) fprintf(stderr, "%3d ", index[i]);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
return 1 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return 0 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* build_decode_matrix constructs the encoding matrix given the
|
||||||
|
* indexes. The matrix must be already allocated as
|
||||||
|
* a vector of k*k elements, in row-major order
|
||||||
|
*/
|
||||||
|
static gf *
|
||||||
|
build_decode_matrix(struct fec_parms *code, gf *pkt[], int index[])
|
||||||
|
{
|
||||||
|
int i , k = code->k ;
|
||||||
|
gf *p, *matrix = NEW_GF_MATRIX(k, k);
|
||||||
|
|
||||||
|
TICK(ticks[9]);
|
||||||
|
for (i = 0, p = matrix ; i < k ; i++, p += k ) {
|
||||||
|
#if 1 /* this is simply an optimization, not very useful indeed */
|
||||||
|
if (index[i] < k) {
|
||||||
|
bzero(p, k*sizeof(gf) );
|
||||||
|
p[i] = 1 ;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
if (index[i] < code->n )
|
||||||
|
bcopy( &(code->enc_matrix[index[i]*k]), p, k*sizeof(gf) );
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "decode: invalid index %d (max %d)\n",
|
||||||
|
index[i], code->n - 1 );
|
||||||
|
free(matrix) ;
|
||||||
|
return NULL ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TICK(ticks[9]);
|
||||||
|
if (invert_mat(matrix, k)) {
|
||||||
|
free(matrix);
|
||||||
|
matrix = NULL ;
|
||||||
|
}
|
||||||
|
TOCK(ticks[9]);
|
||||||
|
return matrix ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fec_decode receives as input a vector of packets, the indexes of
|
||||||
|
* packets, and produces the correct vector as output.
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* code: pointer to code descriptor
|
||||||
|
* pkt: pointers to received packets. They are modified
|
||||||
|
* to store the output packets (in place)
|
||||||
|
* index: pointer to packet indexes (modified)
|
||||||
|
* sz: size of each packet
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
fec_decode(void *code0, void *pkt0[], int index[], int sz)
|
||||||
|
//fec_decode(struct fec_parms *code, gf *pkt[], int index[], int sz)
|
||||||
|
{
|
||||||
|
struct fec_parms * code=(struct fec_parms*)code0;
|
||||||
|
gf **pkt=(gf**)pkt0;
|
||||||
|
gf *m_dec ;
|
||||||
|
gf **new_pkt ;
|
||||||
|
int row, col , k = code->k ;
|
||||||
|
|
||||||
|
if (GF_BITS > 8)
|
||||||
|
sz /= 2 ;
|
||||||
|
|
||||||
|
if (shuffle(pkt, index, k)) /* error if true */
|
||||||
|
return 1 ;
|
||||||
|
m_dec = build_decode_matrix(code, pkt, index);
|
||||||
|
|
||||||
|
if (m_dec == NULL)
|
||||||
|
return 1 ; /* error */
|
||||||
|
/*
|
||||||
|
* do the actual decoding
|
||||||
|
*/
|
||||||
|
new_pkt = (gf** )my_malloc (k * sizeof (gf * ), "new pkt pointers" );
|
||||||
|
for (row = 0 ; row < k ; row++ ) {
|
||||||
|
if (index[row] >= k) {
|
||||||
|
new_pkt[row] = (gf*)my_malloc (sz * sizeof (gf), "new pkt buffer" );
|
||||||
|
bzero(new_pkt[row], sz * sizeof(gf) ) ;
|
||||||
|
for (col = 0 ; col < k ; col++ )
|
||||||
|
addmul(new_pkt[row], pkt[col], m_dec[row*k + col], sz) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* move pkts to their final destination
|
||||||
|
*/
|
||||||
|
for (row = 0 ; row < k ; row++ ) {
|
||||||
|
if (index[row] >= k) {
|
||||||
|
bcopy(new_pkt[row], pkt[row], sz*sizeof(gf));
|
||||||
|
free(new_pkt[row]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(new_pkt);
|
||||||
|
free(m_dec);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int get_n(void *code0)
|
||||||
|
{
|
||||||
|
struct fec_parms * code= (struct fec_parms *)code0;
|
||||||
|
return code->n;
|
||||||
|
}
|
||||||
|
int get_k(void *code0)
|
||||||
|
{
|
||||||
|
struct fec_parms * code= (struct fec_parms *)code0;
|
||||||
|
return code->k;
|
||||||
|
}
|
||||||
|
/*********** end of FEC code -- beginning of test code ************/
|
||||||
|
|
||||||
|
#if (TEST || DEBUG)
|
||||||
|
void
|
||||||
|
test_gf()
|
||||||
|
{
|
||||||
|
int i ;
|
||||||
|
/*
|
||||||
|
* test gf tables. Sufficiently tested...
|
||||||
|
*/
|
||||||
|
for (i=0; i<= GF_SIZE; i++) {
|
||||||
|
if (gf_exp[gf_log[i]] != i)
|
||||||
|
fprintf(stderr, "bad exp/log i %d log %d exp(log) %d\n",
|
||||||
|
i, gf_log[i], gf_exp[gf_log[i]]);
|
||||||
|
|
||||||
|
if (i != 0 && gf_mul(i, inverse[i]) != 1)
|
||||||
|
fprintf(stderr, "bad mul/inv i %d inv %d i*inv(i) %d\n",
|
||||||
|
i, inverse[i], gf_mul(i, inverse[i]) );
|
||||||
|
if (gf_mul(0,i) != 0)
|
||||||
|
fprintf(stderr, "bad mul table 0,%d\n",i);
|
||||||
|
if (gf_mul(i,0) != 0)
|
||||||
|
fprintf(stderr, "bad mul table %d,0\n",i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TEST */
|
56
lib/fec.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* fec.c -- forward error correction based on Vandermonde matrices
|
||||||
|
* 980614
|
||||||
|
* (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
|
||||||
|
*
|
||||||
|
* Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
|
||||||
|
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
|
||||||
|
* Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||||
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||||
|
* OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following parameter defines how many bits are used for
|
||||||
|
* field elements. The code supports any value from 2 to 16
|
||||||
|
* but fastest operation is achieved with 8 bit elements
|
||||||
|
* This is the only parameter you may want to change.
|
||||||
|
*/
|
||||||
|
#ifndef GF_BITS
|
||||||
|
#define GF_BITS 8 /* code over GF(2**GF_BITS) - change to suit */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GF_SIZE ((1 << GF_BITS) - 1) /* powers of \alpha */
|
||||||
|
void fec_free(void *p) ;
|
||||||
|
void * fec_new(int k, int n) ;//n>=k
|
||||||
|
|
||||||
|
void init_fec() ; //if you never called this,it will be automatically called in fec_new()
|
||||||
|
void fec_encode(void *code, void *src[], void *dst, int index, int sz) ;
|
||||||
|
int fec_decode(void *code, void *pkt[], int index[], int sz) ;
|
||||||
|
|
||||||
|
int get_k(void *code);
|
||||||
|
int get_n(void *code);
|
||||||
|
|
||||||
|
/* end of file */
|
76
lib/rs.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* rs.c
|
||||||
|
*
|
||||||
|
* Created on: Sep 14, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
#include "rs.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
void rs_encode(void *code,char *data[],int size)
|
||||||
|
{
|
||||||
|
int k=get_k(code);
|
||||||
|
int n=get_n(code);
|
||||||
|
for(int i=k;i<n;i++)
|
||||||
|
{
|
||||||
|
fec_encode(code, (void **)data, data[i],i, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs_decode(void *code,char *data[],int size)
|
||||||
|
{
|
||||||
|
int k=get_k(code);
|
||||||
|
int n=get_n(code);
|
||||||
|
int index[n];
|
||||||
|
int count=0;
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
if(data[i]!=0)
|
||||||
|
{
|
||||||
|
index[count++]=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(count<k)
|
||||||
|
return -1;
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
if(i<count)
|
||||||
|
data[i]=data[index[i]];
|
||||||
|
else
|
||||||
|
data[i]=0;
|
||||||
|
}
|
||||||
|
return fec_decode(code,(void**)data,index,size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void * (*table)[256]=0;
|
||||||
|
void* get_code(int k,int n)
|
||||||
|
{
|
||||||
|
if (table==0)
|
||||||
|
{
|
||||||
|
table=(void* (*)[256]) malloc(sizeof(void*)*256*256);
|
||||||
|
if(!table)
|
||||||
|
{
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
memset(table,0,sizeof(void*)*256*256);
|
||||||
|
}
|
||||||
|
if(table[k][n]==0)
|
||||||
|
{
|
||||||
|
table[k][n]=fec_new(k,n);
|
||||||
|
}
|
||||||
|
return table[k][n];
|
||||||
|
}
|
||||||
|
void rs_encode2(int k,int n,char *data[],int size)
|
||||||
|
{
|
||||||
|
void* code=get_code(k,n);
|
||||||
|
rs_encode(code,data,size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs_decode2(int k,int n,char *data[],int size)
|
||||||
|
{
|
||||||
|
void* code=get_code(k,n);
|
||||||
|
return rs_decode(code,data,size);
|
||||||
|
}
|
51
lib/rs.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* rs.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 14, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIB_RS_H_
|
||||||
|
#define LIB_RS_H_
|
||||||
|
|
||||||
|
#include "fec.h"
|
||||||
|
|
||||||
|
// input:
|
||||||
|
// code, generated by fec_new() function from fec.h
|
||||||
|
// data[0....k-1 ], points to original data
|
||||||
|
// size, data length
|
||||||
|
//
|
||||||
|
// output:
|
||||||
|
// data[k....n-1], points to generated redundant data
|
||||||
|
//
|
||||||
|
// info:
|
||||||
|
// the function will always succeed,except malloc fail.if malloc fail,it will call exit()
|
||||||
|
void rs_encode(void *code,char *data[],int size);
|
||||||
|
|
||||||
|
|
||||||
|
// input:
|
||||||
|
// data[0.....n-1] points to original data and redundate data,in right order
|
||||||
|
// if data[i] is missing ,set poniter data[i] to 0 (point it to null)
|
||||||
|
//
|
||||||
|
// outout:
|
||||||
|
// data[0.....k-1] will point to the recovered original data.
|
||||||
|
//
|
||||||
|
// info:
|
||||||
|
// return zero on success
|
||||||
|
// if the number of no-zero pointers is less than k,the function will fail and return non-zero
|
||||||
|
//
|
||||||
|
// advanced info:
|
||||||
|
// 1. rs_decode wont malloc memory for those zero pointers in data[0.....k-1]. instead it will re-use the memory of other non-zero pointers (and let data[0.....k-1] point to those memory).
|
||||||
|
// 2. if the input data[0.....n-1] contains x non-zero pointers,after called rs_decode,there will still be exactly x non-zero poninters in data[0.....n-1],just the order may change.
|
||||||
|
int rs_decode(void *code,char *data[],int size);
|
||||||
|
|
||||||
|
|
||||||
|
void rs_encode2(int k,int n,char *data[],int size);
|
||||||
|
|
||||||
|
int rs_decode2(int k,int n,char *data[],int size);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIB_RS_H_ */
|
83
makefile
Normal file → Executable file
@@ -1,18 +1,69 @@
|
|||||||
cross_cc=mips-openwrt-linux-g++
|
cc_cross=/home/wangyu/Desktop/arm-2014.05/bin/arm-none-linux-gnueabi-g++
|
||||||
FLAGS=-Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -ggdb
|
cc_local=g++
|
||||||
FLAGS2= -O3
|
#cc_mips34kc=/toolchains/OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/staging_dir/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-g++
|
||||||
all:
|
cc_mips24kc_be=/toolchains/lede-sdk-17.01.2-ar71xx-generic_gcc-5.4.0_musl-1.1.16.Linux-x86_64/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/bin/mips-openwrt-linux-musl-g++
|
||||||
g++ main.cpp common.cpp log.cpp -I. -o speeder -static -lrt -std=c++11 ${FLAGS} ${FLAGS2}
|
cc_mips24kc_le=/toolchains/lede-sdk-17.01.2-ramips-mt7621_gcc-5.4.0_musl-1.1.16.Linux-x86_64/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/bin/mipsel-openwrt-linux-musl-g++
|
||||||
release:
|
#cc_arm= /toolchains/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi-g++ -march=armv6 -marm
|
||||||
g++ main.cpp common.cpp log.cpp -I. -o speeder_amd64 -static -lrt -std=c++11 ${FLAGS} ${FLAGS2}
|
cc_arm= /toolchains/arm-2014.05/bin/arm-none-linux-gnueabi-g++
|
||||||
g++ -m32 main.cpp common.cpp log.cpp -I. -o speeder_x86 -static -lrt -std=c++11 ${FLAGS} ${FLAGS2}
|
#cc_bcm2708=/home/wangyu/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++
|
||||||
${cross_cc} main.cpp common.cpp log.cpp -I. -o speeder_ar71xx -lrt -std=c++11 ${FLAGS} ${FLAGS2}
|
FLAGS= -std=c++11 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers -ggdb
|
||||||
tar -zcvf udp_speeder_binaries.tar.gz speeder_amd64 speeder_x86 speeder_ar71xx
|
|
||||||
cross:
|
|
||||||
${cross_cc} main.cpp common.cpp log.cpp -I. -o speeder_cross -lrt -std=c++11 ${FLAGS} ${FLAGS2}
|
|
||||||
|
|
||||||
debug:
|
SOURCES=main.cpp log.cpp common.cpp lib/fec.c lib/rs.c packet.cpp delay_manager.cpp fd_manager.cpp connection.cpp fec_manager.cpp misc.cpp tunnel.cpp
|
||||||
g++ main.cpp common.cpp log.cpp -I. -o speeder -static -lrt -std=c++11 ${FLAGS} -Wformat-nonliteral -D MY_DEBUG
|
|
||||||
|
NAME=speederv2
|
||||||
|
TARGETS=amd64 arm mips24kc_be x86 mips24kc_le
|
||||||
|
|
||||||
|
TAR=${NAME}_binaries.tar.gz `echo ${TARGETS}|sed -r 's/([^ ]+)/speederv2_\1/g'` version.txt
|
||||||
|
|
||||||
|
all:git_version
|
||||||
|
rm -f ${NAME}
|
||||||
|
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O3
|
||||||
|
debug: git_version
|
||||||
|
rm -f ${NAME}
|
||||||
|
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -D MY_DEBUG
|
||||||
|
debug2: git_version
|
||||||
|
rm -f ${NAME}
|
||||||
|
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -ggdb
|
||||||
|
|
||||||
|
mips24kc_be: git_version
|
||||||
|
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
||||||
|
|
||||||
|
mips24kc_be_debug: git_version
|
||||||
|
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -ggdb
|
||||||
|
|
||||||
|
mips24kc_le: git_version
|
||||||
|
${cc_mips24kc_le} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
||||||
|
|
||||||
|
amd64:git_version
|
||||||
|
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||||
|
amd64_debug:git_version
|
||||||
|
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -ggdb
|
||||||
|
x86:git_version
|
||||||
|
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3 -m32
|
||||||
|
arm:git_version
|
||||||
|
${cc_arm} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||||
|
|
||||||
|
arm_debug:git_version
|
||||||
|
${cc_arm} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -ggdb
|
||||||
|
|
||||||
|
cross:git_version
|
||||||
|
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -O3
|
||||||
|
|
||||||
|
cross2:git_version
|
||||||
|
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -static -lgcc_eh -O3
|
||||||
|
|
||||||
|
cross3:git_version
|
||||||
|
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||||
|
|
||||||
|
release: ${TARGETS}
|
||||||
|
cp git_version.h version.txt
|
||||||
|
tar -zcvf ${TAR}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ${TAR}
|
||||||
|
rm -f speeder speeder_cross
|
||||||
|
rm -f git_version.h
|
||||||
|
|
||||||
|
git_version:
|
||||||
|
echo "const char * const gitversion = \"$(shell git rev-parse HEAD)\";" > git_version.h
|
||||||
|
|
||||||
#g++ forward.cpp aes.c -o forward -static
|
|
||||||
# ${ccarm} forward.cpp aes.c -o forwardarm -static -lgcc_eh
|
|
||||||
|
974
misc.cpp
Normal file
@@ -0,0 +1,974 @@
|
|||||||
|
/*
|
||||||
|
* misc.cpp
|
||||||
|
*
|
||||||
|
* Created on: Oct 26, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
|
||||||
|
char fifo_file[1000]="";
|
||||||
|
|
||||||
|
int mtu_warn=1350;
|
||||||
|
|
||||||
|
int disable_mtu_warn=1;
|
||||||
|
int disable_fec=0;
|
||||||
|
int disable_checksum=0;
|
||||||
|
|
||||||
|
int debug_force_flush_fec=0;
|
||||||
|
|
||||||
|
|
||||||
|
int jitter_min=0*1000;
|
||||||
|
int jitter_max=0*1000;
|
||||||
|
|
||||||
|
int output_interval_min=0*1000;
|
||||||
|
int output_interval_max=0*1000;
|
||||||
|
|
||||||
|
int fix_latency=0;
|
||||||
|
|
||||||
|
u32_t local_ip_uint32,remote_ip_uint32=0;
|
||||||
|
char local_ip[100], remote_ip[100];
|
||||||
|
int local_port = -1, remote_port = -1;
|
||||||
|
|
||||||
|
conn_manager_t conn_manager;
|
||||||
|
delay_manager_t delay_manager;
|
||||||
|
fd_manager_t fd_manager;
|
||||||
|
|
||||||
|
int time_mono_test=0;
|
||||||
|
|
||||||
|
int delay_capacity=0;
|
||||||
|
|
||||||
|
|
||||||
|
char sub_net[100]="10.22.22.0";
|
||||||
|
u32_t sub_net_uint32=0;
|
||||||
|
|
||||||
|
char tun_dev[100]="";
|
||||||
|
|
||||||
|
|
||||||
|
int keep_reconnect=0;
|
||||||
|
|
||||||
|
int tun_mtu=1500;
|
||||||
|
|
||||||
|
int mssfix=1;
|
||||||
|
|
||||||
|
|
||||||
|
int from_normal_to_fec(conn_info_t & conn_info,char *data,int len,int & out_n,char **&out_arr,int *&out_len,my_time_t *&out_delay)
|
||||||
|
{
|
||||||
|
|
||||||
|
static my_time_t out_delay_buf[max_fec_packet_num+100]={0};
|
||||||
|
//static int out_len_buf[max_fec_packet_num+100]={0};
|
||||||
|
//static int counter=0;
|
||||||
|
out_delay=out_delay_buf;
|
||||||
|
//out_len=out_len_buf;
|
||||||
|
inner_stat_t &inner_stat=conn_info.stat.normal_to_fec;
|
||||||
|
if(disable_fec)
|
||||||
|
{
|
||||||
|
if(data==0)
|
||||||
|
{
|
||||||
|
out_n=0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//assert(data!=0);
|
||||||
|
inner_stat.input_packet_num++;
|
||||||
|
inner_stat.input_packet_size+=len;
|
||||||
|
inner_stat.output_packet_num++;
|
||||||
|
inner_stat.output_packet_size+=len;
|
||||||
|
|
||||||
|
out_n=1;
|
||||||
|
static char *data_static;
|
||||||
|
data_static=data;
|
||||||
|
static int len_static;
|
||||||
|
len_static=len;
|
||||||
|
out_arr=&data_static;
|
||||||
|
out_len=&len_static;
|
||||||
|
out_delay[0]=0;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(data!=0)
|
||||||
|
{
|
||||||
|
inner_stat.input_packet_num++;
|
||||||
|
inner_stat.input_packet_size+=len;
|
||||||
|
}
|
||||||
|
//counter++;
|
||||||
|
|
||||||
|
conn_info.fec_encode_manager.input(data,len);
|
||||||
|
|
||||||
|
//if(counter%5==0)
|
||||||
|
//conn_info.fec_encode_manager.input(0,0);
|
||||||
|
|
||||||
|
//int n;
|
||||||
|
//char **s_arr;
|
||||||
|
//int s_len;
|
||||||
|
|
||||||
|
|
||||||
|
conn_info.fec_encode_manager.output(out_n,out_arr,out_len);
|
||||||
|
|
||||||
|
if(out_n>0)
|
||||||
|
{
|
||||||
|
my_time_t common_latency=0;
|
||||||
|
my_time_t first_packet_time=conn_info.fec_encode_manager.get_first_packet_time();
|
||||||
|
|
||||||
|
if(fix_latency==1&&conn_info.fec_encode_manager.get_type()==0)
|
||||||
|
{
|
||||||
|
my_time_t current_time=get_current_time_us();
|
||||||
|
my_time_t tmp;
|
||||||
|
assert(first_packet_time!=0);
|
||||||
|
//mylog(log_info,"current_time=%llu first_packlet_time=%llu fec_pending_time=%llu\n",current_time,first_packet_time,(my_time_t)fec_pending_time);
|
||||||
|
if((my_time_t)conn_info.fec_encode_manager.get_pending_time() >=(current_time - first_packet_time))
|
||||||
|
{
|
||||||
|
tmp=(my_time_t)conn_info.fec_encode_manager.get_pending_time()-(current_time - first_packet_time);
|
||||||
|
//mylog(log_info,"tmp=%llu\n",tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp=0;
|
||||||
|
//mylog(log_info,"0\n");
|
||||||
|
}
|
||||||
|
common_latency+=tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
common_latency+=random_between(jitter_min,jitter_max);
|
||||||
|
|
||||||
|
out_delay_buf[0]=common_latency;
|
||||||
|
|
||||||
|
for(int i=1;i<out_n;i++)
|
||||||
|
{
|
||||||
|
out_delay_buf[i]=out_delay_buf[i-1]+ (my_time_t)( random_between(output_interval_min,output_interval_max)/(out_n-1) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mylog(log_trace,"from_normal_to_fec input_len=%d,output_n=%d\n",len,out_n);
|
||||||
|
|
||||||
|
if(out_n>0)
|
||||||
|
{
|
||||||
|
log_bare(log_trace,"seq= %u ",read_u32(out_arr[0]));
|
||||||
|
}
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
inner_stat.output_packet_num++;
|
||||||
|
inner_stat.output_packet_size+=out_len[i];
|
||||||
|
|
||||||
|
log_bare(log_trace,"%d ",out_len[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
log_bare(log_trace,"\n");
|
||||||
|
//for(int i=0;i<n;i++)
|
||||||
|
//{
|
||||||
|
//delay_send(0,dest,s_arr[i],s_len);
|
||||||
|
//}
|
||||||
|
//delay_send(0,dest,data,len);
|
||||||
|
//delay_send(1000*1000,dest,data,len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int from_fec_to_normal(conn_info_t & conn_info,char *data,int len,int & out_n,char **&out_arr,int *&out_len,my_time_t *&out_delay)
|
||||||
|
{
|
||||||
|
static my_time_t out_delay_buf[max_blob_packet_num+100]={0};
|
||||||
|
out_delay=out_delay_buf;
|
||||||
|
inner_stat_t &inner_stat=conn_info.stat.fec_to_normal;
|
||||||
|
if(disable_fec)
|
||||||
|
{
|
||||||
|
assert(data!=0);
|
||||||
|
inner_stat.input_packet_num++;
|
||||||
|
inner_stat.input_packet_size+=len;
|
||||||
|
inner_stat.output_packet_num++;
|
||||||
|
inner_stat.output_packet_size+=len;
|
||||||
|
|
||||||
|
if(data==0)
|
||||||
|
{
|
||||||
|
out_n=0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
out_n=1;
|
||||||
|
static char *data_static;
|
||||||
|
data_static=data;
|
||||||
|
static int len_static;
|
||||||
|
len_static=len;
|
||||||
|
out_arr=&data_static;
|
||||||
|
out_len=&len_static;
|
||||||
|
out_delay[0]=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if(data!=0)
|
||||||
|
{
|
||||||
|
inner_stat.input_packet_num++;
|
||||||
|
inner_stat.input_packet_size+=len;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_info.fec_decode_manager.input(data,len);
|
||||||
|
|
||||||
|
//int n;char ** s_arr;int* len_arr;
|
||||||
|
conn_info.fec_decode_manager.output(out_n,out_arr,out_len);
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
out_delay_buf[i]=0;
|
||||||
|
|
||||||
|
inner_stat.output_packet_num++;
|
||||||
|
inner_stat.output_packet_size+=out_len[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mylog(log_trace,"from_fec_to_normal input_len=%d,output_n=%d,input_seq=%u\n",len,out_n,read_u32(data));
|
||||||
|
|
||||||
|
|
||||||
|
// printf("<n:%d>",n);
|
||||||
|
/*
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
delay_send(0,dest,s_arr[i],len_arr[i]);
|
||||||
|
//s_arr[i][len_arr[i]]=0;
|
||||||
|
//printf("<%s>\n",s_arr[i]);
|
||||||
|
}*/
|
||||||
|
//my_send(dest,data,len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int delay_send(my_time_t delay,const dest_t &dest,char *data,int len)
|
||||||
|
{
|
||||||
|
//int rand=random()%100;
|
||||||
|
//mylog(log_info,"rand = %d\n",rand);
|
||||||
|
|
||||||
|
if (dest.cook&&random_drop != 0) {
|
||||||
|
if (get_true_random_number() % 10000 < (u32_t) random_drop) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return delay_manager.add(delay,dest,data,len);;
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_parameter()
|
||||||
|
{
|
||||||
|
mylog(log_info,"jitter_min=%d jitter_max=%d output_interval_min=%d output_interval_max=%d fec_timeout=%d fec_data_num=%d fec_redundant_num=%d fec_mtu=%d fec_queue_len=%d fec_mode=%d\n",
|
||||||
|
jitter_min/1000,jitter_max/1000,output_interval_min/1000,output_interval_max/1000,g_fec_timeout/1000,
|
||||||
|
g_fec_data_num,g_fec_redundant_num,g_fec_mtu,g_fec_queue_len,g_fec_mode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int handle_command(char *s)
|
||||||
|
{
|
||||||
|
int len=strlen(s);
|
||||||
|
while(len>=1&&s[len-1]=='\n')
|
||||||
|
s[len-1]=0;
|
||||||
|
mylog(log_info,"got data from fifo,len=%d,s=[%s]\n",len,s);
|
||||||
|
int a=-1,b=-1;
|
||||||
|
if(strncmp(s,"fec",strlen("fec"))==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"got command [fec]\n");
|
||||||
|
sscanf(s,"fec %d:%d",&a,&b);
|
||||||
|
if(a<1||b<0||a+b>254)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"invaild value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_fec_data_num=a;
|
||||||
|
g_fec_redundant_num=b;
|
||||||
|
}
|
||||||
|
else if(strncmp(s,"mtu",strlen("mtu"))==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"got command [mtu]\n");
|
||||||
|
sscanf(s,"mtu %d",&a);
|
||||||
|
if(a<100||a>2000)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"invaild value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_fec_mtu=a;
|
||||||
|
}
|
||||||
|
else if(strncmp(s,"queue-len",strlen("queue-len"))==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"got command [queue-len]\n");
|
||||||
|
sscanf(s,"queue-len %d",&a);
|
||||||
|
if(a<1||a>10000)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"invaild value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_fec_queue_len=a;
|
||||||
|
}
|
||||||
|
else if(strncmp(s,"mode",strlen("mode"))==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"got command [mode]\n");
|
||||||
|
sscanf(s,"mode %d",&a);
|
||||||
|
if(a!=0&&a!=1)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"invaild value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_fec_mode=a;
|
||||||
|
}
|
||||||
|
else if(strncmp(s,"timeout",strlen("timeout"))==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"got command [timeout]\n");
|
||||||
|
sscanf(s,"timeout %d",&a);
|
||||||
|
if(a<0||a>1000)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"invaild value\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
g_fec_timeout=a*1000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_info,"unknown command\n");
|
||||||
|
}
|
||||||
|
print_parameter();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unit_test()
|
||||||
|
{
|
||||||
|
int i,j,k;
|
||||||
|
void *code=fec_new(3,6);
|
||||||
|
char arr[6][100]=
|
||||||
|
{
|
||||||
|
"aaa","bbb","ccc"
|
||||||
|
,"ddd","eee","fff"
|
||||||
|
};
|
||||||
|
char *data[6];
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
{
|
||||||
|
data[i]=arr[i];
|
||||||
|
}
|
||||||
|
rs_encode2(3,6,data,3);
|
||||||
|
//printf("%d %d",(int)(unsigned char)arr[5][0],(int)('a'^'b'^'c'^'d'^'e'));
|
||||||
|
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
{
|
||||||
|
printf("<%s>",data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0]=0;
|
||||||
|
//data[1]=0;
|
||||||
|
//data[5]=0;
|
||||||
|
|
||||||
|
int ret=rs_decode2(3,6,data,3);
|
||||||
|
printf("ret:%d\n",ret);
|
||||||
|
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
{
|
||||||
|
printf("<%s>",data[i]);
|
||||||
|
}
|
||||||
|
fec_free(code);
|
||||||
|
|
||||||
|
|
||||||
|
char arr2[6][100]=
|
||||||
|
{
|
||||||
|
"aaa11111","","ccc333333333"
|
||||||
|
,"ddd444","eee5555","ff6666"
|
||||||
|
};
|
||||||
|
blob_encode_t blob_encode;
|
||||||
|
for(int i=0;i<6;i++)
|
||||||
|
blob_encode.input(arr2[i],strlen(arr2[i]));
|
||||||
|
|
||||||
|
char **output;
|
||||||
|
int shard_len;
|
||||||
|
blob_encode.output(7,output,shard_len);
|
||||||
|
|
||||||
|
|
||||||
|
printf("<shard_len:%d>",shard_len);
|
||||||
|
blob_decode_t blob_decode;
|
||||||
|
for(int i=0;i<7;i++)
|
||||||
|
{
|
||||||
|
blob_decode.input(output[i],shard_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
char **decode_output;
|
||||||
|
int * len_arr;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
|
||||||
|
ret=blob_decode.output(num,decode_output,len_arr);
|
||||||
|
|
||||||
|
printf("<num:%d,ret:%d>\n",num,ret);
|
||||||
|
for(int i=0;i<num;i++)
|
||||||
|
{
|
||||||
|
char buf[1000]={0};
|
||||||
|
memcpy(buf,decode_output[i],len_arr[i]);
|
||||||
|
printf("<%d:%s>",len_arr[i],buf);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
static fec_encode_manager_t fec_encode_manager;
|
||||||
|
static fec_decode_manager_t fec_decode_manager;
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
string a = "11111";
|
||||||
|
string b = "22";
|
||||||
|
string c = "33333333";
|
||||||
|
|
||||||
|
fec_encode_manager.input((char *) a.c_str(), a.length());
|
||||||
|
fec_encode_manager.input((char *) b.c_str(), b.length());
|
||||||
|
fec_encode_manager.input((char *) c.c_str(), c.length());
|
||||||
|
fec_encode_manager.input(0, 0);
|
||||||
|
|
||||||
|
int n;
|
||||||
|
char **s_arr;
|
||||||
|
int *len;
|
||||||
|
|
||||||
|
|
||||||
|
fec_encode_manager.output(n,s_arr,len);
|
||||||
|
printf("<n:%d,len:%d>",n,len[0]);
|
||||||
|
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
fec_decode_manager.input(s_arr[i],len[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int n;char ** s_arr;int* len_arr;
|
||||||
|
fec_decode_manager.output(n,s_arr,len_arr);
|
||||||
|
printf("<n:%d>",n);
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
s_arr[i][len_arr[i]]=0;
|
||||||
|
printf("<%s>\n",s_arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
string a = "aaaaaaa";
|
||||||
|
string b = "bbbbbbbbbbbbb";
|
||||||
|
string c = "ccc";
|
||||||
|
|
||||||
|
|
||||||
|
fec_encode_manager.input((char *) a.c_str(), a.length());
|
||||||
|
fec_encode_manager.input((char *) b.c_str(), b.length());
|
||||||
|
fec_encode_manager.input((char *) c.c_str(), c.length());
|
||||||
|
fec_encode_manager.input(0, 0);
|
||||||
|
|
||||||
|
int n;
|
||||||
|
char **s_arr;
|
||||||
|
int * len;
|
||||||
|
|
||||||
|
|
||||||
|
fec_encode_manager.output(n,s_arr,len);
|
||||||
|
printf("<n:%d,len:%d>",n,len[0]);
|
||||||
|
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
if(i==1||i==3||i==5||i==0)
|
||||||
|
fec_decode_manager.input(s_arr[i],len[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int n;char ** s_arr;int* len_arr;
|
||||||
|
fec_decode_manager.output(n,s_arr,len_arr);
|
||||||
|
printf("<n:%d>",n);
|
||||||
|
for(int i=0;i<n;i++)
|
||||||
|
{
|
||||||
|
s_arr[i][len_arr[i]]=0;
|
||||||
|
printf("<%s>\n",s_arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0;i<10;i++)
|
||||||
|
{
|
||||||
|
string a = "aaaaaaaaaaaaaaaaaaaaaaa";
|
||||||
|
string b = "bbbbbbbbbbbbb";
|
||||||
|
string c = "cccccccccccccccccc";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
printf("======\n");
|
||||||
|
int n;
|
||||||
|
char **s_arr;
|
||||||
|
int * len;
|
||||||
|
fec_decode_manager.output(n,s_arr,len);
|
||||||
|
|
||||||
|
fec_encode_manager.reset_fec_parameter(3,2,g_fec_mtu,g_fec_queue_len,g_fec_timeout,1);
|
||||||
|
|
||||||
|
fec_encode_manager.input((char *) a.c_str(), a.length());
|
||||||
|
fec_encode_manager.output(n,s_arr,len);
|
||||||
|
assert(n==1);
|
||||||
|
|
||||||
|
fec_decode_manager.input(s_arr[0],len[0]);
|
||||||
|
|
||||||
|
fec_decode_manager.output(n,s_arr,len);
|
||||||
|
assert(n==1);
|
||||||
|
printf("%s\n",s_arr[0]);
|
||||||
|
|
||||||
|
fec_encode_manager.input((char *) b.c_str(), b.length());
|
||||||
|
fec_encode_manager.output(n,s_arr,len);
|
||||||
|
assert(n==1);
|
||||||
|
//fec_decode_manager.input(s_arr[0],len[0]);
|
||||||
|
|
||||||
|
|
||||||
|
fec_encode_manager.input((char *) c.c_str(), c.length());
|
||||||
|
fec_encode_manager.output(n,s_arr,len);
|
||||||
|
|
||||||
|
assert(n==3);
|
||||||
|
|
||||||
|
fec_decode_manager.input(s_arr[0],len[0]);
|
||||||
|
//printf("n=%d\n",n);
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char **s_arr;
|
||||||
|
int * len;
|
||||||
|
fec_decode_manager.output(n,s_arr,len);
|
||||||
|
assert(n==1);
|
||||||
|
printf("%s\n",s_arr[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fec_decode_manager.input(s_arr[1],len[1]);
|
||||||
|
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char **s_arr;
|
||||||
|
int * len;
|
||||||
|
fec_decode_manager.output(n,s_arr,len);
|
||||||
|
assert(n==1);
|
||||||
|
printf("n=%d\n",n);
|
||||||
|
s_arr[0][len[0]]=0;
|
||||||
|
printf("%s\n",s_arr[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void process_arg(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int is_client=0,is_server=0;
|
||||||
|
int i, j, k;
|
||||||
|
int opt;
|
||||||
|
static struct option long_options[] =
|
||||||
|
{
|
||||||
|
{"log-level", required_argument, 0, 1},
|
||||||
|
{"log-position", no_argument, 0, 1},
|
||||||
|
{"disable-color", no_argument, 0, 1},
|
||||||
|
{"disable-filter", no_argument, 0, 1},
|
||||||
|
{"disable-fec", no_argument, 0, 1},
|
||||||
|
{"disable-obscure", no_argument, 0, 1},
|
||||||
|
{"disable-xor", no_argument, 0, 1},
|
||||||
|
{"disable-checksum", no_argument, 0, 1},
|
||||||
|
{"fix-latency", no_argument, 0, 1},
|
||||||
|
{"sock-buf", required_argument, 0, 1},
|
||||||
|
{"random-drop", required_argument, 0, 1},
|
||||||
|
{"report", required_argument, 0, 1},
|
||||||
|
{"delay-capacity", required_argument, 0, 1},
|
||||||
|
{"mtu", required_argument, 0, 1},
|
||||||
|
{"mode", required_argument, 0,1},
|
||||||
|
{"timeout", required_argument, 0,1},
|
||||||
|
{"decode-buf", required_argument, 0,1},
|
||||||
|
{"queue-len", required_argument, 0,'q'},
|
||||||
|
{"fec", required_argument, 0,'f'},
|
||||||
|
{"jitter", required_argument, 0,'j'},
|
||||||
|
{"fifo", required_argument, 0, 1},
|
||||||
|
{"sub-net", required_argument, 0, 1},
|
||||||
|
{"tun-dev", required_argument, 0, 1},
|
||||||
|
{"tun-mtu", required_argument, 0, 1},
|
||||||
|
{"disable-mssfix", no_argument, 0, 1},
|
||||||
|
{"keep-reconnect", no_argument, 0, 1},
|
||||||
|
{NULL, 0, 0, 0}
|
||||||
|
};
|
||||||
|
int option_index = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
if(strcmp(argv[i],"--unit-test")==0)
|
||||||
|
{
|
||||||
|
unit_test();
|
||||||
|
myexit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
if(strcmp(argv[i],"--log-level")==0)
|
||||||
|
{
|
||||||
|
if(i<argc -1)
|
||||||
|
{
|
||||||
|
sscanf(argv[i+1],"%d",&log_level);
|
||||||
|
if(0<=log_level&&log_level<log_end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_bare(log_fatal,"invalid log_level\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(strcmp(argv[i],"--disable-color")==0)
|
||||||
|
{
|
||||||
|
enable_log_color=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mylog(log_info,"argc=%d ", argc);
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
log_bare(log_info, "%s ", argv[i]);
|
||||||
|
}
|
||||||
|
log_bare(log_info, "\n");
|
||||||
|
|
||||||
|
|
||||||
|
int no_l = 1, no_r = 1;
|
||||||
|
while ((opt = getopt_long(argc, argv, "l:r:hcsk:j:f:p:n:i:q:",long_options,&option_index)) != -1)
|
||||||
|
{
|
||||||
|
//string opt_key;
|
||||||
|
//opt_key+=opt;
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 'k':
|
||||||
|
sscanf(optarg,"%s\n",key_string);
|
||||||
|
mylog(log_debug,"key=%s\n",key_string);
|
||||||
|
if(strlen(key_string)==0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"key len=0??\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
if (strchr(optarg, ':') == 0)
|
||||||
|
{
|
||||||
|
int jitter;
|
||||||
|
sscanf(optarg,"%d\n",&jitter);
|
||||||
|
if(jitter<0 ||jitter>1000*10)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"jitter must be between 0 and 10,000(10 second)\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
jitter_min=0;
|
||||||
|
jitter_max=jitter;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d:%d\n",&jitter_min,&jitter_max);
|
||||||
|
if(jitter_min<0 ||jitter_max<0||jitter_min>jitter_max)
|
||||||
|
{
|
||||||
|
mylog(log_fatal," must satisfy 0<=jmin<=jmax\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jitter_min*=1000;
|
||||||
|
jitter_max*=1000;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
if (strchr(optarg, ':') == 0)
|
||||||
|
{
|
||||||
|
int output_interval=-1;
|
||||||
|
sscanf(optarg,"%d\n",&output_interval);
|
||||||
|
if(output_interval<0||output_interval>1000*10)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"output_interval must be between 0 and 10,000(10 second)\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
output_interval_min=output_interval_max=output_interval;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d:%d\n",&output_interval_min,&output_interval_max);
|
||||||
|
if(output_interval_min<0 ||output_interval_max<0||output_interval_min>output_interval_max)
|
||||||
|
{
|
||||||
|
mylog(log_fatal," must satisfy 0<=output_interval_min<=output_interval_max\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output_interval_min*=1000;
|
||||||
|
output_interval_max*=1000;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (strchr(optarg, ':') == 0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"invalid format for f");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d:%d\n",&g_fec_data_num,&g_fec_redundant_num);
|
||||||
|
if(g_fec_data_num<1 ||g_fec_redundant_num<0||g_fec_data_num+g_fec_redundant_num>254)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"fec_data_num<1 ||fec_redundant_num<0||fec_data_num+fec_redundant_num>254\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
sscanf(optarg,"%d",&g_fec_queue_len);
|
||||||
|
if(g_fec_queue_len<1||g_fec_queue_len>10000)
|
||||||
|
{
|
||||||
|
|
||||||
|
mylog(log_fatal,"fec_pending_num should be between 1 and 10000\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
is_client = 1;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
is_server = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
no_l = 0;
|
||||||
|
if (strchr(optarg, ':') != 0)
|
||||||
|
{
|
||||||
|
sscanf(optarg, "%[^:]:%d", local_ip, &local_port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal," -r ip:port\n");
|
||||||
|
myexit(1);
|
||||||
|
strcpy(local_ip, "127.0.0.1");
|
||||||
|
sscanf(optarg, "%d", &local_port);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
no_r = 0;
|
||||||
|
if (strchr(optarg, ':') != 0)
|
||||||
|
{
|
||||||
|
//printf("in :\n");
|
||||||
|
//printf("%s\n",optarg);
|
||||||
|
sscanf(optarg, "%[^:]:%d", remote_ip, &remote_port);
|
||||||
|
//printf("%d\n",remote_port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal," -r ip:port\n");
|
||||||
|
myexit(1);
|
||||||
|
strcpy(remote_ip, "127.0.0.1");
|
||||||
|
sscanf(optarg, "%d", &remote_port);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if(strcmp(long_options[option_index].name,"log-level")==0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-filter")==0)
|
||||||
|
{
|
||||||
|
disable_replay_filter=1;
|
||||||
|
//enable_log_color=0;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-color")==0)
|
||||||
|
{
|
||||||
|
//enable_log_color=0;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-fec")==0)
|
||||||
|
{
|
||||||
|
disable_fec=1;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-obscure")==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"obscure disabled\n");
|
||||||
|
disable_obscure=1;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-xor")==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"xor disabled\n");
|
||||||
|
disable_xor=1;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-checksum")==0)
|
||||||
|
{
|
||||||
|
disable_checksum=1;
|
||||||
|
mylog(log_warn,"checksum disabled\n");
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"fix-latency")==0)
|
||||||
|
{
|
||||||
|
mylog(log_info,"fix-latency enabled\n");
|
||||||
|
fix_latency=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(strcmp(long_options[option_index].name,"log-position")==0)
|
||||||
|
{
|
||||||
|
enable_log_position=1;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"random-drop")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&random_drop);
|
||||||
|
if(random_drop<0||random_drop>10000)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"random_drop must be between 0 10000 \n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
mylog(log_info,"random_drop=%d\n",random_drop);
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"delay-capacity")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&delay_capacity);
|
||||||
|
|
||||||
|
if(delay_capacity<0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"delay_capacity must be >=0 \n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"report")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&report_interval);
|
||||||
|
|
||||||
|
if(report_interval<=0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"report_interval must be >0 \n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"sock-buf")==0)
|
||||||
|
{
|
||||||
|
int tmp=-1;
|
||||||
|
sscanf(optarg,"%d",&tmp);
|
||||||
|
if(10<=tmp&&tmp<=10*1024)
|
||||||
|
{
|
||||||
|
socket_buf_size=tmp*1024;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"sock-buf value must be between 1 and 10240 (kbyte) \n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"decode-buf")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&fec_buff_num);
|
||||||
|
if(fec_buff_num<300 || fec_buff_num>20000)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"decode-buf value must be between 300 and 20000 (kbyte) \n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
mylog(log_info,"decode-buf=%d\n",fec_buff_num);
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"mode")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&g_fec_mode);
|
||||||
|
if(g_fec_mode!=0&&g_fec_mode!=1)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"mode should be 0 or 1\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"mtu")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&g_fec_mtu);
|
||||||
|
if(g_fec_mtu<100||g_fec_mtu>2000)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"fec_mtu should be between 100 and 2000\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"timeout")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&g_fec_timeout);
|
||||||
|
if(g_fec_timeout<0||g_fec_timeout>1000)
|
||||||
|
{
|
||||||
|
|
||||||
|
mylog(log_fatal,"fec_pending_time should be between 0 and 1000(1s)\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
g_fec_timeout*=1000;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"fifo")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%s",fifo_file);
|
||||||
|
|
||||||
|
mylog(log_info,"fifo_file =%s \n",fifo_file);
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"keep-reconnect")==0)
|
||||||
|
{
|
||||||
|
keep_reconnect=1;
|
||||||
|
mylog(log_info,"keep_reconnect enabled\n");
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"sub-net")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%s",sub_net);
|
||||||
|
mylog(log_info,"sub_net %s\n",sub_net);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"tun-dev")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%s",tun_dev);
|
||||||
|
mylog(log_info,"tun_dev=%s\n",tun_dev);
|
||||||
|
|
||||||
|
mylog(log_info,"running at tun-dev mode\n");
|
||||||
|
working_mode=tun_dev_mode;
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"tun-mtu")==0)
|
||||||
|
{
|
||||||
|
sscanf(optarg,"%d",&tun_mtu);
|
||||||
|
mylog(log_warn,"changed tun_mtu,tun_mtu=%d\n",tun_mtu);
|
||||||
|
}
|
||||||
|
else if(strcmp(long_options[option_index].name,"disable-mssfix")==0)
|
||||||
|
{
|
||||||
|
mssfix=0;
|
||||||
|
mylog(log_warn,"mssfix disabled\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"unknown option\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mylog(log_fatal,"unknown option <%x>", opt);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_client == 0 && is_server == 0)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"-s -c hasnt been set\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
if (is_client == 1 && is_server == 1)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"-s -c cant be both set\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
if(is_client==1)
|
||||||
|
{
|
||||||
|
client_or_server=client_mode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client_or_server=server_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(working_mode==tunnel_mode)
|
||||||
|
{
|
||||||
|
if (no_l)
|
||||||
|
mylog(log_fatal,"error: -l not found\n");
|
||||||
|
if (no_r)
|
||||||
|
mylog(log_fatal,"error: -r not found\n");
|
||||||
|
if (no_l || no_r)
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
else if(working_mode==tun_dev_mode)
|
||||||
|
{
|
||||||
|
if(client_or_server==client_mode&&no_r)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"error: -r not found\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
else if(client_or_server==server_mode&&no_l)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"error: -l not found\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_parameter();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
74
misc.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* misc.h
|
||||||
|
*
|
||||||
|
* Created on: Oct 26, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MISC_H_
|
||||||
|
#define MISC_H_
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "connection.h"
|
||||||
|
#include "fd_manager.h"
|
||||||
|
#include "delay_manager.h"
|
||||||
|
#include "fec_manager.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern char fifo_file[1000];
|
||||||
|
|
||||||
|
extern int mtu_warn;
|
||||||
|
|
||||||
|
extern int disable_mtu_warn;
|
||||||
|
extern int disable_fec;
|
||||||
|
extern int disable_checksum;
|
||||||
|
|
||||||
|
extern int debug_force_flush_fec;
|
||||||
|
|
||||||
|
|
||||||
|
extern int jitter_min;
|
||||||
|
extern int jitter_max;
|
||||||
|
|
||||||
|
extern int output_interval_min;
|
||||||
|
extern int output_interval_max;
|
||||||
|
|
||||||
|
extern int fix_latency;
|
||||||
|
|
||||||
|
extern u32_t local_ip_uint32,remote_ip_uint32;
|
||||||
|
extern char local_ip[100], remote_ip[100];
|
||||||
|
extern int local_port, remote_port;
|
||||||
|
|
||||||
|
extern conn_manager_t conn_manager;
|
||||||
|
extern delay_manager_t delay_manager;
|
||||||
|
extern fd_manager_t fd_manager;
|
||||||
|
|
||||||
|
extern int time_mono_test;
|
||||||
|
|
||||||
|
extern int delay_capacity;
|
||||||
|
|
||||||
|
extern int keep_reconnect;
|
||||||
|
|
||||||
|
extern int tun_mtu;
|
||||||
|
|
||||||
|
extern int mssfix;
|
||||||
|
|
||||||
|
|
||||||
|
int from_normal_to_fec(conn_info_t & conn_info,char *data,int len,int & out_n,char **&out_arr,int *&out_len,my_time_t *&out_delay);
|
||||||
|
int from_fec_to_normal(conn_info_t & conn_info,char *data,int len,int & out_n,char **&out_arr,int *&out_len,my_time_t *&out_delay);
|
||||||
|
|
||||||
|
int delay_send(my_time_t delay,const dest_t &dest,char *data,int len);
|
||||||
|
int print_parameter();
|
||||||
|
int handle_command(char *s);
|
||||||
|
|
||||||
|
int unit_test();
|
||||||
|
|
||||||
|
//void print_help();
|
||||||
|
|
||||||
|
void process_arg(int argc, char *argv[]);
|
||||||
|
|
||||||
|
extern char sub_net[100];
|
||||||
|
extern u32_t sub_net_uint32;
|
||||||
|
extern char tun_dev[100];
|
||||||
|
|
||||||
|
#endif /* MISC_H_ */
|
402
packet.cpp
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* packet.cpp
|
||||||
|
*
|
||||||
|
* Created on: Sep 15, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
int iv_min=4;
|
||||||
|
int iv_max=32;//< 256;
|
||||||
|
u64_t packet_send_count=0;
|
||||||
|
u64_t dup_packet_send_count=0;
|
||||||
|
u64_t packet_recv_count=0;
|
||||||
|
u64_t dup_packet_recv_count=0;
|
||||||
|
|
||||||
|
typedef u64_t anti_replay_seq_t;
|
||||||
|
int disable_replay_filter=0;
|
||||||
|
|
||||||
|
int disable_obscure=0;
|
||||||
|
int disable_xor=0;
|
||||||
|
|
||||||
|
int random_drop=0;
|
||||||
|
|
||||||
|
char key_string[1000]= "";
|
||||||
|
|
||||||
|
//int local_listen_fd=-1;
|
||||||
|
|
||||||
|
|
||||||
|
void encrypt_0(char * input,int &len,char *key)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
if(key[0]==0) return;
|
||||||
|
for(i=0,j=0;i<len;i++,j++)
|
||||||
|
{
|
||||||
|
if(key[j]==0)j=0;
|
||||||
|
input[i]^=key[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrypt_0(char * input,int &len,char *key)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
if(key[0]==0) return;
|
||||||
|
for(i=0,j=0;i<len;i++,j++)
|
||||||
|
{
|
||||||
|
if(key[j]==0)j=0;
|
||||||
|
input[i]^=key[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int do_obscure_old(const char * input, int in_len,char *output,int &out_len)
|
||||||
|
{
|
||||||
|
//memcpy(output,input,in_len);
|
||||||
|
// out_len=in_len;
|
||||||
|
//return 0;
|
||||||
|
|
||||||
|
int i, j, k;
|
||||||
|
if (in_len > 65535||in_len<0)
|
||||||
|
return -1;
|
||||||
|
int iv_len=iv_min+rand()%(iv_max-iv_min);
|
||||||
|
get_true_random_chars(output,iv_len);
|
||||||
|
memcpy(output+iv_len,input,in_len);
|
||||||
|
|
||||||
|
output[iv_len+in_len]=(uint8_t)iv_len;
|
||||||
|
|
||||||
|
output[iv_len+in_len]^=output[0];
|
||||||
|
output[iv_len+in_len]^=key_string[0];
|
||||||
|
|
||||||
|
for(i=0,j=0,k=1;i<in_len;i++,j++,k++)
|
||||||
|
{
|
||||||
|
if(j==iv_len) j=0;
|
||||||
|
if(key_string[k]==0)k=0;
|
||||||
|
output[iv_len+i]^=output[j];
|
||||||
|
output[iv_len+i]^=key_string[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
out_len=iv_len+in_len+1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_obscure(char * data,int &len)
|
||||||
|
{
|
||||||
|
assert(len>=0);
|
||||||
|
assert(len<buf_len);
|
||||||
|
|
||||||
|
int iv_len=random_between(iv_min,iv_max);
|
||||||
|
get_true_random_chars(data+len,iv_len);
|
||||||
|
data[iv_len+len]=(uint8_t)iv_len;
|
||||||
|
for(int i=0,j=0;i<len;i++,j++)
|
||||||
|
{
|
||||||
|
if(j==iv_len)j=0;
|
||||||
|
data[i]^=data[len+j];
|
||||||
|
}
|
||||||
|
|
||||||
|
len=len+iv_len+1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int de_obscure(char * data,int &len)
|
||||||
|
{
|
||||||
|
if(len<1) return -1;
|
||||||
|
int iv_len=int ((uint8_t) data[len-1]);
|
||||||
|
|
||||||
|
if(len<1+iv_len) return -1;
|
||||||
|
|
||||||
|
len=len-1-iv_len;
|
||||||
|
for(int i=0,j=0;i<len;i++,j++)
|
||||||
|
{
|
||||||
|
if(j==iv_len)j=0;
|
||||||
|
data[i]^=data[len+j];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int de_obscure_old(const char * input, int in_len,char *output,int &out_len)
|
||||||
|
{
|
||||||
|
//memcpy(output,input,in_len);
|
||||||
|
//out_len=in_len;
|
||||||
|
//return 0;
|
||||||
|
|
||||||
|
int i, j, k;
|
||||||
|
if (in_len > 65535||in_len<0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"in_len > 65535||in_len<0 , %d",in_len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int iv_len= int ((uint8_t)(input[in_len-1]^input[0]^key_string[0]) );
|
||||||
|
out_len=in_len-1-iv_len;
|
||||||
|
if(out_len<0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"%d %d\n",in_len,out_len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(i=0,j=0,k=1;i<in_len;i++,j++,k++)
|
||||||
|
{
|
||||||
|
if(j==iv_len) j=0;
|
||||||
|
if(key_string[k]==0)k=0;
|
||||||
|
output[i]=input[iv_len+i]^input[j]^key_string[k];
|
||||||
|
|
||||||
|
}
|
||||||
|
dup_packet_recv_count++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sendto_fd_ip_port (int fd,u32_t ip,int port,char * buf, int len,int flags)
|
||||||
|
{
|
||||||
|
|
||||||
|
sockaddr_in tmp_sockaddr;
|
||||||
|
|
||||||
|
memset(&tmp_sockaddr,0,sizeof(tmp_sockaddr));
|
||||||
|
tmp_sockaddr.sin_family = AF_INET;
|
||||||
|
tmp_sockaddr.sin_addr.s_addr = ip;
|
||||||
|
tmp_sockaddr.sin_port = htons(uint16_t(port));
|
||||||
|
|
||||||
|
return sendto(fd, buf,
|
||||||
|
len , 0,
|
||||||
|
(struct sockaddr *) &tmp_sockaddr,
|
||||||
|
sizeof(tmp_sockaddr));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
int sendto_ip_port (u32_t ip,int port,char * buf, int len,int flags)
|
||||||
|
{
|
||||||
|
return sendto_fd_ip_port(local_listen_fd,ip,port,buf,len,flags);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int send_fd (int fd,char * buf, int len,int flags)
|
||||||
|
{
|
||||||
|
return send(fd,buf,len,flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int my_send(const dest_t &dest,char *data,int len)
|
||||||
|
{
|
||||||
|
if(dest.cook)
|
||||||
|
{
|
||||||
|
do_cook(data,len);
|
||||||
|
}
|
||||||
|
switch(dest.type)
|
||||||
|
{
|
||||||
|
case type_fd_ip_port:
|
||||||
|
{
|
||||||
|
return sendto_fd_ip_port(dest.inner.fd,dest.inner.fd_ip_port.ip_port.ip,dest.inner.fd_ip_port.ip_port.port,data,len,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case type_fd64_ip_port:
|
||||||
|
{
|
||||||
|
if(!fd_manager.exist(dest.inner.fd64)) return -1;
|
||||||
|
int fd=fd_manager.to_fd(dest.inner.fd64);
|
||||||
|
|
||||||
|
return sendto_fd_ip_port(fd,dest.inner.fd64_ip_port.ip_port.ip,dest.inner.fd64_ip_port.ip_port.port,data,len,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case type_fd:
|
||||||
|
{
|
||||||
|
return send_fd(dest.inner.fd,data,len,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case type_write_fd:
|
||||||
|
{
|
||||||
|
return write(dest.inner.fd,data,len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case type_fd64:
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!fd_manager.exist(dest.inner.fd64)) return -1;
|
||||||
|
int fd=fd_manager.to_fd(dest.inner.fd64);
|
||||||
|
|
||||||
|
return send_fd(fd,data,len,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
case type_fd64_ip_port_conv:
|
||||||
|
{
|
||||||
|
if(!fd_manager.exist(dest.inner.fd64)) return -1;
|
||||||
|
int fd=fd_manager.to_fd(dest.inner.fd64);
|
||||||
|
|
||||||
|
char *new_data;
|
||||||
|
int new_len;
|
||||||
|
|
||||||
|
put_conv(dest.conv,data,len,new_data,new_len);
|
||||||
|
return sendto_fd_ip_port(fd,dest.inner.fd64_ip_port.ip_port.ip,dest.inner.fd64_ip_port.ip_port.port,new_data,new_len,0);
|
||||||
|
break;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
case type_fd64_conv:
|
||||||
|
{
|
||||||
|
char *new_data;
|
||||||
|
int new_len;
|
||||||
|
put_conv(dest.conv,data,len,new_data,new_len);
|
||||||
|
|
||||||
|
if(!fd_manager.exist(dest.inner.fd64)) return -1;
|
||||||
|
int fd=fd_manager.to_fd(dest.inner.fd64);
|
||||||
|
return send_fd(fd,new_data,new_len,0);
|
||||||
|
}*/
|
||||||
|
/*
|
||||||
|
case type_fd:
|
||||||
|
{
|
||||||
|
send_fd(dest.inner.fd,data,len,0);
|
||||||
|
break;
|
||||||
|
}*/
|
||||||
|
default:
|
||||||
|
assert(0==1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this function comes from http://www.hackersdelight.org/hdcodetxt/crc.c.txt
|
||||||
|
*/
|
||||||
|
unsigned int crc32h(unsigned char *message,int len) {
|
||||||
|
assert(len>=0);
|
||||||
|
int i, crc;
|
||||||
|
unsigned int byte, c;
|
||||||
|
const unsigned int g0 = 0xEDB88320, g1 = g0>>1,
|
||||||
|
g2 = g0>>2, g3 = g0>>3, g4 = g0>>4, g5 = g0>>5,
|
||||||
|
g6 = (g0>>6)^g0, g7 = ((g0>>6)^g0)>>1;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
crc = 0xFFFFFFFF;
|
||||||
|
while (i!=len) { // Get next byte.
|
||||||
|
byte = message[i];
|
||||||
|
crc = crc ^ byte;
|
||||||
|
c = ((crc<<31>>31) & g7) ^ ((crc<<30>>31) & g6) ^
|
||||||
|
((crc<<29>>31) & g5) ^ ((crc<<28>>31) & g4) ^
|
||||||
|
((crc<<27>>31) & g3) ^ ((crc<<26>>31) & g2) ^
|
||||||
|
((crc<<25>>31) & g1) ^ ((crc<<24>>31) & g0);
|
||||||
|
crc = ((unsigned)crc >> 8) ^ c;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int put_conv0(u32_t conv,const char * input,int len_in,char *&output,int &len_out)
|
||||||
|
{
|
||||||
|
assert(len_in>=0);
|
||||||
|
static char buf[buf_len];
|
||||||
|
output=buf;
|
||||||
|
u32_t n_conv=htonl(conv);
|
||||||
|
memcpy(output,&n_conv,sizeof(n_conv));
|
||||||
|
memcpy(output+sizeof(n_conv),input,len_in);
|
||||||
|
u32_t crc32=crc32h((unsigned char *)output,len_in+sizeof(crc32));
|
||||||
|
u32_t crc32_n=htonl(crc32);
|
||||||
|
len_out=len_in+(int)(sizeof(n_conv))+(int)sizeof(crc32_n);
|
||||||
|
memcpy(output+len_in+(int)(sizeof(n_conv)),&crc32_n,sizeof(crc32_n));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int get_conv0(u32_t &conv,const char *input,int len_in,char *&output,int &len_out )
|
||||||
|
{
|
||||||
|
assert(len_in>=0);
|
||||||
|
u32_t n_conv;
|
||||||
|
memcpy(&n_conv,input,sizeof(n_conv));
|
||||||
|
conv=ntohl(n_conv);
|
||||||
|
output=(char *)input+sizeof(n_conv);
|
||||||
|
u32_t crc32_n;
|
||||||
|
len_out=len_in-(int)sizeof(n_conv)-(int)sizeof(crc32_n);
|
||||||
|
if(len_out<0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"len_out<0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(&crc32_n,input+len_in-(int)sizeof(crc32_n),sizeof(crc32_n));
|
||||||
|
u32_t crc32=ntohl(crc32_n);
|
||||||
|
if(crc32!=crc32h((unsigned char *)input,len_in-(int)sizeof(crc32_n)))
|
||||||
|
{
|
||||||
|
mylog(log_debug,"crc32 check failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int put_crc32(char * s,int &len)
|
||||||
|
{
|
||||||
|
if(disable_checksum)return 0;
|
||||||
|
assert(len>=0);
|
||||||
|
//if(len<0) return -1;
|
||||||
|
u32_t crc32=crc32h((unsigned char *)s,len);
|
||||||
|
write_u32(s+len,crc32);
|
||||||
|
len+=sizeof(u32_t);
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_cook(char * data,int &len)
|
||||||
|
{
|
||||||
|
put_crc32(data,len);
|
||||||
|
if(!disable_obscure)do_obscure(data,len);
|
||||||
|
if(!disable_xor)encrypt_0(data,len,key_string);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int de_cook(char * s,int &len)
|
||||||
|
{
|
||||||
|
if(!disable_xor)decrypt_0(s,len,key_string);
|
||||||
|
if(!disable_obscure)
|
||||||
|
{
|
||||||
|
int ret=de_obscure(s,len);
|
||||||
|
if(ret!=0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"de_obscure fail\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int ret=rm_crc32(s,len);
|
||||||
|
if(ret!=0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"rm_crc32 fail\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int rm_crc32(char * s,int &len)
|
||||||
|
{
|
||||||
|
if(disable_checksum)return 0;
|
||||||
|
assert(len>=0);
|
||||||
|
len-=sizeof(u32_t);
|
||||||
|
if(len<0) return -1;
|
||||||
|
u32_t crc32_in=read_u32(s+len);
|
||||||
|
u32_t crc32=crc32h((unsigned char *)s,len);
|
||||||
|
if(crc32!=crc32_in) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
int do_obs()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
int de_obs()*/
|
||||||
|
int put_conv(u32_t conv,const char * input,int len_in,char *&output,int &len_out)
|
||||||
|
{
|
||||||
|
static char buf[buf_len];
|
||||||
|
output=buf;
|
||||||
|
u32_t n_conv=htonl(conv);
|
||||||
|
memcpy(output,&n_conv,sizeof(n_conv));
|
||||||
|
memcpy(output+sizeof(n_conv),input,len_in);
|
||||||
|
len_out=len_in+(int)(sizeof(n_conv));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int get_conv(u32_t &conv,const char *input,int len_in,char *&output,int &len_out )
|
||||||
|
{
|
||||||
|
u32_t n_conv;
|
||||||
|
memcpy(&n_conv,input,sizeof(n_conv));
|
||||||
|
conv=ntohl(n_conv);
|
||||||
|
output=(char *)input+sizeof(n_conv);
|
||||||
|
len_out=len_in-(int)sizeof(n_conv);
|
||||||
|
if(len_out<0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"len_out<0\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
47
packet.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* packet.h
|
||||||
|
*
|
||||||
|
* Created on: Sep 15, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PACKET_H_
|
||||||
|
#define PACKET_H_
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "fd_manager.h"
|
||||||
|
|
||||||
|
extern int iv_min;
|
||||||
|
extern int iv_max;//< 256;
|
||||||
|
|
||||||
|
extern u64_t packet_send_count;
|
||||||
|
extern u64_t dup_packet_send_count;
|
||||||
|
extern u64_t packet_recv_count;
|
||||||
|
extern u64_t dup_packet_recv_count;
|
||||||
|
extern char key_string[1000];
|
||||||
|
extern int disable_replay_filter;
|
||||||
|
extern int random_drop;
|
||||||
|
extern int disable_obscure;
|
||||||
|
extern int disable_xor;
|
||||||
|
|
||||||
|
|
||||||
|
int my_send(const dest_t &dest,char *data,int len);
|
||||||
|
|
||||||
|
void encrypt_0(char * input,int &len,char *key);
|
||||||
|
void decrypt_0(char * input,int &len,char *key);
|
||||||
|
int add_seq(char * data,int &data_len );
|
||||||
|
int remove_seq(char * data,int &data_len);
|
||||||
|
int do_obscure(const char * input, int in_len,char *output,int &out_len);
|
||||||
|
int de_obscure(const char * input, int in_len,char *output,int &out_len);
|
||||||
|
|
||||||
|
//int sendto_fd_u64 (int fd,u64_t u64,char * buf, int len,int flags);
|
||||||
|
int sendto_ip_port (u32_t ip,int port,char * buf, int len,int flags);
|
||||||
|
int send_fd (int fd,char * buf, int len,int flags);
|
||||||
|
|
||||||
|
int put_conv(u32_t conv,const char * input,int len_in,char *&output,int &len_out);
|
||||||
|
int get_conv(u32_t &conv,const char *input,int len_in,char *&output,int &len_out );
|
||||||
|
int put_crc32(char * s,int &len);
|
||||||
|
int rm_crc32(char * s,int &len);
|
||||||
|
int do_cook(char * data,int &len);
|
||||||
|
int de_cook(char * s,int &len);
|
||||||
|
#endif /* PACKET_H_ */
|
705
tunnel.cpp
Normal file
@@ -0,0 +1,705 @@
|
|||||||
|
/*
|
||||||
|
* tunnel.cpp
|
||||||
|
*
|
||||||
|
* Created on: Oct 26, 2017
|
||||||
|
* Author: root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tunnel.h"
|
||||||
|
|
||||||
|
|
||||||
|
int tunnel_client_event_loop()
|
||||||
|
{
|
||||||
|
//char buf[buf_len];
|
||||||
|
int i, j, k;int ret;
|
||||||
|
int yes = 1;
|
||||||
|
int epoll_fd;
|
||||||
|
int remote_fd;
|
||||||
|
fd64_t remote_fd64;
|
||||||
|
|
||||||
|
conn_info_t *conn_info_p=new conn_info_t;
|
||||||
|
conn_info_t &conn_info=*conn_info_p; //huge size of conn_info,do not allocate on stack
|
||||||
|
//conn_info.conv_manager.reserve();
|
||||||
|
//conn_info.fec_encode_manager.re_init(fec_data_num,fec_redundant_num,fec_mtu,fec_pending_num,fec_pending_time,fec_type);
|
||||||
|
|
||||||
|
|
||||||
|
int local_listen_fd;
|
||||||
|
//fd64_t local_listen_fd64;
|
||||||
|
new_listen_socket(local_listen_fd,local_ip_uint32,local_port);
|
||||||
|
//local_listen_fd64=fd_manager.create(local_listen_fd);
|
||||||
|
|
||||||
|
epoll_fd = epoll_create1(0);
|
||||||
|
assert(epoll_fd>0);
|
||||||
|
|
||||||
|
const int max_events = 4096;
|
||||||
|
struct epoll_event ev, events[max_events];
|
||||||
|
if (epoll_fd < 0) {
|
||||||
|
mylog(log_fatal,"epoll return %d\n", epoll_fd);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = local_listen_fd;
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, local_listen_fd, &ev);
|
||||||
|
if (ret!=0) {
|
||||||
|
mylog(log_fatal,"add udp_listen_fd error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(new_connected_socket(remote_fd,remote_ip_uint32,remote_port)==0);
|
||||||
|
remote_fd64=fd_manager.create(remote_fd);
|
||||||
|
|
||||||
|
mylog(log_debug,"remote_fd64=%llu\n",remote_fd64);
|
||||||
|
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = remote_fd64;
|
||||||
|
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, remote_fd, &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add raw_fd error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = delay_manager.get_timer_fd();
|
||||||
|
|
||||||
|
mylog(log_debug,"delay_manager.get_timer_fd()=%d\n",delay_manager.get_timer_fd());
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, delay_manager.get_timer_fd(), &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64_t tmp_fd64=conn_info.fec_encode_manager.get_timer_fd64();
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = tmp_fd64;
|
||||||
|
|
||||||
|
mylog(log_debug,"conn_info.fec_encode_manager.get_timer_fd64()=%llu\n",conn_info.fec_encode_manager.get_timer_fd64());
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_manager.to_fd(tmp_fd64), &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add fec_encode_manager.get_timer_fd64() error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//my_timer_t timer;
|
||||||
|
conn_info.timer.add_fd_to_epoll(epoll_fd);
|
||||||
|
conn_info.timer.set_timer_repeat_us(timer_interval*1000);
|
||||||
|
|
||||||
|
mylog(log_debug,"conn_info.timer.get_timer_fd()=%d\n",conn_info.timer.get_timer_fd());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int fifo_fd=-1;
|
||||||
|
|
||||||
|
if(fifo_file[0]!=0)
|
||||||
|
{
|
||||||
|
fifo_fd=create_fifo(fifo_file);
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = fifo_fd;
|
||||||
|
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add fifo_fd to epoll error %s\n",strerror(errno));
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
mylog(log_info,"fifo_file=%s\n",fifo_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)////////////////////////
|
||||||
|
{
|
||||||
|
if(about_to_exit) myexit(0);
|
||||||
|
|
||||||
|
int nfds = epoll_wait(epoll_fd, events, max_events, 180 * 1000);
|
||||||
|
if (nfds < 0) { //allow zero
|
||||||
|
if(errno==EINTR )
|
||||||
|
{
|
||||||
|
mylog(log_info,"epoll interrupted by signal continue\n");
|
||||||
|
//myexit(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"epoll_wait return %d,%s\n", nfds,strerror(errno));
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int idx;
|
||||||
|
for (idx = 0; idx < nfds; ++idx) {
|
||||||
|
if(events[idx].data.u64==(u64_t)conn_info.timer.get_timer_fd())
|
||||||
|
{
|
||||||
|
uint64_t value;
|
||||||
|
read(conn_info.timer.get_timer_fd(), &value, 8);
|
||||||
|
conn_info.conv_manager.clear_inactive();
|
||||||
|
mylog(log_trace,"events[idx].data.u64==(u64_t)conn_info.timer.get_timer_fd()\n");
|
||||||
|
|
||||||
|
conn_info.stat.report_as_client();
|
||||||
|
|
||||||
|
if(debug_force_flush_fec)
|
||||||
|
{
|
||||||
|
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
|
||||||
|
dest_t dest;
|
||||||
|
dest.type=type_fd64;
|
||||||
|
dest.inner.fd64=remote_fd64;
|
||||||
|
dest.cook=1;
|
||||||
|
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
delay_send(out_delay[i],dest,out_arr[i],out_len[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (events[idx].data.u64 == (u64_t)fifo_fd)
|
||||||
|
{
|
||||||
|
char buf[buf_len];
|
||||||
|
int len=read (fifo_fd, buf, sizeof (buf));
|
||||||
|
if(len<0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"fifo read failed len=%d,errno=%s\n",len,strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf[len]=0;
|
||||||
|
handle_command(buf);
|
||||||
|
}
|
||||||
|
else if (events[idx].data.u64 == (u64_t)local_listen_fd||events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64())
|
||||||
|
{
|
||||||
|
char data[buf_len];
|
||||||
|
int data_len;
|
||||||
|
ip_port_t ip_port;
|
||||||
|
u32_t conv;
|
||||||
|
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
|
||||||
|
dest_t dest;
|
||||||
|
dest.type=type_fd64;
|
||||||
|
dest.inner.fd64=remote_fd64;
|
||||||
|
dest.cook=1;
|
||||||
|
|
||||||
|
if(events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64())
|
||||||
|
{
|
||||||
|
fd64_t fd64=events[idx].data.u64;
|
||||||
|
mylog(log_trace,"events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64()\n");
|
||||||
|
|
||||||
|
//mylog(log_info,"timer!!!\n");
|
||||||
|
uint64_t value;
|
||||||
|
if(!fd_manager.exist(fd64)) //fd64 has been closed
|
||||||
|
{
|
||||||
|
mylog(log_trace,"!fd_manager.exist(fd64)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if((ret=read(fd_manager.to_fd(fd64), &value, 8))!=8)
|
||||||
|
{
|
||||||
|
mylog(log_trace,"(ret=read(fd_manager.to_fd(fd64), &value, 8))!=8,ret=%d\n",ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(value==0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"value==0\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(value==1);
|
||||||
|
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
|
||||||
|
//from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
|
||||||
|
}
|
||||||
|
else//events[idx].data.u64 == (u64_t)local_listen_fd
|
||||||
|
{
|
||||||
|
mylog(log_trace,"events[idx].data.u64 == (u64_t)local_listen_fd\n");
|
||||||
|
struct sockaddr_in udp_new_addr_in={0};
|
||||||
|
socklen_t udp_new_addr_len = sizeof(sockaddr_in);
|
||||||
|
if ((data_len = recvfrom(local_listen_fd, data, max_data_len, 0,
|
||||||
|
(struct sockaddr *) &udp_new_addr_in, &udp_new_addr_len)) == -1) {
|
||||||
|
mylog(log_error,"recv_from error,this shouldnt happen,err=%s,but we can try to continue\n",strerror(errno));
|
||||||
|
continue;
|
||||||
|
//mylog(log_error,"recv_from error,this shouldnt happen at client\n");
|
||||||
|
//myexit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!disable_mtu_warn&&data_len>=mtu_warn)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
|
||||||
|
}
|
||||||
|
mylog(log_trace,"Received packet from %s:%d,len: %d\n", inet_ntoa(udp_new_addr_in.sin_addr),
|
||||||
|
ntohs(udp_new_addr_in.sin_port),data_len);
|
||||||
|
|
||||||
|
ip_port.ip=udp_new_addr_in.sin_addr.s_addr;
|
||||||
|
ip_port.port=ntohs(udp_new_addr_in.sin_port);
|
||||||
|
|
||||||
|
u64_t u64=ip_port.to_u64();
|
||||||
|
|
||||||
|
if(!conn_info.conv_manager.is_u64_used(u64))
|
||||||
|
{
|
||||||
|
if(conn_info.conv_manager.get_size() >=max_conv_num)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"ignored new udp connect bc max_conv_num exceed\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
conv=conn_info.conv_manager.get_new_conv();
|
||||||
|
conn_info.conv_manager.insert_conv(conv,u64);
|
||||||
|
mylog(log_info,"new packet from %s:%d,conv_id=%x\n",inet_ntoa(udp_new_addr_in.sin_addr),ntohs(udp_new_addr_in.sin_port),conv);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
conv=conn_info.conv_manager.find_conv_by_u64(u64);
|
||||||
|
mylog(log_trace,"conv=%d\n",conv);
|
||||||
|
}
|
||||||
|
conn_info.conv_manager.update_active_time(conv);
|
||||||
|
char * new_data;
|
||||||
|
int new_len;
|
||||||
|
put_conv(conv,data,data_len,new_data,new_len);
|
||||||
|
|
||||||
|
|
||||||
|
mylog(log_trace,"data_len=%d new_len=%d\n",data_len,new_len);
|
||||||
|
//dest.conv=conv;
|
||||||
|
from_normal_to_fec(conn_info,new_data,new_len,out_n,out_arr,out_len,out_delay);
|
||||||
|
|
||||||
|
}
|
||||||
|
mylog(log_trace,"out_n=%d\n",out_n);
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
delay_send(out_delay[i],dest,out_arr[i],out_len[i]);
|
||||||
|
}
|
||||||
|
//my_send(dest,data,data_len);
|
||||||
|
}
|
||||||
|
else if (events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()) {
|
||||||
|
uint64_t value;
|
||||||
|
read(delay_manager.get_timer_fd(), &value, 8);
|
||||||
|
mylog(log_trace,"events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()\n");
|
||||||
|
//printf("<timerfd_triggered, %d>",delay_mp.size());
|
||||||
|
//fflush(stdout);
|
||||||
|
}
|
||||||
|
else if(events[idx].data.u64>u32_t(-1) )
|
||||||
|
{
|
||||||
|
char data[buf_len];
|
||||||
|
if(!fd_manager.exist(events[idx].data.u64)) //fd64 has been closed
|
||||||
|
{
|
||||||
|
mylog(log_trace,"!fd_manager.exist(events[idx].data.u64)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(events[idx].data.u64==remote_fd64);
|
||||||
|
int fd=fd_manager.to_fd(remote_fd64);
|
||||||
|
int data_len =recv(fd,data,max_data_len,0);
|
||||||
|
mylog(log_trace, "received data from udp fd %d, len=%d\n", remote_fd,data_len);
|
||||||
|
if(data_len<0)
|
||||||
|
{
|
||||||
|
if(errno==ECONNREFUSED)
|
||||||
|
{
|
||||||
|
//conn_manager.clear_list.push_back(udp_fd);
|
||||||
|
mylog(log_debug, "recv failed %d ,udp_fd%d,errno:%s\n", data_len,remote_fd,strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
mylog(log_warn, "recv failed %d ,udp_fd%d,errno:%s\n", data_len,remote_fd,strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!disable_mtu_warn&&data_len>mtu_warn)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"huge packet,data len=%d (>%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(de_cook(data,data_len)!=0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"de_cook error");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
|
||||||
|
from_fec_to_normal(conn_info,data,data_len,out_n,out_arr,out_len,out_delay);
|
||||||
|
|
||||||
|
mylog(log_trace,"out_n=%d\n",out_n);
|
||||||
|
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
u32_t conv;
|
||||||
|
char *new_data;
|
||||||
|
int new_len;
|
||||||
|
if(get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!conn_info.conv_manager.is_conv_used(conv))
|
||||||
|
{
|
||||||
|
mylog(log_trace,"!conn_info.conv_manager.is_conv_used(conv)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_info.conv_manager.update_active_time(conv);
|
||||||
|
|
||||||
|
u64_t u64=conn_info.conv_manager.find_u64_by_conv(conv);
|
||||||
|
dest_t dest;
|
||||||
|
dest.inner.fd_ip_port.fd=local_listen_fd;
|
||||||
|
dest.inner.fd_ip_port.ip_port.from_u64(u64);
|
||||||
|
dest.type=type_fd_ip_port;
|
||||||
|
//dest.conv=conv;
|
||||||
|
|
||||||
|
delay_send(out_delay[i],dest,new_data,new_len);
|
||||||
|
}
|
||||||
|
//mylog(log_trace,"[%s] send packet\n",dest.inner.ip_port.to_s());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"unknown fd,this should never happen\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay_manager.check();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tunnel_server_event_loop()
|
||||||
|
{
|
||||||
|
//char buf[buf_len];
|
||||||
|
int i, j, k;int ret;
|
||||||
|
int yes = 1;
|
||||||
|
int epoll_fd;
|
||||||
|
int remote_fd;
|
||||||
|
|
||||||
|
// conn_info_t conn_info;
|
||||||
|
int local_listen_fd;
|
||||||
|
// fd64_t local_listen_fd64;
|
||||||
|
new_listen_socket(local_listen_fd,local_ip_uint32,local_port);
|
||||||
|
// local_listen_fd64=fd_manager.create(local_listen_fd);
|
||||||
|
|
||||||
|
epoll_fd = epoll_create1(0);
|
||||||
|
assert(epoll_fd>0);
|
||||||
|
|
||||||
|
const int max_events = 4096;
|
||||||
|
struct epoll_event ev, events[max_events];
|
||||||
|
if (epoll_fd < 0) {
|
||||||
|
mylog(log_fatal,"epoll return %d\n", epoll_fd);
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = local_listen_fd;
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, local_listen_fd, &ev);
|
||||||
|
if (ret!=0) {
|
||||||
|
mylog(log_fatal,"add udp_listen_fd error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = delay_manager.get_timer_fd();
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, delay_manager.get_timer_fd(), &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mylog(log_debug," delay_manager.get_timer_fd() =%d\n", delay_manager.get_timer_fd());
|
||||||
|
|
||||||
|
mylog(log_info,"now listening at %s:%d\n",my_ntoa(local_ip_uint32),local_port);
|
||||||
|
|
||||||
|
my_timer_t timer;
|
||||||
|
timer.add_fd_to_epoll(epoll_fd);
|
||||||
|
timer.set_timer_repeat_us(timer_interval*1000);
|
||||||
|
|
||||||
|
mylog(log_debug," timer.get_timer_fd() =%d\n",timer.get_timer_fd());
|
||||||
|
|
||||||
|
int fifo_fd=-1;
|
||||||
|
|
||||||
|
if(fifo_file[0]!=0)
|
||||||
|
{
|
||||||
|
fifo_fd=create_fifo(fifo_file);
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = fifo_fd;
|
||||||
|
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
|
||||||
|
if (ret!= 0) {
|
||||||
|
mylog(log_fatal,"add fifo_fd to epoll error %s\n",strerror(errno));
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
mylog(log_info,"fifo_file=%s\n",fifo_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)////////////////////////
|
||||||
|
{
|
||||||
|
|
||||||
|
if(about_to_exit) myexit(0);
|
||||||
|
|
||||||
|
int nfds = epoll_wait(epoll_fd, events, max_events, 180 * 1000);
|
||||||
|
if (nfds < 0) { //allow zero
|
||||||
|
if(errno==EINTR )
|
||||||
|
{
|
||||||
|
mylog(log_info,"epoll interrupted by signal,continue\n");
|
||||||
|
//myexit(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"epoll_wait return %d,%s\n", nfds,strerror(errno));
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int idx;
|
||||||
|
for (idx = 0; idx < nfds; ++idx)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if ((events[idx].data.u64 ) == (u64_t)timer_fd)
|
||||||
|
{
|
||||||
|
conn_manager.clear_inactive();
|
||||||
|
u64_t dummy;
|
||||||
|
read(timer_fd, &dummy, 8);
|
||||||
|
//current_time_rough=get_current_time();
|
||||||
|
}
|
||||||
|
else */
|
||||||
|
if(events[idx].data.u64==(u64_t)timer.get_timer_fd())
|
||||||
|
{
|
||||||
|
uint64_t value;
|
||||||
|
read(timer.get_timer_fd(), &value, 8);
|
||||||
|
conn_manager.clear_inactive();
|
||||||
|
mylog(log_trace,"events[idx].data.u64==(u64_t)timer.get_timer_fd()\n");
|
||||||
|
//conn_info.conv_manager.clear_inactive();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (events[idx].data.u64 == (u64_t)fifo_fd)
|
||||||
|
{
|
||||||
|
char buf[buf_len];
|
||||||
|
int len=read (fifo_fd, buf, sizeof (buf));
|
||||||
|
if(len<0)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"fifo read failed len=%d,errno=%s\n",len,strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf[len]=0;
|
||||||
|
handle_command(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (events[idx].data.u64 == (u64_t)local_listen_fd)
|
||||||
|
{
|
||||||
|
|
||||||
|
mylog(log_trace,"events[idx].data.u64 == (u64_t)local_listen_fd\n");
|
||||||
|
//int recv_len;
|
||||||
|
char data[buf_len];
|
||||||
|
int data_len;
|
||||||
|
struct sockaddr_in udp_new_addr_in={0};
|
||||||
|
socklen_t udp_new_addr_len = sizeof(sockaddr_in);
|
||||||
|
if ((data_len = recvfrom(local_listen_fd, data, max_data_len, 0,
|
||||||
|
(struct sockaddr *) &udp_new_addr_in, &udp_new_addr_len)) == -1) {
|
||||||
|
mylog(log_error,"recv_from error,this shouldnt happen,err=%s,but we can try to continue\n",strerror(errno));
|
||||||
|
continue;
|
||||||
|
//myexit(1);
|
||||||
|
};
|
||||||
|
mylog(log_trace,"Received packet from %s:%d,len: %d\n", inet_ntoa(udp_new_addr_in.sin_addr),
|
||||||
|
ntohs(udp_new_addr_in.sin_port),data_len);
|
||||||
|
|
||||||
|
if(!disable_mtu_warn&&data_len>=mtu_warn)///////////////////////delete this for type 0 in furture
|
||||||
|
{
|
||||||
|
mylog(log_warn,"huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(de_cook(data,data_len)!=0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"de_cook error");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ip_port_t ip_port;
|
||||||
|
ip_port.ip=udp_new_addr_in.sin_addr.s_addr;
|
||||||
|
ip_port.port=ntohs(udp_new_addr_in.sin_port);
|
||||||
|
mylog(log_trace,"ip_port= %s\n",ip_port.to_s());
|
||||||
|
if(!conn_manager.exist(ip_port))
|
||||||
|
{
|
||||||
|
if(conn_manager.mp.size() >=max_conn_num)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"new connection %s ignored bc max_conn_num exceed\n",ip_port.to_s());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_manager.insert(ip_port);
|
||||||
|
conn_info_t &conn_info=conn_manager.find(ip_port);
|
||||||
|
//conn_info.fec_encode_manager.re_init(fec_data_num,fec_redundant_num,fec_mtu,fec_pending_num,fec_pending_time,fec_type);
|
||||||
|
//conn_info.conv_manager.reserve(); //already reserved in constructor
|
||||||
|
|
||||||
|
u64_t fec_fd64=conn_info.fec_encode_manager.get_timer_fd64();
|
||||||
|
mylog(log_debug,"fec_fd64=%llu\n",fec_fd64);
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = fec_fd64;
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_manager.to_fd(fec_fd64), &ev);
|
||||||
|
|
||||||
|
fd_manager.get_info(fec_fd64).ip_port=ip_port;
|
||||||
|
|
||||||
|
conn_info.timer.add_fd64_to_epoll(epoll_fd);
|
||||||
|
conn_info.timer.set_timer_repeat_us(timer_interval*1000);
|
||||||
|
|
||||||
|
mylog(log_debug,"conn_info.timer.get_timer_fd64()=%llu\n",conn_info.timer.get_timer_fd64());
|
||||||
|
|
||||||
|
u64_t timer_fd64=conn_info.timer.get_timer_fd64();
|
||||||
|
fd_manager.get_info(timer_fd64).ip_port=ip_port;
|
||||||
|
|
||||||
|
mylog(log_info,"new connection from %s\n",ip_port.to_s());
|
||||||
|
|
||||||
|
}
|
||||||
|
conn_info_t &conn_info=conn_manager.find(ip_port);
|
||||||
|
|
||||||
|
conn_info.update_active_time();
|
||||||
|
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
|
||||||
|
from_fec_to_normal(conn_info,data,data_len,out_n,out_arr,out_len,out_delay);
|
||||||
|
|
||||||
|
mylog(log_trace,"out_n= %d\n",out_n);
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
u32_t conv;
|
||||||
|
char *new_data;
|
||||||
|
int new_len;
|
||||||
|
if(get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"get_conv failed");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
id_t tmp_conv_id;
|
||||||
|
memcpy(&tmp_conv_id,&data_[0],sizeof(tmp_conv_id));
|
||||||
|
tmp_conv_id=ntohl(tmp_conv_id);*/
|
||||||
|
|
||||||
|
if (!conn_info.conv_manager.is_conv_used(conv))
|
||||||
|
{
|
||||||
|
if(conn_info.conv_manager.get_size() >=max_conv_num)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"ignored new udp connect bc max_conv_num exceed\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_udp_fd;
|
||||||
|
ret=new_connected_socket(new_udp_fd,remote_ip_uint32,remote_port);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
mylog(log_warn, "[%s]new_connected_socket failed\n",ip_port.to_s());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd64_t fd64 = fd_manager.create(new_udp_fd);
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.u64 = fd64;
|
||||||
|
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_udp_fd, &ev);
|
||||||
|
|
||||||
|
conn_info.conv_manager.insert_conv(conv, fd64);
|
||||||
|
fd_manager.get_info(fd64).ip_port=ip_port;
|
||||||
|
|
||||||
|
|
||||||
|
mylog(log_info,"[%s]new conv %x,fd %d created,fd64=%llu\n",ip_port.to_s(),conv,new_udp_fd,fd64);
|
||||||
|
//assert(!conn_manager.exist_fd64(fd64));
|
||||||
|
|
||||||
|
//conn_manager.insert_fd64(fd64,ip_port);
|
||||||
|
}
|
||||||
|
conn_info.conv_manager.update_active_time(conv);
|
||||||
|
fd64_t fd64= conn_info.conv_manager.find_u64_by_conv(conv);
|
||||||
|
//int fd=fd_manager.fd64_to_fd(fd64);
|
||||||
|
dest_t dest;
|
||||||
|
dest.type=type_fd64;
|
||||||
|
dest.inner.fd64=fd64;
|
||||||
|
//dest.conv=conv;
|
||||||
|
delay_send(out_delay[i],dest,new_data,new_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()) {
|
||||||
|
uint64_t value;
|
||||||
|
read(delay_manager.get_timer_fd(), &value, 8);
|
||||||
|
mylog(log_trace,"events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()\n");
|
||||||
|
}
|
||||||
|
else if (events[idx].data.u64 >u32_t(-1))
|
||||||
|
{
|
||||||
|
char data[buf_len];
|
||||||
|
int data_len;
|
||||||
|
u32_t conv;
|
||||||
|
fd64_t fd64=events[idx].data.u64;
|
||||||
|
mylog(log_trace,"events[idx].data.u64 >u32_t(-1),%llu\n",(u64_t)events[idx].data.u64);
|
||||||
|
if(!fd_manager.exist(fd64)) //fd64 has been closed
|
||||||
|
{
|
||||||
|
mylog(log_trace,"!fd_manager.exist(fd64)\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(fd_manager.exist_info(fd64));
|
||||||
|
ip_port_t ip_port=fd_manager.get_info(fd64).ip_port;
|
||||||
|
assert(conn_manager.exist(ip_port));
|
||||||
|
|
||||||
|
conn_info_t &conn_info=conn_manager.find(ip_port);
|
||||||
|
//conn_info.update_active_time(); //cant put it here
|
||||||
|
|
||||||
|
int out_n=-2;char **out_arr;int *out_len;my_time_t *out_delay;
|
||||||
|
|
||||||
|
dest_t dest;
|
||||||
|
dest.inner.fd_ip_port.fd=local_listen_fd;
|
||||||
|
dest.inner.fd_ip_port.ip_port=ip_port;
|
||||||
|
dest.type=type_fd_ip_port;
|
||||||
|
dest.cook=1;
|
||||||
|
|
||||||
|
if(fd64==conn_info.fec_encode_manager.get_timer_fd64())
|
||||||
|
{
|
||||||
|
//mylog(log_infol,"timer!!!\n");
|
||||||
|
uint64_t value;
|
||||||
|
if((ret=read(fd_manager.to_fd(fd64), &value, 8))!=8)
|
||||||
|
{
|
||||||
|
mylog(log_trace,"fd_manager.to_fd(fd64), &value, 8)!=8 ,%d\n",ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(value==0)
|
||||||
|
{
|
||||||
|
mylog(log_trace,"value==0\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(value==1);
|
||||||
|
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
|
||||||
|
}
|
||||||
|
else if(fd64==conn_info.timer.get_timer_fd64())
|
||||||
|
{
|
||||||
|
uint64_t value;
|
||||||
|
read(conn_info.timer.get_timer_fd(), &value, 8);
|
||||||
|
conn_info.conv_manager.clear_inactive();
|
||||||
|
if(debug_force_flush_fec)
|
||||||
|
{
|
||||||
|
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_info.stat.report_as_server(ip_port);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(conn_info.conv_manager.is_u64_used(fd64));
|
||||||
|
|
||||||
|
conv=conn_info.conv_manager.find_conv_by_u64(fd64);
|
||||||
|
conn_info.conv_manager.update_active_time(conv);
|
||||||
|
conn_info.update_active_time();
|
||||||
|
|
||||||
|
int fd=fd_manager.to_fd(fd64);
|
||||||
|
data_len=recv(fd,data,max_data_len,0);
|
||||||
|
|
||||||
|
mylog(log_trace,"received a packet from udp_fd,len:%d,conv=%d\n",data_len,conv);
|
||||||
|
|
||||||
|
if(data_len<0)
|
||||||
|
{
|
||||||
|
mylog(log_debug,"udp fd,recv_len<0 continue,%s\n",strerror(errno));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!disable_mtu_warn&&data_len>=mtu_warn)
|
||||||
|
{
|
||||||
|
mylog(log_warn,"huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
char * new_data;
|
||||||
|
int new_len;
|
||||||
|
put_conv(conv,data,data_len,new_data,new_len);
|
||||||
|
|
||||||
|
from_normal_to_fec(conn_info,new_data,new_len,out_n,out_arr,out_len,out_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
mylog(log_trace,"out_n=%d\n",out_n);
|
||||||
|
for(int i=0;i<out_n;i++)
|
||||||
|
{
|
||||||
|
delay_send(out_delay[i],dest,out_arr[i],out_len[i]);
|
||||||
|
}
|
||||||
|
//mylog(log_trace,"[%s] send packet\n",ip_port.to_s());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"unknown fd,this should never happen\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay_manager.check();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|