Compare commits
304 Commits
20170816.0
...
v2@2017110
Author | SHA1 | Date | |
---|---|---|---|
|
2cc560088b | ||
|
b83b21f2eb | ||
|
f526046846 | ||
|
1be5fb5fc1 | ||
|
27874aa413 | ||
|
fde2329528 | ||
|
30b78c1339 | ||
|
17a3074b34 | ||
|
cce7d18163 | ||
|
90029e1ea7 | ||
|
c26a4708ee | ||
|
fdbf4ab205 | ||
|
7ee72ee84c | ||
|
cac5f8271c | ||
|
219e8e2592 | ||
|
02b4d59500 | ||
|
894bf7cd0d | ||
|
a8cab43139 | ||
|
369a9d8e1b | ||
|
0f7894f42f | ||
|
d7daae25bf | ||
|
d7271b74b4 | ||
|
1eeef14470 | ||
|
9d30caffc9 | ||
|
e50e383ede | ||
|
c73a197971 | ||
|
ba44617124 | ||
|
19e718f0a3 | ||
|
bd323be9c4 | ||
|
35a85e38ae | ||
|
4871983b8f | ||
|
fec3c39cad | ||
|
4d55348a4d | ||
|
36eef02d8a | ||
|
3fd48dd20e | ||
|
766aa274d9 | ||
|
d961537682 | ||
|
4dcfe87bd3 | ||
|
2aa0ece465 | ||
|
05fd84b675 | ||
|
1e00ff7c87 | ||
|
c76818c6a8 | ||
|
e3e2fe6d36 | ||
|
7cd87e7b59 | ||
|
c3aae9579b | ||
|
d9f80781a2 | ||
|
203604e1b5 | ||
|
5ac6e30c8e | ||
|
6728d5a561 | ||
|
f9eaae172f | ||
|
a0eeedb12a | ||
|
324d34c87a | ||
|
1fe64b5689 | ||
|
d5e900e91c | ||
|
8e00273db4 | ||
|
73d68a2baf | ||
|
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 |
209
README.md
@@ -1,71 +1,98 @@
|
||||
# UDPspeeder
|
||||

|
||||
UDP双边加速工具,降低丢包率,配合vpn可以加速任何协议,尤其适用于加速游戏和网页打开速度;同时也是一个UDP连接的调试和统计工具。
|
||||
Network Speed-Up Tool. Boost your Connection on a High Lantency High Packet-Loss Link by using Forward Error Correction.
|
||||
|
||||
这个是我自己稳定用了一个月的项目,用来加速美服的Brawl Stars和亚服的Mobile Legend,效果不错。加速前卡得几乎没法玩,加速后就没怎么卡过了。
|
||||
#### 效果
|
||||

|
||||
#### 原理简介
|
||||
目前原理是多倍发包。以后会做各种优化,比如:对高频率的短包先合并再冗余;FEC(Forward Error Correction),在包速低的时候多倍发包,包速高时用FEC。
|
||||
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。
|
||||
|
||||
跟net-speeder比,优势在于client和server会把收到的多余包自动去掉,这个过程对上层透明,没有兼容性问题。而且发出的冗余数据包会做长度和内容的随机化,抓包是看不出发了冗余数据的,所以不用担心vps被封的问题。
|
||||

|
||||
|
||||
每个冗余数据包都是间隔数毫秒(可配置)以后延迟发出的,可以避开中间路由器因为瞬时buffer长度过长而连续丢掉所有副本。
|
||||
or
|
||||
|
||||
模拟一定的延迟抖动,这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。
|
||||

|
||||
|
||||
#### 适用场景
|
||||
绝大部分流量不高的情况。程序本身加速udp,但是配合openvpn可以加速任何流量。网络状况不好时,游戏卡得没法玩,或者网页卡得没法打开,使用起来效果最好。对于解决语音通话的断断续续效果也不错。不适合大流量的场景,比如BT下载和在线看视频。 无论从自己使用效果的角度,还是从国际出口带宽占用的角度,都建议不要在大流量环境使用。
|
||||
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.
|
||||
|
||||
#### 其他功能
|
||||
输出UDP收发情况报告,可以看出丢包率。
|
||||
[简体中文](/doc/README.zh-cn.md)(内容更丰富)
|
||||
|
||||
模拟丢包,模拟延迟,模拟jitter。便于通过实验找出应用卡顿的原因。
|
||||
###### Note
|
||||
You can use udp2raw with UDPspeeder together to get better speed on some ISP with UDP QoS(UDP throttling).
|
||||
|
||||
重复包过滤功能可以关掉,模拟网络本身有重复包的情况。用来测试应用对重复报的支持情况。
|
||||
|
||||
client支持多个udp连接,server也支持多个client
|
||||
|
||||
目前有amd64,x86,ar71xx,树莓派armv7的binary
|
||||
|
||||
如果你需要绕过UDP屏蔽/QoS,或者需要连接复用/连接保持功能,或者是加密。解决方案在另一个repo(可以跟UDPspeeder一起使用):
|
||||
udp2raw's repo:
|
||||
|
||||
https://github.com/wangyu-/udp2raw-tunnel
|
||||
|
||||
# 简明操作说明
|
||||
You can also try tinyFecVPN, a lightweight high-performance VPN with build-in FEC support:
|
||||
|
||||
### 环境要求
|
||||
Linux主机,可以使是openwrt路由器,也可以是树莓派。在windows和mac上配合虚拟机可以稳定使用(speeder跑在Linux里,其他应用照常跑在window里,桥接模式测试可用)。
|
||||
tinyFecVPN's repo:
|
||||
|
||||
### 安装
|
||||
下载编译好的二进制文件,解压到本地和服务器的任意目录。
|
||||
https://github.com/wangyu-/tinyFecVPN
|
||||
|
||||
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"
|
||||
# Efficacy
|
||||
tested on a link with 100ms latency and 10% packet loss at both direction
|
||||
|
||||
在server端运行:
|
||||
./speeder_amd64 -l0.0.0.0:8855 -r127.0.0.1:7777 -s -d2 -k "passwd"
|
||||
### Ping Packet Loss
|
||||

|
||||
|
||||
### SCP Copy Speed
|
||||

|
||||
|
||||
# Supported Platforms
|
||||
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/20170918.0/lede-17.01.2-x86_virtual_machine_image_with_udp2raw_pre_installed.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 is boosted by UDPspeeder.
|
||||
|
||||
###### 注:
|
||||
##### Note
|
||||
|
||||
-d2 表示除了本来的包以外,额外再发2个冗余包。可调。
|
||||
`-f20:10` means sending 10 redundant packets for every 20 original packets.
|
||||
|
||||
-k 指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
||||
`-k` enables simple XOR encryption
|
||||
|
||||
# 进阶操作说明
|
||||
To run stably, pay attention to MTU.
|
||||
|
||||
### 命令选项
|
||||
# Advanced Topic
|
||||
### Full Options
|
||||
```
|
||||
UDPspeeder
|
||||
version: Aug 9 2017 18:13:09
|
||||
UDPspeeder V2
|
||||
git version:8e7a8aed92 build date:Oct 25 2017 02:00:54
|
||||
repository: https://github.com/wangyu-/UDPspeeder
|
||||
|
||||
usage:
|
||||
@@ -73,69 +100,51 @@ usage:
|
||||
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"
|
||||
-k,--key <string> key for simple xor encryption. if not set,xor is disabled
|
||||
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
|
||||
-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
|
||||
--mode <number> fec-mode,available values: 0,1 ; 0 cost less bandwidth,1 cost less latency(default)
|
||||
--report <number> turn on send/recv report,and set a period 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%
|
||||
-m <number> max pending packets,to prevent the program from eating up all your memory.
|
||||
other options:
|
||||
--log-level <number> 0:never 1:fatal 2:error 3:warn
|
||||
--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
|
||||
-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
|
||||
-q,--queue-len <number> max fec queue len,only for mode 0
|
||||
--decode-buf <number> size of buffer of fec decoder,unit: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
|
||||
--sock-buf <number> buf size for socket,>=10 and <=10240,unit:kbyte,default:512
|
||||
-h,--help print this help message
|
||||
|
||||
```
|
||||
### 包发送选项,两端设置可以不同。 只影响本地包发送。
|
||||
##### -d 选项
|
||||
设置冗余包数量。
|
||||
##### -t 选项
|
||||
为冗余包的发送,增加一个延迟.对中间路由buffer做优化,应对瞬时Buffer过长导致的连续丢包
|
||||
##### -j 选项
|
||||
为原始数据的发送,增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大。可以不用设-j
|
||||
#### `--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
|
||||
|
||||
##### --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方案更好。不论是速度,还是流量的耗费上。
|
||||

|
||||
|
||||
# 编译教程
|
||||
暂时先参考udp2raw的这篇教程,几乎一样的过程。
|
||||
|
||||
https://github.com/wangyu-/udp2raw-tunnel/blob/master/doc/build_guide.zh-cn.md
|
||||
Check [UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
|
||||
|
361
common.cpp
@@ -14,12 +14,41 @@ int about_to_exit=0;
|
||||
|
||||
raw_mode_t raw_mode=mode_faketcp;
|
||||
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;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tmp_time);
|
||||
@@ -49,6 +78,47 @@ u32_t get_u64_l(u64_t a)
|
||||
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)
|
||||
{
|
||||
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 ret;
|
||||
int size=read(random_number_fd,&ret,sizeof(ret));
|
||||
int size=read(random_fd.get_fd(),&ret,sizeof(ret));
|
||||
if(size!=sizeof(ret))
|
||||
{
|
||||
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 ret;
|
||||
int size=read(random_number_fd,&ret,sizeof(ret));
|
||||
int size=read(random_fd.get_fd(),&ret,sizeof(ret));
|
||||
if(size!=sizeof(ret))
|
||||
{
|
||||
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);
|
||||
}
|
||||
int set_buf_size(int fd,int size)
|
||||
|
||||
|
||||
unsigned short tcp_csum(const pseudo_header & ph,const unsigned short *ptr,int nbytes) {//works both for big and little endian
|
||||
|
||||
register long sum;
|
||||
unsigned short oddbyte;
|
||||
register short answer;
|
||||
|
||||
sum=0;
|
||||
unsigned short * tmp= (unsigned short *)&ph;
|
||||
for(int i=0;i<6;i++)
|
||||
{
|
||||
sum+=*tmp++;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
//int socket_buf_size=1024*1024;
|
||||
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)
|
||||
{
|
||||
printf("set SO_SNDBUF fail\n");
|
||||
exit(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)
|
||||
{
|
||||
printf("set SO_RCVBUF fail\n");
|
||||
exit(1);
|
||||
}
|
||||
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_RCVBUF, &socket_buf_size, sizeof(socket_buf_size))<0)
|
||||
{
|
||||
mylog(log_fatal,"SO_RCVBUF fail socket_buf_size=%d errno=%s\n",socket_buf_size,strerror(errno));
|
||||
myexit(1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void myexit(int a)
|
||||
{
|
||||
if(enable_log_color)
|
||||
printf("%s\n",RESET);
|
||||
clear_iptables_rule();
|
||||
// clear_iptables_rule();
|
||||
exit(a);
|
||||
}
|
||||
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)
|
||||
{
|
||||
int size=read(random_number_fd,s,len);
|
||||
int size=read(random_fd.get_fd(),s,len);
|
||||
if(size!=len)
|
||||
{
|
||||
printf("get random number failed\n");
|
||||
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;
|
||||
}
|
||||
|
||||
|
125
common.h
@@ -7,7 +7,7 @@
|
||||
|
||||
#ifndef COMMON_H_
|
||||
#define COMMON_H_
|
||||
#define __STDC_FORMAT_MACROS 1
|
||||
//#define __STDC_FORMAT_MACROS 1
|
||||
#include <inttypes.h>
|
||||
|
||||
#include<stdio.h>
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h> //for socket ofcourse
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h> //for exit(0);
|
||||
#include <errno.h> //For errno - the error number
|
||||
#include <netinet/tcp.h> //Provides declarations for tcp header
|
||||
@@ -43,12 +44,11 @@
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <linux/if_packet.h>
|
||||
|
||||
|
||||
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include<unordered_map>
|
||||
#include<unordered_set>
|
||||
#include<map>
|
||||
#include<list>
|
||||
using namespace std;
|
||||
|
||||
@@ -59,16 +59,20 @@ typedef long long i64_t;
|
||||
typedef unsigned int u32_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 u32_t conv_clear_interval=200;
|
||||
const u32_t timer_interval=400;
|
||||
const int conv_clear_ratio=40;
|
||||
const int conv_clear_min=5;
|
||||
const u32_t conv_timeout=20000;
|
||||
|
||||
//const u32_t timer_interval=400;
|
||||
////const u32_t conv_timeout=180000;
|
||||
const u32_t conv_timeout=40000;//for test
|
||||
const int max_conv_num=10000;
|
||||
const int max_conn_num=200;
|
||||
|
||||
/*
|
||||
const u32_t max_handshake_conn_num=10000;
|
||||
@@ -79,14 +83,14 @@ const u32_t max_ready_conn_num=1000;
|
||||
const u32_t client_handshake_timeout=5000;
|
||||
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 conn_clear_ratio=10;
|
||||
const int conv_clear_min=5;
|
||||
const int conv_clear_ratio=30; //conv grabage collecter check 1/30 of all conv one time
|
||||
const int conn_clear_ratio=50;
|
||||
const int conv_clear_min=1;
|
||||
const int conn_clear_min=1;
|
||||
|
||||
|
||||
const u32_t conv_clear_interval=1000;
|
||||
const u32_t conn_clear_interval=1000;
|
||||
|
||||
|
||||
@@ -97,24 +101,29 @@ const u32_t heartbeat_interval=1000;
|
||||
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 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_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 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;
|
||||
|
||||
enum raw_mode_t{mode_faketcp=0,mode_udp,mode_icmp,mode_end};
|
||||
extern raw_mode_t raw_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 ;
|
||||
|
||||
enum working_mode_t {unset_working_mode=0,tunnel_mode,tun_dev_mode};
|
||||
extern working_mode_t working_mode;
|
||||
|
||||
extern int socket_buf_size;
|
||||
|
||||
|
||||
typedef u32_t id_t;
|
||||
|
||||
typedef u64_t iv_t;
|
||||
@@ -123,6 +132,58 @@ typedef u64_t padding_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_us();
|
||||
u64_t pack_u64(u32_t a,u32_t b);
|
||||
@@ -131,6 +192,15 @@ u32_t get_u64_h(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);
|
||||
|
||||
void myexit(int a);
|
||||
@@ -143,9 +213,10 @@ u64_t hton64(u64_t a);
|
||||
bool larger_than_u16(uint16_t a,uint16_t b);
|
||||
bool larger_than_u32(u32_t a,u32_t b);
|
||||
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 tcp_csum(const pseudo_header & ph,const unsigned short *ptr,int nbytes);
|
||||
|
||||
void signal_handler(int sig);
|
||||
int numbers_to_char(id_t id1,id_t id2,id_t id3,char * &data,int &len);
|
||||
@@ -157,5 +228,19 @@ int add_iptables_rule(char *);
|
||||
|
||||
int clear_iptables_rule();
|
||||
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_ */
|
||||
|
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_ */
|
330
doc/README.zh-cn.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# 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路由器,也可以是树莓派。在windows和mac上配合虚拟机可以稳定使用(speeder跑在Linux里,其他应用照常跑在window里,桥接模式测试可用),可以使用[这个](https://github.com/wangyu-/udp2raw-tunnel/releases/download/20170918.0/lede-17.01.2-x86_virtual_machine_image_with_udp2raw_pre_installed.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"
|
||||
|
||||
在client端运行:
|
||||
./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流量会被加速。
|
||||
|
||||
###### 备注:
|
||||
|
||||
`-f20:10` 表示对每20个原始数据发送10个冗余包。`-f20:10` 和`-f 20:10`都是可以的,空格可以省略,对于所有的单字节option都是如此。对于双字节option,例如后面的`--mode 0`和`--mtu 1200`,空格不可以省略。
|
||||
|
||||
`-k` 指定一个字符串,开启简单的异或加密
|
||||
|
||||
如果需要更省流量,请加上`--mode 0`,`--mode 0`模式会牺牲一点点的延迟(默认最多8ms,可调)换取更低的流量消耗。
|
||||
|
||||
###### 注意
|
||||
|
||||
要为UDPspeeder加速的应用设置好MTU(不是在UDPspeeder中,是在被加速的应用中),建议设置为1200。 另外,如果被加速的应用不能调整MTU,也可以在UDPspeeder中通过`--mode 0 --mtu 1200`设置MTU。关于`--mode 0`和`--mtu`的具体解释请看下文。
|
||||
|
||||
# 进阶操作说明
|
||||
|
||||
### 命令选项
|
||||
```
|
||||
UDPspeeder V2
|
||||
git version:8e7a8aed92 build date:Oct 25 2017 02:00:54
|
||||
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. 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
|
||||
--mode <number> fec-mode,available values: 0,1 ; 0 cost less bandwidth,1 cost less latency(default)
|
||||
--report <number> turn on send/recv report,and set a period for reporting,unit:s
|
||||
advanced options:
|
||||
--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
|
||||
-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
|
||||
-q,--queue-len <number> max fec queue len,only for mode 0
|
||||
--decode-buf <number> size of buffer of fec decoder,unit: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`选项
|
||||
fec编码器的工作模式。对于mode 0,编码器会积攒一定数量的packet,然后把他们合并再切成等长的片段(切分长度由--mtu指定)。对于mode 1,编码器不会做任何切分,而是会把packet按最大长度对齐,fec冗余包的长度为对齐后的长度(最大长度)。
|
||||
|
||||
mode 0更省流量,在丢包率正常的情况下效果和mode 1是一样的;mode 1延迟更低,在极高丢包的情况下表现更好。
|
||||
|
||||
mode 0使用起来可以不用关注mtu,因为fec编码器会帮你把包切分到合理的大小;用mode 1时必须合理设置上层应用的mtu。mode 0模式中--mtu选项决定切分的片段的长度,mode 1模式中--mtu选项只起检查作用,如果超过了--mtu指定的值,数据包会被丢弃。
|
||||
|
||||
mode 0模式的流量消耗基本完全透明。mode 1因为涉及到数据按最大长度对齐,所以流量消耗不是完全可预期。<del>不过就实际使用来看,mode 1消耗的额外流量不多。</del> mode 1一般会比mode 0多消耗零点几倍的流量,对于在意流量的人,推荐用mode 0。
|
||||
|
||||
mode 0模式数据包一般不会乱序,除非网络本身有严重乱序;mode 1模式被恢复的数据包可能会乱序,不过UDP本来就允许乱序,对绝大多数应用没有影响。mode 0模式反而可以纠正一些乱序情况。
|
||||
|
||||
mode 0模式允许你发送的数据包大小超过物理接口的MTU而几乎不引起性能损失(而普通的ip分片做不到这点),目前最高支持到2000字节,2000字节已经可以应对任何应用了,因为一般网络的MTU只有1400多。之所以支持到2000字节是为了省程序内部开的静态buff(静态buff避免malloc提高性能),如果你是开发者,通过重新编译,支持到UDP协议的极限(
|
||||
65507字节)也没问题。
|
||||
|
||||
##### `--report` 选项
|
||||
数据发送和接受报告。开启后可以根据此数据推测出包速和丢包率等特征。
|
||||
|
||||
##### `-i` 选项
|
||||
指定一个时间窗口,长度为n毫秒。同一个fec分组的数据在发送时候会被均匀分散到这n毫秒中。可以对抗突发性的丢包。默认值是0,因为这个功能需要用到时钟,在某些虚拟机里时钟不稳定,可能会导致个别包出现非常大的延迟,所以默认关掉了。这个功能很有用,默认参数效果不理想时可以尝试打开。这个选项的跟通信原理上常说的`交错fec` `交织fec`的原理是差不多的。
|
||||
|
||||
##### `-j` 选项
|
||||
为原始数据的发送,增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大,可以不用设-j。这个功能也需要时钟,默认关掉了,不过一般情况应该不需要这个功能。
|
||||
|
||||
-j选项不但可以模拟延迟抖动,也可以模拟延迟。
|
||||
|
||||
##### `--random-drop` 选项
|
||||
随机丢包。模拟高丢包的网络环境时使用。 `--random-drop`和`-j`选项一起用,可以模拟高延迟(或者高延迟抖动)高丢包的网络,可用于测试FEC参数在各种网络环境下的表现。
|
||||
|
||||
##### `-k`选项
|
||||
指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
||||
|
||||
##### `--disable-obscure`
|
||||
UDPspeeder默认情况下会对每个发出的数据包随机填充和异或一些字节(4~32字节),这样通过抓包难以发现你发了冗余数据,防止VPS被封。这个功能只是为了小心谨慎,即使你关掉这个功能,基本上也没问题,关掉可以省一些带宽和CPU。
|
||||
|
||||
#### `--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是否发送成功。
|
||||
|
||||
# 使用经验
|
||||
|
||||
### 在FEC和多倍发包之间如何选择
|
||||
|
||||
对于游戏,游戏的流量本身不大,延迟很重要,多倍发包是最佳解决方案,多倍发包不会引入额外的延迟。FEC编码器需要先积攒一些数据,才可以做FEC,延迟无法避免;对于多倍发包,没有这个问题,所以没有延迟。
|
||||
|
||||
对于其他日常应用(延迟要求一般),在合理配置的情况下,FEC的效果肯定好过多倍发包。不过需要根据网络的最大丢包来配置FEC参数,才能有稳定的效果。如果配置不当,对于--mode 1可能会完全没有效果;对于--mode 0,可能效果会比不用UDPspeeder还差。
|
||||
|
||||
对于游戏以外的应用,推荐使用FEC。但是,如果FEC版的默认参数在你那边效果很差,而你又不会调,可以先用多倍发包。
|
||||
|
||||
### V2版如何多倍发包
|
||||
|
||||
只要在设置-f参数时把x设置为1,fec算法就退化为多倍发包了。例如-f1:1,表示2倍发包,-f1:2表示3倍发包,以此类推。另外建议加上"--mode 1"参数,防止fec编码器试图积攒和合并数据,获得最低的延迟。
|
||||
|
||||
2倍发包的完整参数:
|
||||
|
||||
```
|
||||
./speederv2 -s -l0.0.0.0:4096 -r127.0.0.1:7777 -f1:1 -k "passwd" --mode 1
|
||||
./speederv2 -c -l0.0.0.0:3333 -r44.55.66.77:4096 -f1:1 -k "passwd" --mode 1
|
||||
```
|
||||
|
||||
使用了`--mode 1`以后,`--timeout`选项不起作用,所以不用调。
|
||||
|
||||
如果你只需要多倍发包,可以直接用回V1版,V1版配置更简单,占用内存更小,而且经过了几个月的考验,很稳定。
|
||||
|
||||
### 根据网络丢包合理设置FEC参数
|
||||
|
||||
默认的FEC参数为-f20:10,对每20个包,额外发送10个冗余包,也就是1.5倍发包。已经可以适应绝大多数的网络情况了,对于10%的网络丢包,可以降低到0.01%以下;对于20%的网络丢包,可以降低到2.5%。
|
||||
|
||||
如果你的网络丢包很低,比如在3%以下,可以尝试调低参数。比如-f20:5,也就是1.2倍发包,这个参数已经足够把3%的丢包降低到0.01%以下了。
|
||||
|
||||
如果网络丢包超过20%,需要把-f20:10调大。
|
||||
|
||||
如果你实在不会配,那么也可以用回V1版。
|
||||
|
||||
### 根据CPU处理能力来调整FEC参数
|
||||
|
||||
FEC算法很吃CPU,初次使用建议关注UDPspeeder的CPU占用。如果CPU被打满,可以在冗余度不变的情况下把FEC分组大小调小,否则的话效果可能很差。
|
||||
|
||||
比如-f20:10和-f10:5,都是1.5倍的冗余度,而-f20:10的FEC分组大小是30个包,-f10:5的FEC分组大小是15个包。-f20:10更费CPU,但是在一般情况下效果更稳定。把分组调小可以节省CPU。
|
||||
|
||||
另外,fec分组大小不宜过大,否则不但很耗CPU,还有其他副作用,建议x+y<50。
|
||||
|
||||
### 改变FEC参数而不断线
|
||||
|
||||
`--fifo`选项可以在运行时改变FEC参数,无需重启程序,也不会断线。如果你在使用过程中发现网络丢包突然变高,可以动态地把冗余度调大;反之也一样,如果网络变好了,把冗余度调小节省流量。一切都是无缝进行,不会断线,也不会因为改FEC参数导致额外的丢包。
|
||||
|
||||
### 为什么使用之后效果反而变差了?
|
||||
|
||||
有可能是你用了`--mode 0`参数,而又没调好参数。
|
||||
|
||||
如果你没有使用`--mode 0`,而确实效果变差了,那很可能是因为你的运营商对UDP有限制。一般看视频和下载都是TCP流量,而用UDPspeeder中转后流量变成了UDP流量,如果运营商对UDP做了限制,就可能导致效果比不用还差。用udp2raw可以解决,udp2raw: https://github.com/wangyu-/udp2raw-tunnel
|
||||
|
||||
|
||||
### UDPspeeder和BBR/锐速配合
|
||||
|
||||
UDPspeeder和BBR/锐速可以配合使用,UDPspeeder工作在IP层负责降低丢包率,BBR/锐速工作在TCP层负责优化拥塞和重传。这种情况下,可以调低UDPspeeder的冗余度,能把丢包率降低到5%以内就可以了,剩下的交给BBR/锐速解决,这样预计可以节省一些流量。如果是UDPspeeder跟Linux默认的Cubic一起用,最少也要把丢包率降低到1%以下才能流畅使用TCP。
|
||||
|
||||
对下文的`UDPspeeder + openvpn`和`UDPspeeder + openvpn + $***`方法有效。不过有一点区别,具体见下文。
|
||||
|
||||
### UDPspeeder和Kcptun配合
|
||||
|
||||
UDPspeeder和Kcptun配合,UDPspeeder和Kcptun可以并联也可以串联。
|
||||
|
||||
并联的情况下,让kcptun负责加速TCP,UDPspeeder负责加速UDP。见下文的`UDPspeeder + kcptun + $*** 同时加速tcp和udp流量`。
|
||||
|
||||
串联的情况。UDPspeeder的FEC跟Kcptun自带的相比:可以对两个方向设置不同的FEC参数、有一个更省流量的mode 0模式、可以动态改变FEC参数;但是UDPspeeder本身不优化拥塞和重传算法。所以UDPspeeder和Kcptun也可以配合使用,结合两者的优点。
|
||||
|
||||
串联时可以关掉Kcptun的FEC,让UDPspeeder接管FEC功能。这样UDPspeeder工作在UDP层负责降低丢包率,Kcptun工作在应用层用kcp算法负责优化拥塞和重传,能起到和`UDPspeeder+BBR/锐速`类似的效果。
|
||||
|
||||
如果发Issue问Kcptun+UDPspeeder相关的问题,一定要说明是并联还是串联。
|
||||
|
||||
# 应用
|
||||
|
||||
#### 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加速。
|
||||
|
||||
#### 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的完整设置
|
||||
|
||||
|
||||
#### 用树莓派做路由器,搭建透明代理,加速游戏主机的网络
|
||||
|
||||
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_ */
|
843
fec_manager.cpp
Normal file
@@ -0,0 +1,843 @@
|
||||
/*
|
||||
* 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=1;
|
||||
|
||||
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);
|
||||
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,fec_mtu=%d,len>fec_mtu,packet may not be able to deliver\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 new_len=(blob_encode.get_shard_len(i,0)+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 |
Before Width: | Height: | Size: 73 KiB 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_ */
|
79
makefile
@@ -1,45 +1,68 @@
|
||||
cc_cross=/home/wangyu/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++
|
||||
cc_cross=/home/wangyu/Desktop/arm-2014.05/bin/arm-none-linux-gnueabi-g++
|
||||
cc_local=g++
|
||||
cc_ar71xx=/home/wangyu/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++
|
||||
cc_bcm2708=/home/wangyu/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++
|
||||
cc_arm=/home/wangyu/Desktop/arm-2014.05/bin/arm-none-linux-gnueabi-g++
|
||||
FLAGS= -std=c++11 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter
|
||||
SOURCES=main.cpp log.cpp common.cpp
|
||||
NAME=speeder
|
||||
TAR=${NAME}_binaries.tar.gz ${NAME}_amd64 ${NAME}_x86 ${NAME}_ar71xx ${NAME}_bcm2708 ${NAME}_arm
|
||||
#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++
|
||||
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++
|
||||
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++
|
||||
#cc_arm= /toolchains/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi-g++ -march=armv6 -marm
|
||||
cc_arm= /toolchains/arm-2014.05/bin/arm-none-linux-gnueabi-g++
|
||||
#cc_bcm2708=/home/wangyu/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++
|
||||
FLAGS= -std=c++11 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers -ggdb
|
||||
|
||||
all:
|
||||
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
|
||||
|
||||
NAME=speederv2
|
||||
TARGETS=amd64 arm mips24kc_be x86 mips24kc_le
|
||||
|
||||
TAR=${NAME}_binaries.tar.gz `echo ${TARGETS}|sed -r 's/([^ ]+)/speederv2_\1/g'`
|
||||
|
||||
all:git_version
|
||||
rm -f ${NAME}
|
||||
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||
fast:
|
||||
rm -f ${NAME}
|
||||
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt
|
||||
debug:
|
||||
${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
|
||||
|
||||
ar71xx:
|
||||
${cc_ar71xx} -o ${NAME}_ar71xx -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
||||
bcm2708:
|
||||
${cc_bcm2708} -o ${NAME}_bcm2708 -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||
amd64:
|
||||
${cc_local} -o ${NAME}_amd64 -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||
x86:
|
||||
${cc_local} -o ${NAME}_x86 -I. ${SOURCES} ${FLAGS} -lrt -m32 -static -O3
|
||||
arm:
|
||||
${cc_arm} -o ${NAME}_arm -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
||||
mips24kc_be: git_version
|
||||
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
||||
|
||||
cross:
|
||||
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:
|
||||
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: amd64 x86 ar71xx bcm2708 arm
|
||||
release: ${TARGETS}
|
||||
tar -zcvf ${TAR}
|
||||
|
||||
clean:
|
||||
rm -f ${TAR}
|
||||
rm -f ${NAME}_cross ${NAME}
|
||||
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
|
||||
|
||||
|
967
misc.cpp
Normal file
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* 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 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},
|
||||
{"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,"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();
|
||||
|
||||
}
|
||||
|
73
misc.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 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_ */
|
400
packet.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* packet.cpp
|
||||
*
|
||||
* Created on: Sep 15, 2017
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
#include "packet.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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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\n");
|
||||
myexit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mylog(log_fatal,"epoll_wait return %d\n", nfds);
|
||||
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\n");
|
||||
myexit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mylog(log_fatal,"epoll_wait return %d\n", nfds);
|
||||
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;
|
||||
}
|