wangyu 8 лет назад
Родитель
Сommit
307399b00a
9 измененных файлов с 241 добавлено и 37 удалено
  1. 4 2
      README.md
  2. 2 2
      common.cpp
  3. 1 0
      common.h
  4. 8 3
      doc/README.zh-cn.md
  5. 87 0
      doc/openvpn_guide.md
  6. 20 0
      main.cpp
  7. 9 4
      makefile
  8. 105 26
      network.cpp
  9. 5 0
      network.h

+ 4 - 2
README.md

@@ -10,8 +10,8 @@ Which can help you bypass udp blocking or udp QOS or just poorly supported udp N
 encrypt your traffic with aes128cbc,protects data integrity by md5 or crc32,protect replay attack with an anti-replay window smiliar to ipsec/openvpn.
 ### Simulated TCP Handshake
 simulated 3-way handshake,simluated seq ack_seq. Simluated tcp options:MSS,sackOk,TS,TS_ack,wscale. Provides real-time delivery ,no tcp over tcp problem when using openvpn.
-### Connnection Recover
-After connection timeouts,the client will re-connect.if re-connection is successful,the previous connection will be recovered,and all existed udp conversations will stay vaild.
+### Connnection Failure Dectection & Recover
+Conection failure detection by hearbeat. After hearbeat timeouts,client will auto change port and re-connect.if re-connection is successful,the previous connection will be recovered,and all existed udp conversations will stay vaild.
 ### Other Features
 Multiplexing ,one client supports multi udp connections,all of those traffic will share one raw connection
 
@@ -118,6 +118,8 @@ raw_mode: faketcp  cipher_mode: aes128cbc  auth_mode: md5
 2. no TCP ovr tcp problem (tcp over tcp problem http://sites.inka.de/bigred/devel/tcp-tcp.html ,https://community.openvpn.net/openvpn/ticket/2 )
 
 3. openvpn over icmp also becomes a choice
+
+more details at [openvpn+udp2raw_guide](/doc/openvpn_guide.md)
 ### tunneling kcptun
 make kcptun support tcp mode.
 (kcptun, https://github.com/xtaci/kcptun)

+ 2 - 2
common.cpp

@@ -198,12 +198,12 @@ int set_buf_size(int fd)
 {
     if(setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
     {
-    	mylog(log_fatal,"SO_SNDBUFFORCE fail\n");
+    	mylog(log_fatal,"SO_SNDBUFFORCE fail,fd %d\n",fd);
     	myexit(1);
     }
     if(setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
     {
-    	mylog(log_fatal,"SO_RCVBUFFORCE fail\n");
+    	mylog(log_fatal,"SO_RCVBUFFORCE fail,fd %d\n",fd);
     	myexit(1);
     }
 	return 0;

+ 1 - 0
common.h

@@ -42,6 +42,7 @@
 #include <arpa/inet.h>
 #include <stdarg.h>
 #include <assert.h>
+#include <linux/if_packet.h>
 
 
 

+ 8 - 3
doc/README.zh-cn.md

@@ -18,10 +18,12 @@ Udp2raw-tunnel
 ### 模拟TCP3次握手
 模拟TCP3次握手,模拟seq ack过程。另外还模拟了一些tcp option:MSS,sackOk,TS,TS_ack,wscale,用来使流量看起来更像是由普通的linux tcp协议栈发送的。
 
-### 连接快速恢复
+### 连接保持,连接快速恢复
 心跳机制检查连接是否中断,一旦心跳超时。client会立即换raw socket的端口重连,重连成功后会恢复之前中断的连接。虽然raw端的端口变了,但是udp端的所有连接都会继续有效。udp这边感觉不到raw端的重连,只会感觉到短暂断流,这跟普通的短暂丢包是类似的,不会导致上层应用重连。
 
-另一个优化是,重连只需要client发起,就可以立即被server处理,不需要等到server端的连接超时后。
+另一个优化是,重连只需要client发起,就可以立即被server处理,不需要等到server端的连接超时后。这个在单向连接失效的情况下有用。
+
+另外,对于有大量client的情况,对于不同client,server发送的心跳是错开时间发送的,不会因为短时间发送大量的心跳而造成拥塞和延迟抖动。
 
 ### 其他特性
 信道复用,client的udp端支持多个连接。
@@ -32,7 +34,10 @@ NAT 穿透 ,tcp icmp udp模式都支持nat穿透。
 
 支持Openvz,配合finalspeed使用,可以在openvz上用tcp模式的finalspeed
 
-支持Openwrt,没有编译以来,容易编译到任何平台上。release中提供了ar71xx版本的binary
+支持Openwrt,没有编译依赖,容易编译到任何平台上。release中提供了ar71xx版本的binary
+
+单进程,纯异步,无锁,高并发,除了回收过期连接外,所有操作的时间复杂度都跟连接数无关。回收过期连接这个操作是个批量操作,会定期进行,但是会保证一次回收的数量不超过总数的1/10(可配置),不会造成延迟抖动。
+
 ### 关键词
 突破udp qos,突破udp屏蔽,openvpn tcp over tcp problem,openvpn over icmp,udp to icmp tunnel,udp to tcp tunnel,udp via icmp,udp via tcp
 

+ 87 - 0
doc/openvpn_guide.md

@@ -1 +1,88 @@
+# udp2raw+openvpn config guide
+![image_vpn](/images/openvpn.PNG)
 
+![image4](/images/image4.PNG)
+# udp2raw command
+#### run at server side
+```
+./udp2raw_amd64 -s -l0.0.0.0:8855 -r 127.0.0.1:7777 -k "passwd" --raw-mode faketcp -a
+```
+#### run at client side
+assume server ip is 45.66.77.88
+```
+./udp2raw_amd64 -s -l0.0.0.0:3333 -r 45.66.77.88:8855 -k "passwd" --raw-mode faketcp -a
+```
+
+
+# openvpn config
+
+#### client side config
+```
+
+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
+cipher none      ##### disable openvpn 's cipher and auth for maxmized peformance. 
+auth none        ##### you can enable openvpn's cipher and auth,if you dont care about peformance,oryou dont trust udp2raw 's encryption
+
+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
+cipher none      ##### disable openvpn 's cipher and auth for maxmized peformance. 
+auth none        ##### you can enable openvpn's cipher and auth,if you dont care about peformance,oryou dont trust udp2raw 's encryption
+
+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
+```

+ 20 - 0
main.cpp

@@ -38,6 +38,7 @@ int disable_anti_replay=0;
 char key_string[1000]= "secret key";
 char key[16];//,key2[16];
 
+
 //uint64_t current_time_rough=0;
 
 
@@ -2351,6 +2352,7 @@ void process_arg(int argc, char *argv[])
 		{"gen-rule", no_argument,    0, 'g'},
 		{"debug", no_argument,    0, 1},
 		{"clear", no_argument,    0, 1},
+		{"lower-level", required_argument,    0, 1},
 		{"sock-buf", required_argument,    0, 1},
 		{"seq-mode", required_argument,    0, 1},
 		{NULL, 0, 0, 0}
@@ -2544,6 +2546,24 @@ void process_arg(int argc, char *argv[])
 			else if(strcmp(long_options[option_index].name,"log-level")==0)
 			{
 			}
+			else if(strcmp(long_options[option_index].name,"lower-level")==0)
+			{
+				if(strchr(optarg,'#')==0)
+				{
+					mylog(log_fatal,"lower-level parameter invaild,should be if_name#mac_adress  ,ie eth0#00:23:45:67:89:b9\n");
+					myexit(-1);
+				}
+				lower_level=1;
+				u32_t hw[6];
+				memset(hw,0,sizeof(hw));
+				sscanf(optarg,"%[^#]#%x:%x:%x:%x:%x:%x",if_name,&hw[0],&hw[1],&hw[2],&hw[3],&hw[4],&hw[5]);
+
+				mylog(log_warn,"make sure this is correct:   ifname=<%s>  gateway_hw_hd=<%x:%x:%x:%x:%x:%x>  \n",if_name,hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);
+				for(int i=0;i<6;i++)
+				{
+					oppsite_hw_addr[i]=uint8_t(hw[i]);
+				}
+			}
 			else if(strcmp(long_options[option_index].name,"disable-color")==0)
 			{
 				//enable_log_color=0;

+ 9 - 4
makefile

@@ -4,13 +4,18 @@ FLAGS2= -O3
 all:
 	sudo killall udp2raw||true
 	sleep 0.2
-	g++ main.cpp -o udp2raw_amd64 -static  -ggdb -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} ${FLAGS2}
-	${ccmips} main.cpp -o udp2raw_ar71xx  -lrt -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -std=c++11 ${FLAGS} ${FLAGS2}
+	g++ main.cpp -o udp2raw  -static  -ggdb -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} ${FLAGS2}
+#	${ccmips} main.cpp -o udp2raw_ar71xx  -lrt -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -std=c++11 ${FLAGS} ${FLAGS2}
 fast:
 	sudo killall udp2raw||true
 	sleep 0.2
-	g++ main.cpp -o udp2raw_amd64 -ggdb -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} 
+	g++ main.cpp -o udp2raw -ggdb -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} 
 
 
 debug:
-	g++ main.cpp -o udp2raw_amd64 -static  -ggdb -I. -Ilib lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} -Wformat-nonliteral -D MY_DEBUG
+	g++ main.cpp -o udp2raw -static  -ggdb -I. -Ilib lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} -Wformat-nonliteral -D MY_DEBUG
+
+release:
+	g++ main.cpp -o udp2raw_amd64  -static  -ggdb -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -lrt -std=c++11    ${FLAGS} ${FLAGS2}
+	${ccmips} main.cpp -o udp2raw_ar71xx  -lrt -I. lib/aes.c lib/md5.c encrypt.cpp log.cpp network.cpp common.cpp -std=c++11 ${FLAGS} ${FLAGS2}
+	

+ 105 - 26
network.cpp

@@ -20,7 +20,13 @@ int disable_bpf_filter=0;  //for test only,most time no need to disable this
 
 u32_t bind_address_uint32=0;
 
+int lower_level=0;
+int ifindex=-1;
+char if_name[100]="";
 
+unsigned char oppsite_hw_addr[6]=
+    {0xff,0xff,0xff,0xff,0xff,0xff};
+//{0x00,0x23,0x45,0x67,0x89,0xb9};
 
 struct sock_filter code_tcp_old[] = {
 		{ 0x28, 0, 0, 0x0000000c },//0
@@ -156,20 +162,47 @@ packet_info_t::packet_info_t()
 int init_raw_socket()
 {
 
-	raw_send_fd = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);
+	if(lower_level==0)
+	{
+		raw_send_fd = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);
 
+	    if(raw_send_fd == -1) {
+	    	mylog(log_fatal,"Failed to create raw_send_fd\n");
+	        //perror("Failed to create raw_send_fd");
+	        myexit(1);
+	    }
 
-    if(raw_send_fd == -1) {
-    	mylog(log_fatal,"Failed to create raw_send_fd\n");
-        //perror("Failed to create raw_send_fd");
-        myexit(1);
-    }
+	    int one = 1;
+	    const int *val = &one;
+	    if (setsockopt (raw_send_fd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) {
+	    	mylog(log_fatal,"Error setting IP_HDRINCL %d\n",errno);
+	        //perror("Error setting IP_HDRINCL");
+	        myexit(2);
+	    }
+
+
+	}
+	else
+	{
+		raw_send_fd = socket(PF_PACKET , SOCK_DGRAM , htons(ETH_P_IP));
+
+	    if(raw_send_fd == -1) {
+	    	mylog(log_fatal,"Failed to create raw_send_fd\n");
+	        //perror("Failed to create raw_send_fd");
+	        myexit(1);
+	    }
+		init_ifindex(if_name);
+
+	}
 
     if(setsockopt(raw_send_fd, SOL_SOCKET, SO_SNDBUFFORCE, &socket_buf_size, sizeof(socket_buf_size))<0)
     {
     	mylog(log_fatal,"SO_SNDBUFFORCE fail\n");
     	myexit(1);
     }
+
+
+
 	//raw_fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
 
 	raw_recv_fd= socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
@@ -188,13 +221,7 @@ int init_raw_socket()
 
     //IP_HDRINCL to tell the kernel that headers are included in the packet
 
-    int one = 1;
-    const int *val = &one;
-    if (setsockopt (raw_send_fd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) {
-    	mylog(log_fatal,"Error setting IP_HDRINCL %d\n",errno);
-        //perror("Error setting IP_HDRINCL");
-        myexit(2);
-    }
+
 
     setnonblocking(raw_send_fd); //not really necessary
     setnonblocking(raw_recv_fd);
@@ -258,8 +285,26 @@ void remove_filter()
 		//exit(-1);
 	}
 }
+int init_ifindex(char * if_name)
+{
+	struct ifreq ifr;
+	size_t if_name_len=strlen(if_name);
+	if (if_name_len<sizeof(ifr.ifr_name)) {
+		memcpy(ifr.ifr_name,if_name,if_name_len);
+		ifr.ifr_name[if_name_len]=0;
+	} else {
+		mylog(log_fatal,"interface name is too long\n");
+		myexit(-1);
+	}
+	if (ioctl(raw_send_fd,SIOCGIFINDEX,&ifr)==-1) {
 
-
+		mylog(log_fatal,"SIOCGIFINDEX fail ,%s\n",strerror(errno));
+		myexit(-1);
+	}
+	ifindex=ifr.ifr_ifindex;
+	mylog(log_info,"ifname:%s  ifindex:%d\n",if_name,ifindex);
+	return 0;
+}
 
 
 int send_raw_ip(raw_info_t &raw_info,const char * payload,int payloadlen)
@@ -271,17 +316,20 @@ int send_raw_ip(raw_info_t &raw_info,const char * payload,int payloadlen)
 	struct iphdr *iph = (struct iphdr *) send_raw_ip_buf;
     memset(iph,0,sizeof(iphdr));
 
-	struct sockaddr_in sin;
-    sin.sin_family = AF_INET;
-    //sin.sin_port = htons(info.dst_port); //dont need this
-    sin.sin_addr.s_addr = send_info.dst_ip;
+    static unsigned short ip_id=1;
 
     iph->ihl = sizeof(iphdr)/4;  //we dont use ip options,so the length is just sizeof(iphdr)
     iph->version = 4;
     iph->tos = 0;
 
-   // iph->id = htonl (ip_id++); //Id of this packet
-    // iph->id = 0; //Id of this packet  ,kernel will auto fill this if id is zero
+    if(lower_level)
+    {
+    	iph->id=0;
+    	//iph->id = htons (ip_id++); //Id of this packet
+    }
+    else
+    	iph->id = 0; //Id of this packet  ,kernel will auto fill this if id is zero  ,or really?????// todo //seems like there is a problem
+
     iph->frag_off = htons(0x4000); //DF set,others are zero
    // iph->frag_off = htons(0x0000); //DF set,others are zero
     iph->ttl = 64;
@@ -291,20 +339,51 @@ int send_raw_ip(raw_info_t &raw_info,const char * payload,int payloadlen)
     iph->daddr = send_info.dst_ip;
 
     uint16_t ip_tot_len=sizeof (struct iphdr)+payloadlen;
-   // iph->tot_len = htons(ip_tot_len);            //this is not necessary ,kernel will always auto fill this  //http://man7.org/linux/man-pages/man7/raw.7.html
-    //iph->tot_len = ip_tot_len;
+    if(lower_level)iph->tot_len = htons(ip_tot_len);            //this is not necessary ,kernel will always auto fill this  //http://man7.org/linux/man-pages/man7/raw.7.html
+    else
+    	iph->tot_len = 0;
+
     memcpy(send_raw_ip_buf+sizeof(iphdr) , payload, payloadlen);
 
-    //iph->check = csum ((unsigned short *) send_raw_ip_buf, ip_tot_len); //this is not necessary ,kernel will always auto fill this
+    if(lower_level) iph->check =
+    		csum ((unsigned short *) send_raw_ip_buf, iph->ihl*4); //this is not necessary ,kernel will always auto fill this
+    else
+    	iph->check=0;
+
+    int ret;
+    if(lower_level==0)
+    {
+		struct sockaddr_in sin;
+		sin.sin_family = AF_INET;
+		//sin.sin_port = htons(info.dst_port); //dont need this
+		sin.sin_addr.s_addr = send_info.dst_ip;
+		ret = sendto(raw_send_fd, send_raw_ip_buf, ip_tot_len ,  0, (struct sockaddr *) &sin, sizeof (sin));
+
+    }
+    else
+    {
 
-    int ret = sendto(raw_send_fd, send_raw_ip_buf, ip_tot_len ,  0, (struct sockaddr *) &sin, sizeof (sin));
+    	struct sockaddr_ll addr;
+    	memset(&addr,0,sizeof(addr));
 
+    	addr.sll_family=AF_PACKET;
+    	addr.sll_ifindex=ifindex;
+    	addr.sll_halen=ETHER_ADDR_LEN;
+    	addr.sll_protocol=htons(ETH_P_IP);
+    	memcpy(addr.sll_addr,oppsite_hw_addr,ETHER_ADDR_LEN);
+    	ret = sendto(raw_send_fd, send_raw_ip_buf, ip_tot_len ,  0, (struct sockaddr *) &addr, sizeof (addr));
+    }
     if(ret==-1)
     {
-    	mylog(log_debug,"sendto failed\n");
+
+    	mylog(log_trace,"sendto failed\n");
     	//perror("why?");
     	return -1;
     }
+    else
+    {
+    	//mylog(log_info,"sendto succ\n");
+    }
     return 0;
 }
 int peek_raw(packet_info_t &peek_info)
@@ -426,7 +505,7 @@ int recv_raw_ip(raw_info_t &raw_info,char * &payload,int &payloadlen)
 
     if(ip_chk!=0)
      {
-    	mylog(log_debug,"ip header error %d\n",ip_chk);
+    	mylog(log_debug,"ip header error %x\n",ip_chk);
      	return -1;
      }
 

+ 5 - 0
network.h

@@ -15,6 +15,10 @@ extern int filter_port;
 extern u32_t bind_address_uint32;
 extern int disable_bpf_filter;
 
+extern int lower_level;
+extern char if_name[100];
+extern unsigned char oppsite_hw_addr[];
+
 struct icmphdr
 {
 	uint8_t type;
@@ -76,6 +80,7 @@ int init_raw_socket();
 void init_filter(int port);
 
 void remove_filter();
+int init_ifindex(char * if_name);
 
 
 int send_raw_ip(raw_info_t &raw_info,const char * payload,int payloadlen);