raw socket (续)

最近在学习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,sizeof(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值