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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
}
}
}