Compare commits
10 Commits
v2@2017102
...
v1@2017111
Author | SHA1 | Date | |
---|---|---|---|
|
ee9dab37fa | ||
|
d25e1ea9df | ||
|
3b5279235a | ||
|
cccff0a8a5 | ||
|
66da392357 | ||
|
a5bad45780 | ||
|
b1a47bb8ea | ||
|
00cffe1072 | ||
|
b5a873d073 | ||
|
8a2e2b8ce4 |
205
README.md
@@ -1,86 +1,77 @@
|
|||||||
# UDPspeeder
|
# UDPspeeder
|
||||||
Network Speed-Up Tool. Boost your Connection on a High Lantency High Packet-Loss Link by using Forward Error Correction.
|

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

|
||||||
|
#### 原理简介
|
||||||
|
目前原理是多倍发包。以后会做各种优化,比如:对高频率的短包先合并再冗余;FEC(Forward Error Correction),在包速低的时候多倍发包,包速高时用FEC。
|
||||||
|
|
||||||

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

|
模拟一定的延迟抖动,这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。
|
||||||
|
|
||||||
[简体中文](/doc/README.zh-cn.md)(内容更丰富)
|
#### 适用场景
|
||||||
|
绝大部分流量不高的情况。程序本身加速udp,但是配合openvpn可以加速任何流量。网络状况不好时,游戏卡得没法玩,或者网页卡得没法打开,使用起来效果最好。对于解决语音通话的断断续续效果也不错。不适合大流量的场景,比如BT下载和在线看视频。 无论从自己使用效果的角度,还是从国际出口带宽占用的角度,都建议不要在大流量环境使用。
|
||||||
|
|
||||||
###### Note
|
#### 其他功能
|
||||||
You can use udp2raw with UDPspeeder together to bypass UDP firewalls.
|
输出UDP收发情况报告,可以看出丢包率。
|
||||||
udp2raw:https://github.com/wangyu-/udp2raw-tunnel
|
|
||||||
|
|
||||||
|
模拟丢包,模拟延迟,模拟jitter。便于通过实验找出应用卡顿的原因。
|
||||||
|
|
||||||
# Efficacy
|
重复包过滤功能可以关掉,模拟网络本身有重复包的情况。用来测试应用对重复报的支持情况。
|
||||||
tested on a link with 100ms latency and 10% packet loss at both direction
|
|
||||||
|
|
||||||
### Ping Packet Loss
|
client支持多个udp连接,server也支持多个client
|
||||||

|
|
||||||
|
|
||||||
### SCP Copy Speed
|
目前有amd64,x86,ar71xx,树莓派armv7和android的binary
|
||||||

|
|
||||||
|
|
||||||
# Supported Platforms
|
如果你需要绕过UDP屏蔽/QoS,或者需要连接复用/连接保持功能,或者是加密。解决方案在另一个repo(可以跟UDPspeeder一起使用):
|
||||||
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.
|
https://github.com/wangyu-/udp2raw-tunnel
|
||||||
|
|
||||||
# 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.
|
### 环境要求
|
||||||
|
Linux主机,可以是桌面版,可以是android手机/平板,可以是openwrt路由器,也可以是树莓派。在windows和mac上配合虚拟机可以稳定使用(speeder跑在Linux里,其他应用照常跑在window里,桥接模式测试可用)。
|
||||||
|
|
||||||

|
android版需要通过terminal运行。
|
||||||
|
|
||||||
### Reed-Solomon
|
### 安装
|
||||||
|
下载编译好的二进制文件,解压到本地和服务器的任意目录。
|
||||||
|
|
||||||
`
|
https://github.com/wangyu-/UDPspeeder/releases
|
||||||
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.
|
假设你有一个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"
|
||||||
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"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
现在client和server之间建立起了tunnel。想要连接44.55.66.77:7777,只需要连接 127.0.0.1:3333。来回的所有的udp流量会被加速。
|
||||||
|
|
||||||
##### Note
|
###### 注:
|
||||||
|
|
||||||
`-f20:10` means sending 10 redundant packets for every 20 original packets.
|
-d2 表示除了本来的包以外,额外再发2个冗余包。可调。
|
||||||
|
|
||||||
`-k` enables simple XOR encryption to confuse DPI(Deep Packet Inspection)
|
-k 指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
||||||
|
|
||||||
To run stably,pay attention to MTU.
|
### MTU设置(重要)
|
||||||
|
|
||||||
# Advanced Topic
|
不论你用udpspeeder来加速kcptun还是vpn,为了稳定使用,都需要设置合理的MTU(在kcptun/vpn里设置,而不是在udpspeeder里),建议把MTU设置成1200。client和server端都要设置。
|
||||||
### Full Options
|
|
||||||
|
# 进阶操作说明
|
||||||
|
|
||||||
|
### 命令选项
|
||||||
```
|
```
|
||||||
UDPspeeder V2
|
UDPspeeder
|
||||||
git version:8e7a8aed92 build date:Oct 25 2017 02:00:54
|
git version:b4bd385e88 build date:Sep 11 2017 10:29:25
|
||||||
repository: https://github.com/wangyu-/UDPspeeder
|
repository: https://github.com/wangyu-/UDPspeeder
|
||||||
|
|
||||||
usage:
|
usage:
|
||||||
@@ -88,51 +79,77 @@ usage:
|
|||||||
run as server : ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]
|
run as server : ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]
|
||||||
|
|
||||||
common option,must be same on both sides:
|
common option,must be same on both sides:
|
||||||
-k,--key <string> key for simple xor encryption. if not set,xor is disabled
|
-k,--key <string> key for simple xor encryption,default:"secret key"
|
||||||
main options:
|
main options:
|
||||||
-f,--fec x:y forward error correction,send y redundant packets for every x packets
|
-d <number> duplicated packet number, -d 0 means no duplicate. default value:0
|
||||||
--timeout <number> how long could a packet be held in queue before doing fec,unit: ms,default :8ms
|
-t <number> duplicated packet delay time, unit: 0.1ms,default value:20(2ms)
|
||||||
--mode <number> fec-mode,available values: 0,1 ; 0 cost less bandwidth,1 cost less latency(default)
|
-j <number> simulated jitter.randomly delay first packet for 0~jitter_value*0.1 ms,to
|
||||||
--report <number> turn on send/recv report,and set a period for reporting,unit:s
|
create simulated jitter.default value:0.do not use if you dont
|
||||||
|
know what it means
|
||||||
|
--report <number> turn on udp send/recv report,and set a time interval for reporting,unit:s
|
||||||
advanced options:
|
advanced options:
|
||||||
--mtu <number> mtu. for mode 0,the program will split packet to segment smaller than mtu_value.
|
-t tmin:tmax simliar to -t above,but delay randomly between tmin and tmax
|
||||||
for mode 1,no packet will be split,the program just check if the mtu is exceed.
|
-j jmin:jmax simliar to -j above,but create jitter randomly between jmin and jmax
|
||||||
default value:1250
|
--random-drop <number> simulate packet loss ,unit:0.01%
|
||||||
-j,--jitter <number> simulated jitter.randomly delay first packet for 0~<number> ms,default value:0.
|
--disable-filter disable duplicate packet filter.
|
||||||
do not use if you dont know what it means.
|
-m <number> max pending packets,to prevent the program from eating up all your memory,
|
||||||
-i,--interval <number> scatter each fec group to a interval of <number> ms,to protect burst packet loss.
|
default value:0(disabled).
|
||||||
default value:0.do not use if you dont know what it means.
|
other options:
|
||||||
--random-drop <number> simulate packet loss ,unit:0.01%. default value: 0
|
--log-level <number> 0:never 1:fatal 2:error 3:warn
|
||||||
--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
|
4:info (default) 5:debug 6:trace
|
||||||
--log-position enable file name,function name,line number in log
|
--log-position enable file name,function name,line number in log
|
||||||
--disable-color disable log color
|
--disable-color disable log color
|
||||||
|
--sock-buf <number> buf size for socket,>=10 and <=10240,unit:kbyte,default:1024
|
||||||
-h,--help print this help message
|
-h,--help print this help message
|
||||||
|
|
||||||
```
|
```
|
||||||
#### `--fifo` option
|
### 包发送选项,两端设置可以不同。 只影响本地包发送。
|
||||||
Use a fifo(named pipe) for sending commands to the running program. For example `--fifo fifo.file`,you can use following commands to change parameters dynamically:
|
##### -d 选项
|
||||||
```
|
设置冗余包数量。
|
||||||
echo fec 19:9 >fifo.file
|
##### -t 选项
|
||||||
echo mtu 1100 >fifo.file
|
为冗余包的发送,增加一个延迟.对中间路由buffer做优化,应对瞬时Buffer过长导致的连续丢包.对于多个冗余包,依次在前一个包的基础上增加这个延迟。
|
||||||
echo timeout 5 >fifo.file
|
##### -j 选项
|
||||||
echo queue-len 100 >fifo.file
|
为原始数据的发送,增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大。可以不用设-j
|
||||||
echo mode 0 >fifo.file
|
|
||||||
```
|
|
||||||
### Speed-Up any traffic with OpenVPN+UDPspeeder
|
|
||||||
|
|
||||||
Check [UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
|
##### --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 + ss 同时加速tcp和udp流量
|
||||||
|
如果你需要用加速的tcp看视频和下载文件,这样效果比vpn方案更好。不论是速度,还是流量的耗费上。
|
||||||
|

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

|
||||||
|
(也可以把图中的ss server换成其他的socks5 server,这样连ss client也不需要了)
|
||||||
|
# 编译教程
|
||||||
|
暂时先参考udp2raw的这篇教程,几乎一样的过程。
|
||||||
|
|
||||||
|
https://github.com/wangyu-/udp2raw-tunnel/blob/master/doc/build_guide.zh-cn.md
|
||||||
|
261
common.cpp
@@ -14,37 +14,12 @@ int about_to_exit=0;
|
|||||||
|
|
||||||
raw_mode_t raw_mode=mode_faketcp;
|
raw_mode_t raw_mode=mode_faketcp;
|
||||||
unordered_map<int, const char*> raw_mode_tostring = {{mode_faketcp, "faketcp"}, {mode_udp, "udp"}, {mode_icmp, "icmp"}};
|
unordered_map<int, const char*> raw_mode_tostring = {{mode_faketcp, "faketcp"}, {mode_udp, "udp"}, {mode_icmp, "icmp"}};
|
||||||
|
int socket_buf_size=1024*1024;
|
||||||
int delay_capacity=0;
|
static int random_number_fd=-1;
|
||||||
//static int random_number_fd=-1;
|
|
||||||
char iptables_rule[200]="";
|
char iptables_rule[200]="";
|
||||||
//int is_client = 0, is_server = 0;
|
|
||||||
|
|
||||||
program_mode_t program_mode=unset_mode;//0 unset; 1client 2server
|
program_mode_t program_mode=unset_mode;//0 unset; 1client 2server
|
||||||
|
|
||||||
|
u64_t get_current_time()
|
||||||
|
|
||||||
struct random_fd_t
|
|
||||||
{
|
|
||||||
int random_number_fd;
|
|
||||||
random_fd_t()
|
|
||||||
{
|
|
||||||
random_number_fd=open("/dev/urandom",O_RDONLY);
|
|
||||||
|
|
||||||
if(random_number_fd==-1)
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"error open /dev/urandom\n");
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
setnonblocking(random_number_fd);
|
|
||||||
}
|
|
||||||
int get_fd()
|
|
||||||
{
|
|
||||||
return random_number_fd;
|
|
||||||
}
|
|
||||||
}random_fd;
|
|
||||||
|
|
||||||
u64_t get_current_time()//ms
|
|
||||||
{
|
{
|
||||||
timespec tmp_time;
|
timespec tmp_time;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &tmp_time);
|
clock_gettime(CLOCK_MONOTONIC, &tmp_time);
|
||||||
@@ -74,47 +49,6 @@ u32_t get_u64_l(u64_t a)
|
|||||||
return (a<<32u)>>32u;
|
return (a<<32u)>>32u;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_u16(char * p,u16_t w)
|
|
||||||
{
|
|
||||||
*(unsigned char*)(p + 1) = (w & 0xff);
|
|
||||||
*(unsigned char*)(p + 0) = (w >> 8);
|
|
||||||
}
|
|
||||||
u16_t read_u16(char * p)
|
|
||||||
{
|
|
||||||
u16_t res;
|
|
||||||
res = *(const unsigned char*)(p + 0);
|
|
||||||
res = *(const unsigned char*)(p + 1) + (res << 8);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_u32(char * p,u32_t l)
|
|
||||||
{
|
|
||||||
*(unsigned char*)(p + 3) = (unsigned char)((l >> 0) & 0xff);
|
|
||||||
*(unsigned char*)(p + 2) = (unsigned char)((l >> 8) & 0xff);
|
|
||||||
*(unsigned char*)(p + 1) = (unsigned char)((l >> 16) & 0xff);
|
|
||||||
*(unsigned char*)(p + 0) = (unsigned char)((l >> 24) & 0xff);
|
|
||||||
}
|
|
||||||
u32_t read_u32(char * p)
|
|
||||||
{
|
|
||||||
u32_t res;
|
|
||||||
res = *(const unsigned char*)(p + 0);
|
|
||||||
res = *(const unsigned char*)(p + 1) + (res << 8);
|
|
||||||
res = *(const unsigned char*)(p + 2) + (res << 8);
|
|
||||||
res = *(const unsigned char*)(p + 3) + (res << 8);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_u64(char * s,u64_t a)
|
|
||||||
{
|
|
||||||
assert(0==1);
|
|
||||||
}
|
|
||||||
u64_t read_u64(char * s)
|
|
||||||
{
|
|
||||||
assert(0==1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char * my_ntoa(u32_t ip)
|
char * my_ntoa(u32_t ip)
|
||||||
{
|
{
|
||||||
in_addr a;
|
in_addr a;
|
||||||
@@ -160,11 +94,22 @@ int clear_iptables_rule()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void init_random_number_fd()
|
||||||
|
{
|
||||||
|
|
||||||
|
random_number_fd=open("/dev/urandom",O_RDONLY);
|
||||||
|
|
||||||
|
if(random_number_fd==-1)
|
||||||
|
{
|
||||||
|
mylog(log_fatal,"error open /dev/urandom\n");
|
||||||
|
myexit(-1);
|
||||||
|
}
|
||||||
|
setnonblocking(random_number_fd);
|
||||||
|
}
|
||||||
u64_t get_true_random_number_64()
|
u64_t get_true_random_number_64()
|
||||||
{
|
{
|
||||||
u64_t ret;
|
u64_t ret;
|
||||||
int size=read(random_fd.get_fd(),&ret,sizeof(ret));
|
int size=read(random_number_fd,&ret,sizeof(ret));
|
||||||
if(size!=sizeof(ret))
|
if(size!=sizeof(ret))
|
||||||
{
|
{
|
||||||
mylog(log_fatal,"get random number failed %d\n",size);
|
mylog(log_fatal,"get random number failed %d\n",size);
|
||||||
@@ -177,7 +122,7 @@ u64_t get_true_random_number_64()
|
|||||||
u32_t get_true_random_number()
|
u32_t get_true_random_number()
|
||||||
{
|
{
|
||||||
u32_t ret;
|
u32_t ret;
|
||||||
int size=read(random_fd.get_fd(),&ret,sizeof(ret));
|
int size=read(random_number_fd,&ret,sizeof(ret));
|
||||||
if(size!=sizeof(ret))
|
if(size!=sizeof(ret))
|
||||||
{
|
{
|
||||||
mylog(log_fatal,"get random number failed %d\n",size);
|
mylog(log_fatal,"get random number failed %d\n",size);
|
||||||
@@ -256,42 +201,28 @@ unsigned short csum(const unsigned short *ptr,int nbytes) {
|
|||||||
|
|
||||||
return(answer);
|
return(answer);
|
||||||
}
|
}
|
||||||
int set_buf_size(int fd,int socket_buf_size,int force_socket_buf)
|
int set_buf_size(int fd,int size)
|
||||||
{
|
{
|
||||||
if(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)
|
//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));
|
printf("set SO_SNDBUF fail\n");
|
||||||
myexit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if(setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
|
//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)
|
||||||
mylog(log_fatal,"SO_RCVBUFFORCE fail socket_buf_size=%d errno=%s\n",socket_buf_size,strerror(errno));
|
{
|
||||||
myexit(1);
|
printf("set SO_RCVBUF fail\n");
|
||||||
}
|
exit(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void myexit(int a)
|
void myexit(int a)
|
||||||
{
|
{
|
||||||
if(enable_log_color)
|
if(enable_log_color)
|
||||||
printf("%s\n",RESET);
|
printf("%s\n",RESET);
|
||||||
// clear_iptables_rule();
|
clear_iptables_rule();
|
||||||
exit(a);
|
exit(a);
|
||||||
}
|
}
|
||||||
void signal_handler(int sig)
|
void signal_handler(int sig)
|
||||||
@@ -391,138 +322,10 @@ bool larger_than_u16(uint16_t a,uint16_t b)
|
|||||||
|
|
||||||
void get_true_random_chars(char * s,int len)
|
void get_true_random_chars(char * s,int len)
|
||||||
{
|
{
|
||||||
int size=read(random_fd.get_fd(),s,len);
|
int size=read(random_number_fd,s,len);
|
||||||
if(size!=len)
|
if(size!=len)
|
||||||
{
|
{
|
||||||
printf("get random number failed\n");
|
printf("get random number failed\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int random_between(u32_t a,u32_t b)
|
|
||||||
{
|
|
||||||
if(a>b)
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"min >max?? %d %d\n",a ,b);
|
|
||||||
myexit(1);
|
|
||||||
}
|
|
||||||
if(a==b)return a;
|
|
||||||
else return a+get_true_random_number()%(b+1-a);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int set_timer_ms(int epollfd,int &timer_fd,u32_t timer_interval)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
epoll_event ev;
|
|
||||||
|
|
||||||
itimerspec its;
|
|
||||||
memset(&its,0,sizeof(its));
|
|
||||||
|
|
||||||
if((timer_fd=timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK)) < 0)
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"timer_fd create error\n");
|
|
||||||
myexit(1);
|
|
||||||
}
|
|
||||||
its.it_interval.tv_sec=(timer_interval/1000);
|
|
||||||
its.it_interval.tv_nsec=(timer_interval%1000)*1000ll*1000ll;
|
|
||||||
its.it_value.tv_nsec=1; //imidiately
|
|
||||||
timerfd_settime(timer_fd,0,&its,0);
|
|
||||||
|
|
||||||
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
ev.data.fd = timer_fd;
|
|
||||||
|
|
||||||
ret=epoll_ctl(epollfd, EPOLL_CTL_ADD, timer_fd, &ev);
|
|
||||||
if (ret < 0) {
|
|
||||||
mylog(log_fatal,"epoll_ctl return %d\n", ret);
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
int create_new_udp(int &new_udp_fd,int remote_address_uint32,int remote_port)
|
|
||||||
{
|
|
||||||
struct sockaddr_in remote_addr_in;
|
|
||||||
|
|
||||||
socklen_t slen = sizeof(sockaddr_in);
|
|
||||||
memset(&remote_addr_in, 0, sizeof(remote_addr_in));
|
|
||||||
remote_addr_in.sin_family = AF_INET;
|
|
||||||
remote_addr_in.sin_port = htons(remote_port);
|
|
||||||
remote_addr_in.sin_addr.s_addr = remote_address_uint32;
|
|
||||||
|
|
||||||
new_udp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
||||||
if (new_udp_fd < 0) {
|
|
||||||
mylog(log_warn, "create udp_fd error\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
setnonblocking(new_udp_fd);
|
|
||||||
set_buf_size(new_udp_fd);
|
|
||||||
|
|
||||||
mylog(log_debug, "created new udp_fd %d\n", new_udp_fd);
|
|
||||||
int ret = connect(new_udp_fd, (struct sockaddr *) &remote_addr_in, slen);
|
|
||||||
if (ret != 0) {
|
|
||||||
mylog(log_warn, "udp fd connect fail %d %s\n",ret,strerror(errno));
|
|
||||||
close(new_udp_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}*/
|
|
||||||
void ip_port_t::from_u64(u64_t u64)
|
|
||||||
{
|
|
||||||
ip=get_u64_h(u64);
|
|
||||||
port=get_u64_l(u64);
|
|
||||||
}
|
|
||||||
u64_t ip_port_t::to_u64()
|
|
||||||
{
|
|
||||||
return pack_u64(ip,port);
|
|
||||||
}
|
|
||||||
char * ip_port_t::to_s()
|
|
||||||
{
|
|
||||||
static char res[40];
|
|
||||||
sprintf(res,"%s:%d",my_ntoa(ip),port);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int round_up_div(int a,int b)
|
|
||||||
{
|
|
||||||
return (a+b-1)/b;
|
|
||||||
}
|
|
||||||
|
|
||||||
int create_fifo(char * file)
|
|
||||||
{
|
|
||||||
if(mkfifo (file, 0666)!=0)
|
|
||||||
{
|
|
||||||
if(errno==EEXIST)
|
|
||||||
{
|
|
||||||
mylog(log_warn,"warning fifo file %s exist\n",file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"create fifo file %s failed\n",file);
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int fifo_fd=open (file, O_RDWR);
|
|
||||||
if(fifo_fd<0)
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"create fifo file %s failed\n",file);
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
struct stat st;
|
|
||||||
if (fstat(fifo_fd, &st)!=0)
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"fstat failed for fifo file %s\n",file);
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!S_ISFIFO(st.st_mode))
|
|
||||||
{
|
|
||||||
mylog(log_fatal,"%s is not a fifo\n",file);
|
|
||||||
myexit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
setnonblocking(fifo_fd);
|
|
||||||
return fifo_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
89
common.h
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#ifndef COMMON_H_
|
#ifndef COMMON_H_
|
||||||
#define COMMON_H_
|
#define COMMON_H_
|
||||||
//#define __STDC_FORMAT_MACROS 1
|
#define __STDC_FORMAT_MACROS 1
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include<stdio.h>
|
#include<stdio.h>
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/socket.h> //for socket ofcourse
|
#include <sys/socket.h> //for socket ofcourse
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <stdlib.h> //for exit(0);
|
#include <stdlib.h> //for exit(0);
|
||||||
#include <errno.h> //For errno - the error number
|
#include <errno.h> //For errno - the error number
|
||||||
#include <netinet/tcp.h> //Provides declarations for tcp header
|
#include <netinet/tcp.h> //Provides declarations for tcp header
|
||||||
@@ -46,9 +45,10 @@
|
|||||||
#include <linux/if_packet.h>
|
#include <linux/if_packet.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include<unordered_map>
|
#include<unordered_map>
|
||||||
#include<unordered_set>
|
#include<unordered_set>
|
||||||
#include<map>
|
|
||||||
#include<list>
|
#include<list>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -59,20 +59,16 @@ typedef long long i64_t;
|
|||||||
typedef unsigned int u32_t;
|
typedef unsigned int u32_t;
|
||||||
typedef int i32_t;
|
typedef int i32_t;
|
||||||
|
|
||||||
typedef unsigned short u16_t;
|
|
||||||
typedef short i16_t;
|
|
||||||
|
|
||||||
typedef u64_t my_time_t;
|
const int max_data_len=1600;
|
||||||
|
|
||||||
const int max_data_len=2200;
|
|
||||||
const int buf_len=max_data_len+200;
|
const int buf_len=max_data_len+200;
|
||||||
|
|
||||||
const u32_t conv_clear_interval=200;
|
const u32_t conv_clear_interval=200;
|
||||||
//const u32_t timer_interval=400;
|
const u32_t timer_interval=400;
|
||||||
////const u32_t conv_timeout=180000;
|
const int conv_clear_ratio=40;
|
||||||
const u32_t conv_timeout=40000;//for test
|
const int conv_clear_min=5;
|
||||||
|
const u32_t conv_timeout=180000;
|
||||||
const int max_conv_num=10000;
|
const int max_conv_num=10000;
|
||||||
const int max_conn_num=200;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const u32_t max_handshake_conn_num=10000;
|
const u32_t max_handshake_conn_num=10000;
|
||||||
@@ -83,7 +79,7 @@ const u32_t max_ready_conn_num=1000;
|
|||||||
const u32_t client_handshake_timeout=5000;
|
const u32_t client_handshake_timeout=5000;
|
||||||
const u32_t client_retry_interval=1000;
|
const u32_t client_retry_interval=1000;
|
||||||
|
|
||||||
const u32_t server_handshake_timeout=10000;// this should be much longer than clients. client retry initially ,server retry passtively*/
|
const u32_t server_handshake_timeout=10000;// this should be much longer than clients. client retry initially ,server retry passtively
|
||||||
|
|
||||||
const int conv_clear_ratio=10; //conv grabage collecter check 1/10 of all conv one time
|
const int conv_clear_ratio=10; //conv grabage collecter check 1/10 of all conv one time
|
||||||
const int conn_clear_ratio=10;
|
const int conn_clear_ratio=10;
|
||||||
@@ -98,17 +94,17 @@ const i32_t max_fail_time=0;//disable
|
|||||||
|
|
||||||
const u32_t heartbeat_interval=1000;
|
const u32_t heartbeat_interval=1000;
|
||||||
|
|
||||||
const u32_t timer_interval=500;//this should be smaller than heartbeat_interval and retry interval;
|
const u32_t timer_interval=400;//this should be smaller than heartbeat_interval and retry interval;
|
||||||
|
|
||||||
//const uint32_t conv_timeout=120000; //120 second
|
//const uint32_t conv_timeout=120000; //120 second
|
||||||
//const u32_t conv_timeout=120000; //for test
|
const u32_t conv_timeout=120000; //for test
|
||||||
|
|
||||||
const u32_t client_conn_timeout=10000;
|
const u32_t client_conn_timeout=10000;
|
||||||
const u32_t client_conn_uplink_timeout=client_conn_timeout+2000;
|
const u32_t client_conn_uplink_timeout=client_conn_timeout+2000;
|
||||||
|
|
||||||
//const uint32_t server_conn_timeout=conv_timeout+60000;//this should be 60s+ longer than conv_timeout,so that conv_manager can destruct convs gradually,to avoid latency glicth
|
//const uint32_t server_conn_timeout=conv_timeout+60000;//this should be 60s+ longer than conv_timeout,so that conv_manager can destruct convs gradually,to avoid latency glicth
|
||||||
const u32_t server_conn_timeout=conv_timeout+20000;//for test
|
const u32_t server_conn_timeout=conv_timeout+60000;//for test
|
||||||
|
*/
|
||||||
|
|
||||||
extern int about_to_exit;
|
extern int about_to_exit;
|
||||||
|
|
||||||
@@ -117,9 +113,7 @@ extern raw_mode_t raw_mode;
|
|||||||
enum program_mode_t {unset_mode=0,client_mode,server_mode};
|
enum program_mode_t {unset_mode=0,client_mode,server_mode};
|
||||||
extern program_mode_t program_mode;
|
extern program_mode_t program_mode;
|
||||||
extern unordered_map<int, const char*> raw_mode_tostring ;
|
extern unordered_map<int, const char*> raw_mode_tostring ;
|
||||||
|
extern int socket_buf_size;
|
||||||
extern int delay_capacity;
|
|
||||||
|
|
||||||
|
|
||||||
typedef u32_t id_t;
|
typedef u32_t id_t;
|
||||||
|
|
||||||
@@ -129,40 +123,6 @@ typedef u64_t padding_t;
|
|||||||
|
|
||||||
typedef u64_t anti_replay_seq_t;
|
typedef u64_t anti_replay_seq_t;
|
||||||
|
|
||||||
typedef u64_t fd64_t;
|
|
||||||
|
|
||||||
enum dest_type{none=0,type_ip_port,type_fd64,type_ip_port_conv,type_fd64_conv/*,type_fd*/};
|
|
||||||
|
|
||||||
|
|
||||||
struct ip_port_t
|
|
||||||
{
|
|
||||||
u32_t ip;
|
|
||||||
int port;
|
|
||||||
void from_u64(u64_t u64);
|
|
||||||
u64_t to_u64();
|
|
||||||
char * to_s();
|
|
||||||
};
|
|
||||||
|
|
||||||
union inner_t
|
|
||||||
{
|
|
||||||
ip_port_t ip_port;
|
|
||||||
//int fd;
|
|
||||||
fd64_t fd64;
|
|
||||||
};
|
|
||||||
struct dest_t
|
|
||||||
{
|
|
||||||
dest_type type;
|
|
||||||
inner_t inner;
|
|
||||||
u32_t conv;
|
|
||||||
int cook=0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fd_info_t
|
|
||||||
{
|
|
||||||
ip_port_t ip_port;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
u64_t get_current_time();
|
u64_t get_current_time();
|
||||||
u64_t get_current_time_us();
|
u64_t get_current_time_us();
|
||||||
u64_t pack_u64(u32_t a,u32_t b);
|
u64_t pack_u64(u32_t a,u32_t b);
|
||||||
@@ -171,15 +131,6 @@ u32_t get_u64_h(u64_t a);
|
|||||||
|
|
||||||
u32_t get_u64_l(u64_t a);
|
u32_t get_u64_l(u64_t a);
|
||||||
|
|
||||||
void write_u16(char *,u16_t a);
|
|
||||||
u16_t read_u16(char *);
|
|
||||||
|
|
||||||
void write_u32(char *,u32_t a);
|
|
||||||
u32_t read_u32(char *);
|
|
||||||
|
|
||||||
void write_u64(char *,u64_t a);
|
|
||||||
u64_t read_uu64(char *);
|
|
||||||
|
|
||||||
char * my_ntoa(u32_t ip);
|
char * my_ntoa(u32_t ip);
|
||||||
|
|
||||||
void myexit(int a);
|
void myexit(int a);
|
||||||
@@ -192,7 +143,7 @@ u64_t hton64(u64_t a);
|
|||||||
bool larger_than_u16(uint16_t a,uint16_t b);
|
bool larger_than_u16(uint16_t a,uint16_t b);
|
||||||
bool larger_than_u32(u32_t a,u32_t b);
|
bool larger_than_u32(u32_t a,u32_t b);
|
||||||
void setnonblocking(int sock);
|
void setnonblocking(int sock);
|
||||||
int set_buf_size(int fd,int socket_buf_size,int force_socket_buf=0);
|
int set_buf_size(int fd,int size=socket_buf_size);
|
||||||
|
|
||||||
unsigned short csum(const unsigned short *ptr,int nbytes);
|
unsigned short csum(const unsigned short *ptr,int nbytes);
|
||||||
|
|
||||||
@@ -206,15 +157,5 @@ int add_iptables_rule(char *);
|
|||||||
|
|
||||||
int clear_iptables_rule();
|
int clear_iptables_rule();
|
||||||
void get_true_random_chars(char * s,int len);
|
void get_true_random_chars(char * s,int len);
|
||||||
int random_between(u32_t a,u32_t b);
|
|
||||||
|
|
||||||
int set_timer_ms(int epollfd,int &timer_fd,u32_t timer_interval);
|
|
||||||
|
|
||||||
int round_up_div(int a,int b);
|
|
||||||
|
|
||||||
int create_fifo(char * file);
|
|
||||||
/*
|
|
||||||
int create_new_udp(int &new_udp_fd,int remote_address_uint32,int remote_port);
|
|
||||||
*/
|
|
||||||
|
|
||||||
#endif /* COMMON_H_ */
|
#endif /* COMMON_H_ */
|
||||||
|
293
connection.cpp
@@ -1,293 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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(program_mode==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(program_mode==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
@@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
@@ -1,119 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
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
@@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
@@ -1,275 +0,0 @@
|
|||||||
# 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):
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
另外,可以和udp2raw串联使用,在加速的同时把UDP伪装成TCP,防止UDP被运营商QOS或屏蔽。udp2raw: https://github.com/wangyu-/udp2raw-tunnel
|
|
||||||
#### 效果
|
|
||||||

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

|
|
||||||
#### 原理简介
|
|
||||||
主要原理是通过冗余数据来对抗网络的丢包,发送冗余数据的方式支持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,免去在虚拟机里装系统的麻烦。
|
|
||||||
|
|
||||||
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个冗余包。
|
|
||||||
|
|
||||||
-k 指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
|
||||||
|
|
||||||
###### 注意
|
|
||||||
|
|
||||||
要为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因为涉及到数据按最大长度对齐,所以流量消耗不是完全可预期。不过就实际使用来看,mode 1消耗的额外流量不多。
|
|
||||||
|
|
||||||
mode 0模式数据包一般不会乱序,除非网络本身有严重乱序;mode 1模式被恢复的数据包可能会乱序,不过UDP本来就允许乱序,对绝大多数应用没有影响。mode 0模式反而可以纠正一些乱序情况。
|
|
||||||
|
|
||||||
##### `--report` 选项
|
|
||||||
数据发送和接受报告。开启后可以根据此数据推测出包速和丢包率等特征。
|
|
||||||
|
|
||||||
##### `-i` 选项
|
|
||||||
指定一个时间窗口,长度为n毫秒。同一个fec分组的数据在发送时候会被均匀分散到这n毫秒中。可以对抗突发性的丢包。默认值是0,因为这个功能需要用到时钟,在某些虚拟机里时钟不稳定,可能会导致个别包出现非常大的延迟,所以默认关掉了。这个功能很有用,默认参数效果不理想时可以尝试打开。
|
|
||||||
|
|
||||||
##### `-j` 选项
|
|
||||||
为原始数据的发送,增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大,以等待后续冗余包的到达,不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大,可以不用设-j。这个功能也需要时钟,默认关掉了,不过一般情况应该不需要这个功能。
|
|
||||||
|
|
||||||
##### `--random-drop` 选项
|
|
||||||
随机丢包。模拟恶劣的网络环境时使用。如果你的网络现在没有多大丢包,但是你想测试一下高丢包环境下各种FEC参数的表现,可以开这个选项。
|
|
||||||
|
|
||||||
##### `-k`选项
|
|
||||||
指定一个字符串,server/client间所有收发的包都会被异或,改变协议特征,防止UDPspeeder的协议被运营商针对。
|
|
||||||
|
|
||||||
##### `--disable-obscure`
|
|
||||||
UDPspeeder默认情况下会对每个发出的数据包随机填充和异或一些字节(4~32字节),这样通过抓包难以发现你发了冗余数据,防止VPS被封。这个功能只是为了小心谨慎,即使你关掉这个功能,基本上也没问题,关掉可以省一些带宽和CPU。
|
|
||||||
|
|
||||||
##### `-q,--queue-len`
|
|
||||||
编码器在做FEC前最多积攒多少个数据包,只对mode 0有效。除非是使用下文`V2版如何多倍发包`里面提到的用法,不建议改动。
|
|
||||||
#### `--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编码器参数。
|
|
||||||
|
|
||||||
# 使用经验
|
|
||||||
|
|
||||||
### 在FEC和多倍发包之间如何选择
|
|
||||||
|
|
||||||
对于游戏,游戏的流量本身不大,延迟很重要,多倍发包是最佳解决方案,多倍发包不会引入额外的延迟。FEC编码器需要先积攒一些数据,才可以做FEC,延迟无法避免;对于多倍发包,没有这个问题,所以没有延迟。
|
|
||||||
|
|
||||||
对于其他日常应用(延迟要求一般),在合理配置的情况下,FEC的效果肯定好过多倍发包。不过需要根据网络的最大丢包来配置FEC参数,才能有稳定的效果。如果配置不当,对于--mode 1可能会完全没有效果;对于--mode 0,可能效果会比不用UDPspeeder还差。
|
|
||||||
|
|
||||||
对于游戏以外的应用,推荐使用FEC。但是,如果FEC版的默认参数在你那边效果很差,而你又不会调,可以先用多倍发包。
|
|
||||||
|
|
||||||
### V2版如何多倍发包
|
|
||||||
|
|
||||||
只要在设置-f参数时把x设置为1,fec算法就退化为多倍发包了。例如-f1:1,表示2倍发包,-f1:2表示3倍发包,以此类推。另外可以加上`--mode 0 -q1`参数,防止fec编码器试图积攒和合并数据,获得最低的延迟。
|
|
||||||
|
|
||||||
2倍发包的完整参数:
|
|
||||||
|
|
||||||
```
|
|
||||||
./speederv2 -s -l0.0.0.0:4096 -r127.0.0.1:7777 -f1:1 -k "passwd" --mode 0 -q1
|
|
||||||
./speederv2 -c -l0.0.0.0:3333 -r44.55.66.77:4096 -f1:1 -k "passwd" --mode 0 -q1
|
|
||||||
```
|
|
||||||
|
|
||||||
使用了`--mode 0 -q1`以后,`--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参数,无需重启程序,也不会断线。
|
|
||||||
|
|
||||||
### 为什么使用之后效果反而变差了?
|
|
||||||
|
|
||||||
有可能是你用了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加速任何流量
|
|
||||||

|
|
||||||
|
|
||||||
具体配置见,[UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
|
|
||||||
|
|
||||||
可以和BBR/锐速叠加,不过BBR/锐速部署在VPS上只对从本地到VPS的流量有效,对从本地到第三方服务器的流量无效。
|
|
||||||
|
|
||||||
需要在服务端开启ipforward和NAT。在客户端改路由表(可以手动修改,也可以由OpenVPN的redirect-gateway选项自动加好)。
|
|
||||||
|
|
||||||
#### UDPspeeder + kcptun/finalspeed + $*** 同时加速tcp和udp流量
|
|
||||||
如果你需要用加速的tcp看视频和下载文件,这样效果比UDPspeeder+vpn方案更好(在没有BBR的情况下)。
|
|
||||||

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

|
|
||||||
|
|
||||||
(也可以把图中的$*** server换成其他的socks5 server,这样就不需要$*** client了)
|
|
||||||
|
|
||||||
可以和BBR/锐速叠加,BBR/锐速只要部署在VPS上就有效。
|
|
||||||
|
|
||||||
# 编译教程
|
|
||||||
暂时先参考udp2raw的这篇教程,几乎一样的过程。
|
|
||||||
|
|
||||||
https://github.com/wangyu-/udp2raw-tunnel/blob/master/doc/build_guide.zh-cn.md
|
|
@@ -1,154 +0,0 @@
|
|||||||
# 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
|
|
@@ -1,102 +0,0 @@
|
|||||||
|
|
||||||
# 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)
|
|
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
849
fec_manager.cpp
@@ -1,849 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
re_init(fec_data_num,fec_redundant_num,fec_mtu,fec_queue_len,fec_timeout,fec_mode);
|
|
||||||
|
|
||||||
seq=(u32_t)get_true_random_number(); //TODO temp solution for a bug.
|
|
||||||
}
|
|
||||||
int fec_encode_manager_t::re_init(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);
|
|
||||||
counter=0;
|
|
||||||
blob_encode.clear();
|
|
||||||
ready_for_output=0;
|
|
||||||
//seq=0;
|
|
||||||
|
|
||||||
itimerspec zero_its;
|
|
||||||
memset(&zero_its, 0, sizeof(zero_its));
|
|
||||||
|
|
||||||
timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
|
|
||||||
|
|
||||||
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&&blob_encode.get_shard_len(fec_data_num,len)>=fec_mtu)
|
|
||||||
{
|
|
||||||
mylog(log_warn,"message too long len=%d,ignored\n",len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(fec_mode==1&&s!=0&&len>=fec_mtu)
|
|
||||||
{
|
|
||||||
mylog(log_warn,"message too long len=%d fec_mtu=%d,ignored\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()
|
|
||||||
{
|
|
||||||
for(int i=0;i<(int)fec_buff_num;i++)
|
|
||||||
fec_data[i].used=0;
|
|
||||||
ready_for_output=0;
|
|
||||||
index=0;
|
|
||||||
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;
|
|
||||||
}
|
|
218
fec_manager.h
@@ -1,218 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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()
|
|
||||||
{
|
|
||||||
memset(replay_buffer,-1,sizeof(replay_buffer));
|
|
||||||
st.rehash(anti_replay_buff_size*3);
|
|
||||||
index=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();
|
|
||||||
|
|
||||||
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 re_init(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];
|
|
||||||
re_init();
|
|
||||||
}
|
|
||||||
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 re_init();
|
|
||||||
int input(char *s,int len);
|
|
||||||
int output(int &n,char ** &s_arr,int* &len_arr);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* FEC_MANAGER_H_ */
|
|
@@ -1 +0,0 @@
|
|||||||
|
|
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB |
@@ -1 +0,0 @@
|
|||||||
|
|
Before Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 669 KiB |
Before Width: | Height: | Size: 1.1 MiB |
BIN
images/en/rs.png
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 34 KiB |
917
lib/fec.c
@@ -1,917 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
72
lib/rs.c
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
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
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
21
makefile
@@ -6,18 +6,21 @@ cc_mips24kc_le=/toolchains/lede-sdk-17.01.2-ramips-mt7621_gcc-5.4.0_musl-1.1.16.
|
|||||||
#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/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_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++
|
#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
|
FLAGS= -std=c++11 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers
|
||||||
|
|
||||||
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
|
SOURCES=main.cpp log.cpp common.cpp
|
||||||
|
|
||||||
NAME=speederv2
|
NAME=speeder
|
||||||
TARGETS=amd64 arm mips24kc_be x86 mips24kc_le
|
TARGETS=amd64 arm mips24kc_be x86 mips24kc_le
|
||||||
|
|
||||||
TAR=${NAME}_binaries.tar.gz `echo ${TARGETS}|sed -r 's/([^ ]+)/speederv2_\1/g'`
|
TAR=${NAME}_binaries.tar.gz `echo ${TARGETS}|sed -r 's/([^ ]+)/speeder_\1/g'`
|
||||||
|
|
||||||
all:git_version
|
all:git_version
|
||||||
rm -f ${NAME}
|
rm -f ${NAME}
|
||||||
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O3
|
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O3
|
||||||
|
fast: git_version
|
||||||
|
rm -f ${NAME}
|
||||||
|
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb
|
||||||
debug: git_version
|
debug: git_version
|
||||||
rm -f ${NAME}
|
rm -f ${NAME}
|
||||||
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -D MY_DEBUG
|
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -D MY_DEBUG
|
||||||
@@ -28,24 +31,16 @@ debug2: git_version
|
|||||||
mips24kc_be: git_version
|
mips24kc_be: git_version
|
||||||
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
||||||
|
|
||||||
mips24kc_be_debug: git_version
|
|
||||||
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -ggdb
|
|
||||||
|
|
||||||
mips24kc_le: git_version
|
mips24kc_le: git_version
|
||||||
${cc_mips24kc_le} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
${cc_mips24kc_le} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
|
||||||
|
|
||||||
amd64:git_version
|
amd64:git_version
|
||||||
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
${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
|
x86:git_version
|
||||||
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3 -m32
|
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3 -m32
|
||||||
arm:git_version
|
arm:git_version
|
||||||
${cc_arm} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3
|
${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
|
cross:git_version
|
||||||
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -O3
|
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -O3
|
||||||
|
|
||||||
@@ -64,5 +59,5 @@ clean:
|
|||||||
rm -f git_version.h
|
rm -f git_version.h
|
||||||
|
|
||||||
git_version:
|
git_version:
|
||||||
echo "const char * const gitversion = \"$(shell git rev-parse HEAD)\";" > git_version.h
|
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > git_version.h
|
||||||
|
|
||||||
|
364
packet.cpp
@@ -1,364 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
put_crc32(data,len);
|
|
||||||
if(!disable_obscure)do_obscure(data,len);
|
|
||||||
if(!disable_xor)encrypt_0(data,len,key_string);
|
|
||||||
}
|
|
||||||
switch(dest.type)
|
|
||||||
{
|
|
||||||
case type_ip_port:
|
|
||||||
{
|
|
||||||
return sendto_ip_port(dest.inner.ip_port.ip,dest.inner.ip_port.port,data,len,0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case type_ip_port_conv:
|
|
||||||
{
|
|
||||||
char *new_data;
|
|
||||||
int new_len;
|
|
||||||
put_conv(dest.conv,data,len,new_data,new_len);
|
|
||||||
return sendto_ip_port(dest.inner.ip_port.ip,dest.inner.ip_port.port,new_data,new_len,0);
|
|
||||||
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_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 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
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 local_listen_fd;
|
|
||||||
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 de_cook(char * s,int &len);
|
|
||||||
#endif /* PACKET_H_ */
|
|