1:数据通信框图
上面是数据的流向,是双向的。此处主要是PC的数据进过router设备,之后到达proxy server,由proxy server转发之后到达UDPserver端。特定PC(特定IP及port)的数据进行WLAN设备的sock5客服端处理,直接将数据发送给UDP server。其中对于PC来说不用做任何的处理(sock5相关的协议),在此router设备完成了与sock5proxy 认证协商,链路建立,数据包头的添加与去除工作。
2: 实现细节说明
A:router设备完成与proxy的协商及获取proxy的动态port及IP。
此处是需要根据通信协议与sock5服务交互完成动态port和IP的获取。
B:将第A步获取到的port及IP和UDPserver端的IP及port传递给内核。
通过proc文件将应用层获取的port及IP传递给linux内核。
C:添加iptable规则将PC的目的地址更改为A中获取到的port及IP。在此之前PC的数据不能经过WLAN系统发送出去。对来特定源IP的PC的数据包做DNAT使其目的更改为:A中的port及IP。
D:在内核发送函数中添加sock5UDP包头(包括:UDPserver的port及IP)。
其数据包头格式如下:
+----+------+------+----------+----------+----------+
|RSV| FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+----+------+-----+-----------+------------+----------+
|2 | 1 | 1 |Variable | 2 | Variable |
+----+------+------+----------+----------+----------+
其中:
o RSV 保留字段,填0。
o FRAG 当前分片序号,我们没有分片,填0。
o ATYP 地址类型,和前面的几个一样。IPV4填 1 。
o DST.ADDR UDP包最终的目的地址。即udp server端的IP地址
o DST.PORT UDP包最终的目的端口。即udp server端的port。
o DATA 原始的UDP包的数据。
按照上面格式发出的UDP包中的DATA部分会被代理服务器转发到包头中填入的最终目的地址。
D:在内核接受函数中移除sock5UDP包头(包括:UDPserver的port及IP)。
3:实现代码
A :router上面代理服务器动态port及IP的获取及iptables规则的配置。
/* ============================================================================ Name : doproxyDNAT.c Author : Version : Copyright : Your copyright notice Description : Hello World in C, Ansi-style ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <netdb.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <pthread.h> #include <errno.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <bits/time.h> #define SOCKET_ERROR -1 #define closesocket close #define PROXY_PORT 1080 #define PROXY_IPADDR "192.168.100.10" #define UDP_SERVER_PORT 6666 #define UDP_SERVER_IPADDR "10.10.98.2" #define UDP_CLIENT_PORT 5555 #define UDP_CLIENT_IPADDR "192.168.100.20" #define ZF5_BUFFER_SIZE1 255 #define ZF5_BUFFER_SIZE2 1024 #define ZF5_BUFFER_SIZE3 8192 #define USER_NAME "comba" #define USER_PASSWORD "comba123" static char *proxyReplyIp=NULL; static int proxyReplyPort=0; static void sigHandler(int sig) { char cmdbuf[500]; memset(cmdbuf,0,500); // if ((sig == SIGINT ) || (sig == SIGKILL ) ) { //删除建立的规则 sprintf(cmdbuf,"iptables -t nat -D PREROUTING -s 192.168.1.0/24 -p udp -j DNAT --to %s:%d",proxyReplyIp,proxyReplyPort); printf("%s\n",cmdbuf); system(cmdbuf); exit(0); } } int main(void) { int nRet=0; int len=0; int s_sock5_tcp=socket(AF_INET,SOCK_STREAM,0); if( s_sock5_tcp < 0 ) { return -1; } else { printf("create tcp socket ok\n"); } struct timeval tv; tv.tv_sec=10; tv.tv_usec=0; if(setsockopt(s_sock5_tcp,SOL_SOCKET,SO_RCVTIMEO,(char *)&tv,sizeof(struct timeval))==-1) { close(s_sock5_tcp); return -1; } else { printf("setsockopt ok\n"); } struct sockaddr_in sa_1; sa_1.sin_family=AF_INET; sa_1.sin_addr.s_addr=inet_addr(PROXY_IPADDR); sa_1.sin_port=htons(PROXY_PORT); if(sa_1.sin_addr.s_addr==INADDR_NONE) { perror("proxy server ip\n"); } if(connect(s_sock5_tcp,(struct sockaddr *)&sa_1,sizeof(struct sockaddr_in))==-1) { close(s_sock5_tcp); printf("connect proxy server fail \n"); return -1; } else { printf("connect proxy server ok\n"); } char sz_buf[ZF5_BUFFER_SIZE2+2]; memset(sz_buf,0,ZF5_BUFFER_SIZE2); int authTYpe=0; if(authTYpe == 0) { sz_buf[len++]=0x05; sz_buf[len++]=0x01; sz_buf[len++]=0x00; } else { sz_buf[len++]=0x05; sz_buf[len++]=0x02; sz_buf[len++]=0x00; sz_buf[len++]=0x02; } printf("-----------------\n"); if(send(s_sock5_tcp,sz_buf,len,0)==-1) { close(s_sock5_tcp); printf("send error"); return -1; } else { printf("---333-----\n"); } #if 0 int nfds=0; fd_set readfds; struct timeval timeout; FD_ZERO(&readfds); FD_SET(s_sock5_tcp, &readfds); timeout.tv_sec = 10; timeout.tv_usec = 0; nfds = s_sock5_tcp + 1; nfds = select(nfds, &readfds, NULL, NULL, &timeout); if (nfds > 0) { } char resNego[2]; memset(resNego,0,2); int nRcvd=0,nCount=0; while(1) { if(FD_ISSET(s_sock5_tcp,&readfds)) { //接收sock[0]发送来的数据 do { nRet = recv(s_sock5_tcp, (char*)resNego+nRcvd, 2-nRcvd,0); if(nRet==SOCKET_ERROR) { perror("recv error "); } nRcvd += nRet; } while((nRcvd!=2)&&(++nCount<1000)); if(nRcvd==2) { break; } } if(nCount++>=2000) { peeor("recv "); } } if(resNego[0]!=0x05 || (resNego[1]!=0x00 && resNego[1]!=0x02)) { perror(""); } #endif memset(sz_buf,0,ZF5_BUFFER_SIZE2); if(recv(s_sock5_tcp,sz_buf,ZF5_BUFFER_SIZE2,0)==-1) { close(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } /* * +----+------+----------+------+----------+ |VER | ULEN | UNAME | PLEN | PASSWD | +----+------+----------+------+----------+ | 1 | 1 | 1 to 255 | 1 | 1 to 255 | +----+------+----------+------+----------+ * */ if(sz_buf[0]==0x05&&sz_buf[1]==0x02)//auth with password { int sndlen=0; memset(sz_buf,0,ZF5_BUFFER_SIZE2); sz_buf[sndlen++]=0x01; sz_buf[sndlen++]=strlen(USER_NAME); memcpy((void *)&(sz_buf[sndlen]),(void *)USER_NAME,strlen(USER_NAME)); sndlen+=strlen(USER_NAME); sz_buf[sndlen++]=strlen(USER_PASSWORD); memcpy((void *)&(sz_buf[sndlen]),(void *)USER_PASSWORD,strlen(USER_PASSWORD)); sndlen+=strlen(USER_PASSWORD); if(send(s_sock5_tcp,sz_buf,sndlen,0)==SOCKET_ERROR) { closesocket(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } /* check username and password The server verifies the supplied UNAME and PASSWD, and sends the following response: +----+--------+ |VER | STATUS | +----+--------+ | 1 | 1 | +----+--------+ A STATUS field of X'00' indicates success. If the server returns a `failure' (STATUS value other than X'00') status, it MUST close the connection. * */ memset(sz_buf,0,ZF5_BUFFER_SIZE2); if(recv(s_sock5_tcp,sz_buf,ZF5_BUFFER_SIZE2,0)==SOCKET_ERROR) { closesocket(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } if(sz_buf[0]!=0x01||sz_buf[1]!=0x00) { closesocket(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } } if(sz_buf[0]==0x05&&sz_buf[1]==0x00)//auth no password { printf("auth no password ok"); } //前面是认证过程,接着是链路的链路的建立过程 /* 客户端会用通过认证的这个TCP连接发送UDP穿透请求,信令格式如下: +----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ client的本地地址即端口,这个很重要,要填客户端想发送/接收UDP包的本地端口 Where: o VER protocol version: X'05' o CMD o CONNECT X'01' o BIND X'02' o UDP ASSOCIATE X'03' o RSV RESERVED o ATYP address type of following address o IP V4 address: X'01' o DOMAINNAME: X'03' o IP V6 address: X'04' o DST.ADDR desired destination address o DST.PORT desired destination port in network octet order client的本地地址即端口 */ memset(sz_buf,0,ZF5_BUFFER_SIZE2); sz_buf[0]=0x05; sz_buf[1]=0x03; sz_buf[2]=0x00; sz_buf[3]=0x01; printf("---7777777777-----\n"); int nAddr = inet_addr( UDP_CLIENT_IPADDR ); short nPort = htons( (short)UDP_CLIENT_PORT ); memcpy( &sz_buf[4], (void *)&nAddr, 4 ); //client的本地地址即端口 memcpy( &sz_buf[8], (void *)&nPort, 2 ); //client的本地地址即端口 if(send(s_sock5_tcp,sz_buf,10,0)==SOCKET_ERROR) { closesocket(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } memset(sz_buf,0,ZF5_BUFFER_SIZE2); if(recv(s_sock5_tcp,sz_buf,ZF5_BUFFER_SIZE2,0)==SOCKET_ERROR) { closesocket(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } if(sz_buf[0]!=0x05||sz_buf[1]!=0x00) { closesocket(s_sock5_tcp); printf("--line--- %d \n",__LINE__); return -1; } else { printf("receive response ok --line--- %d \n",__LINE__); } /* // 接收回应。 /** +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ Where: o VER protocol version: X'05' o REP Reply field: o X'00' succeeded o X'01' general SOCKS server failure o X'02' connection not allowed by ruleset o X'03' Network unreachable o X'04' Host unreachable o X'05' Connection refused o X'06' TTL expired o X'07' Command not supported o X'08' Address type not supported o X'09' to X'FF' unassigned o RSV RESERVED o ATYP address type of following address o IP V4 address: X'01' o DOMAINNAME: X'03' o IP V6 address: X'04' o BND.ADDR 此UDP穿透通道对应的代理服务器地址。 o BND.PORT 此UDP穿透通道对应的代理服务器端口。 至此,UDP穿透通道已经被建起来了,客户端只要按标准格式将UDP包发往上述地址端口,UDP包就会被代理服务器转发出去。 m_strIPProxyReply = inet_ntoa( *(IN_ADDR*)(&abyUdpAssociateBuf[4]) ); m_sPortProxyReply = ntohs( *(short*)( &abyUdpAssociateBuf[8] ) ); */ if(sz_buf[3]==0x01) { printf("ip\n"); struct sockaddr_in sockaddr; char cmdbuf[500]; memset(cmdbuf,0,sizeof(cmdbuf)); proxyReplyIp = inet_ntoa( *(struct in_addr *)(&sz_buf[4]) ); proxyReplyPort = ntohs( *(short*)( &sz_buf[8] ) ); int nProxyIp = inet_addr( proxyReplyIp ); sockaddr.sin_family = AF_INET; sockaddr.sin_addr.s_addr = nProxyIp; sockaddr.sin_port = htons( proxyReplyPort); printf("ip:%s port:%d\n",proxyReplyIp,proxyReplyPort); //添加iptables规则,使目的地址即192.168.1.0/24为源Ip的数据包的目的地址都改为:代理服务器的IP地址及port. //sprintf(cmdbuf,"iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p udp --sport 5555 -j DNAT --to %s:%d",proxyReplyIp,proxyReplyPort); sprintf(cmdbuf,"iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p udp -j DNAT --to %s:%d",proxyReplyIp,proxyReplyPort); printf("%s\n",cmdbuf); system(cmdbuf); sprintf(cmdbuf,"echo %d > /proc/sock5_udp/sock5_proxy_port",proxyReplyPort); printf("%s\n",cmdbuf); system(cmdbuf); system("echo 1 > /proc/sock5_udp/sock5_udp_link_status"); printf("echo 1 > /proc/sock5_udp/sock5_udp_link_status\n"); //---------------------------------------------------------------------------- struct sigaction sa; sa.sa_handler = sigHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGKILL, &sa, NULL); while(1) { sleep(1); } } else if(sz_buf[3]==0x03) { printf("domain \n"); } else { closesocket(s_sock5_tcp); printf("haha--------------------\n"); return -1; } }
router上面打印驱动层信息
今天测试的时候,花了半天的时间,就是不能正常通信,最后分析原因:首先看看proxy server eth1上面的数据包(在下面),当时也不知道怎么整,源port为:5555。自己没有添加iptables规则更改源port为5555.。下面的iptable规则是后来添加的:可能跟后面的数据包对不上,原理是一样的。
/proc/sock5_udp # iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DNAT udp -- 192.168.1.0/24 anywhere udp spt:7777 to:192.168.100.10:55061
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
SNAT udp -- anywhere anywhere udp spt:7777 to:192.168.100.20:5555
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
/proc/sock5_udp # ls
sock5_proxy_port sock5_udp_server_ip
sock5_udp_link_status sock5_udp_server_port
/proc/sock5_udp # cat sock5_proxy_port
55061
/proc/sock5_udp # cat sock5_udp_server_ip
10.10.98.2
/proc/sock5_udp # cat sock5_udp_server_port
9999
/proc/sock5_udp # cat sock5_udp_link_status
router eth1上面抓包:
proxy server eth0上面抓包,主要查看数据包的长度为11和10
proxy server eth1上面抓包,主要查看数据包的长度为21和20,可以看看UDP包头之后就是10个自己的sock5的头。
在udp server 端的数据为:
PC端的数据为:
int sock5_udp_hard_start(struct sk_buff *skb, struct net_device *dev) { unsigned char * sock5_header_data; struct udp_sock5_header *sock5_header=NULL; struct iphdr *iph; struct udp_hdr *udph; struct ethhdr *macHeader; int macHeaderLen = ETH_HLEN; int ipHeaderLen = 0 ; macHeader= (struct ethhdr *)(skb->data); if(0x8100==macHeader->h_proto) { macHeaderLen+=4; //vlan len } iph=(struct iphdr *)(skb->data+macHeaderLen); ipHeaderLen=iph->ihl<<2;//IP_HEAD_LEN if(IPPROTO_UDP == iph->protocol) { if(sock5_udp_link_status == 1) { udph = (struct udp_hdr *)(skb->data + macHeaderLen + ipHeaderLen); if(udph->dest ==sock5_proxy_port) { #if DEBUG_SOCK5_UDP if(skb->len > SOCK5_UDP_MAX_SKB_LEN) { printk("ERROR data len too large ..................\n"); } else { if ((skb->data - skb->head) < SOCK5_ADD_UDP_HREAD_LEN) { pskb_expand_head(skb, skb->data - skb->head + SOCK5_ADD_UDP_HREAD_LEN, 0, GFP_ATOMIC); printk("---------pskb_expand_head-------------\n"); } skb_push(skb, SOCK5_ADD_UDP_HREAD_LEN); memcpy(skb->data, skb->data + SOCK5_ADD_UDP_HREAD_LEN, macHeaderLen + ipHeaderLen + UDP_HEAD_LEN); #if 1 sock5_header_data = (unsigned char *)skb->data + ETH_HLEN + IP_HEAD_LEN + UDP_HEAD_LEN; // sock5_header_data[0]=0x00; sock5_header_data[1]=0x00; sock5_header_data[2]=0x00; sock5_header_data[3]=0x01; memcpy(&sock5_header_data[4],(void *)&udp_server_ipaddr,4); memcpy(&sock5_header_data[8],(void *)&udp_server_port, 2); #endif //new udp header udph = (struct udp_hdr *)(skb->data + macHeaderLen + ipHeaderLen); udph->len = htons(skb->len - macHeaderLen - ipHeaderLen); udph->check = 0; //new ip header iph=(struct iphdr *)(skb->data + macHeaderLen); iph->tot_len = htons(skb->len - macHeaderLen); // ip_send_check(iph); print_ip_buffer(skb, (char *)"sock5_udp_hard_start", __LINE__); } #endif } } } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) return athr_gmac_hard_start(skb,dev); #else return ag934x_hard_start(skb,dev); #endif }
int sock5_udp_header_remove(struct sk_buff *skb) { int macHeaderLen = ETH_HLEN; int headerLen =0; int ipHeaderLen = IP_HEAD_LEN ; struct udp_hdr *udph=NULL; struct ethhdr *macHeader=NULL; struct iphdr * iph=NULL; macHeader= (struct ethhdr *)(skb->data); if(0x8100==macHeader->h_proto) { macHeaderLen+=4; //vlan len } headerLen= macHeaderLen + IP_HEAD_LEN + UDP_HEAD_LEN; iph = (struct iphdr *)(skb->data + macHeaderLen); if(IPPROTO_UDP == iph->protocol) { if(sock5_udp_link_status == 1) { #if DEBUG_SOCK5_UDP udph = (struct udp_hdr *)(skb->data + macHeaderLen + ipHeaderLen); if(udph->source == sock5_proxy_port) { memcpy(skb->data+headerLen, skb->data + headerLen + SOCK5_ADD_UDP_HREAD_LEN, udph->len-SOCK5_ADD_UDP_HREAD_LEN); udph->len = htons(skb->len - macHeaderLen - IP_HEAD_LEN - SOCK5_ADD_UDP_HREAD_LEN); udph->check = 0; skb_trim(skb,skb->len-SOCK5_ADD_UDP_HREAD_LEN); iph=(struct iphdr *)(skb->data + macHeaderLen); iph->tot_len = htons(skb->len - macHeaderLen); // ip_send_check(iph); print_ip_buffer(skb, (char *)"sock5_udp_header_remove", __LINE__); } #endif } } }