您的位置:首页 > 其它

raw socket (续)

2016-07-20 13:05 363 查看
最近在学习raw socket,上一篇文章也成功发送了SYN,使得服务端的状态变为SYN_RECV。

http://blog.csdn.net/lizhia1221/article/details/51946592

因此,就想尝试去模拟TCP三次握手,无非就是发送三个数据包嘛,想想好像挺简单的,然后瞬间打脸了。

下面是测试代码:

/*
模拟tcp三次握手,然后收到syn+ack之后,内核也会处理包,
自动发送rst,因此目前虽然可以模拟三次握手,但是提前被内
核结束了,无法建立连接
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#define DEST_PORT 8888
#define SRC_PORT 9999
#define SRC_ADDRESS "192.168.0.174"
#define DEST_ADDRESS "192.168.0.174"

//tcp 伪首部
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
/*
Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes)
{
register long sum;
unsigned short oddbyte;
register short answer;

sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}

sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;

return(answer);
}

int get_ack_seq(unsigned char* buffer,int size,unsigned int &seq);
int fill_packet(char *datagram,int dsize,unsigned char* buffer,int bsize,unsigned int ack_seq);

int connect()
{
int fd=socket(PF_INET,SOCK_RAW,IPPROTO_TCP);
if(fd==-1)
{
perror("Failed to create socket");
exit(1);
}

//Datagram to represent the packet
char datagram[4096],source_ip[32],*pseudogram;
memset(datagram,0,sizeof(datagram));
//IP Header
struct iphdr *iph=(struct iphdr*)datagram;
//TCP Header
struct tcphdr *tcph=(struct tcphdr*)(datagram+sizeof(struct iphdr));
struct pseudo_header psh;
//socket address
struct sockaddr_in sin;

strcpy(source_ip,SRC_ADDRESS);
sin.sin_family=AF_INET;
sin.sin_port=htons(DEST_PORT);
sin.sin_addr.s_addr=inet_addr(DEST_ADDRESS);

//Fill in the IP Header
iph->ihl=5;// 20 byte
iph->version=4;//ipv4
iph->tos=0;
iph->tot_len=sizeof(struct iphdr)+sizeof(struct tcphdr);
iph->id=htonl(54321);//ID
iph->frag_off=0;
iph->ttl=255;
iph->protocol=IPPROTO_TCP;
iph->check=0;
iph->saddr=inet_addr(source_ip);
iph->daddr=sin.sin_addr.s_addr;
//IP checksum
iph->check=csum((unsigned short*)datagram,sizeof(struct iphdr));
//TCP header
tcph->source = htons(SRC_PORT);
tcph->dest = htons(DEST_PORT);
tcph->seq = htonl(1);
tcph->ack_seq = htonl(0);
tcph->doff = 5; //tcp header size
tcph->fin=0;
tcph->syn=1;
tcph->rst=0;
tcph->psh=0;
tcph->ack=0;
tcph->urg=0;
tcph->window = htons (5840);    /* maximum allowed window size */
tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header
tcph->urg_ptr = 0;
//Now the TCP checksum
psh.source_address = inet_addr(source_ip);
psh.dest_address = sin.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr));
int psize=sizeof(struct pseudo_header)+sizeof(struct tcphdr);
pseudogram=(char*)malloc(psize);
memcpy(pseudogram,(char*)&psh,sizeof(struct pseudo_header));
memcpy(pseudogram+sizeof(struct pseudo_header),tcph,si
4000
zeof(struct tcphdr));
tcph->check=csum((unsigned short*)pseudogram,psize);

//TP_HDRINCL to tell the kernel that headers are included in the pakcet
int one=1;
const int *val=&one;
if(setsockopt(fd,IPPROTO_IP,IP_HDRINCL,val,sizeof(int))<0)
{
perror("Error setting IP_HDRINCL\n");
exit(0);
}

//send SYN
if(sendto(fd,datagram,iph->tot_len,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
perror("SYN send to failed\n");
}
else
{
printf("SYN Packet seq=%u \n",1);
}
//recv SYN+ACK
struct sockaddr_in saddr;
unsigned char buffer[4096];
unsigned int ack_seq,seq,data_size,saddr_len;
while(1)
{
data_size=recvfrom(fd,buffer,4096,0,(struct sockaddr*)&saddr,(socklen_t*)&saddr_len);
//printf("%d %s\n",saddr_len,inet_ntoa(saddr.sin_addr));
if(data_size<0)
continue;
//为什么saddr返回的端口号为0
if(/*saddr.sin_port!=sin.sin_port
||*/saddr.sin_addr.s_addr!=sin.sin_addr.s_addr)
continue;
ack_seq=get_ack_seq(buffer,data_size,seq);
if(ack_seq==-1||ack_seq!=2)//syn seq=1
continue;
else
{
printf("SYN+ACK Packet Get seq=%u ack=%u port=%u\n",seq,ack_seq,ntohs(saddr.sin_port));
break;
}
}
//send ACK
fill_packet(datagram,iph->tot_len,buffer,data_size,seq+1);
if(sendto(fd,datagram,iph->tot_len,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
perror("ACK send to failed\n");
}
else
{
printf("ACK Packet ack=%u \n",seq+1);
}
}
int get_ack_seq(unsigned char* buffer,int size,unsigned int &seq)
{
struct iphdr *iph=(struct iphdr*)buffer;
struct tcphdr *tcph=(struct tcphdr*)(buffer+iph->ihl*4);
if(iph->protocol!=6)
return -1;
if(tcph->syn!=1||tcph->ack!=1)
return -1;
// printf("sp=%d\n",ntohs(tcph->source));
seq=ntohl(tcph->seq);
return ntohl(tcph->ack_seq);
}
int fill_packet(char *datagram,int dsize,unsigned char* buffer,int bsize,unsigned int ack_seq)
{
struct iphdr *iph=(struct iphdr*)datagram;
struct tcphdr *tcph=(struct tcphdr*)(datagram+iph->ihl*4);
struct iphdr *b_iph=(struct iphdr*)buffer;
struct tcphdr *b_tcph=(struct tcphdr*)(buffer+b_iph->ihl*4);
char *pseudogram;
struct pseudo_header psh;
//IP Header
iph->id=htonl(54322);//ID
iph->check=0;
iph->saddr=b_iph->daddr;
iph->daddr=b_iph->saddr;
//IP checksum
iph->check=csum((unsigned short*)datagram,sizeof(struct iphdr));
//TCP Header
tcph->source = b_tcph->dest;
tcph->dest = b_tcph->source;
tcph->seq = htonl(2);
tcph->ack_seq = htonl(ack_seq);
tcph->syn=0;
tcph->ack=1;
tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header
//Now the TCP checksum
psh.source_address = b_tcph->dest;
psh.dest_address = b_tcph->source;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr));
int psize=sizeof(struct pseudo_header)+sizeof(struct tcphdr);
pseudogram=(char*)malloc(psize);
memcpy(pseudogram,(char*)&psh,sizeof(struct pseudo_header));
memcpy(pseudogram+sizeof(struct pseudo_header),tcph,sizeof(struct tcphdr));
tcph->check=csum((unsigned short*)pseudogram,psize);
}
int main()
{
connect();
}


服务端测试代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>

#define PORT 8888

int main()
{
int fd = socket(PF_INET,SOCK_STREAM,0);
if(fd<0)
{
perror("create socket error");
exit(0);
}

struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=INADDR_ANY;
if(bind(fd,(struct sockaddr*)&sin,sizeof(struct sockaddr))<0)
{
perror("socket bind error");
exit(0);
}

if(listen(fd,100)<0)
{
perror("socket listen error");
exit(0);
}
printf("start listening\n");

int clifd;
struct sockaddr_in client;
socklen_t len;
while(1)
{
if((clifd=accept(fd,(struct sockaddr*)&client,&len))<0)
{
perror("accept error");
}
else
{
printf("accept success\n");
}
}
}


运行结果:



wireshark抓包如下:



结果意外的多出了一个从9999端口到8888端口的RST包,导致TCP握手提前被终止。

百思不得其解,google查资料咯。大概意思是tcp协议栈也会处理三次握手,当内核收到SYN+ACK时并不知道raw socket发送了SYN包,因此响应RST终止连接。



目前没有找到解决方案,下面是有关该问题的一些参考资料:

http://bbs.csdn.net/topics/320208382

http://blog.chinaunix.net/uid-795807-id-3206853.html

http://forums.codeguru.com/showthread.php?320739-Visual-C-Network-Why-do-my-machine-send-an-RST-packet-in-reply-to-a-SYN-ACK-pac
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: