手动完成3路握手?

时间:2018-01-23 18:27:01

标签: c sockets networking tcp

准确地说,我有一个简单的程序,它可以制作TCP syn数据包并将其发送到远程主机。

代码TCP.c:

//Data part
data = datagram + sizeof(struct iphdr) + sizeof(struct tcphdr);
strcpy(data, datagram);
//some address resolution
strcpy(source_ip , sourceip);
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr.s_addr = inet_addr (destinationip);

//Fill in the IP Header
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
iph->id = htonl (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_TCP;
iph->check = 0;      //Set to 0 before calculating checksum
iph->saddr = inet_addr ( source_ip );    //Spoof the source ip address
iph->daddr = sin.sin_addr.s_addr;

//Ip checksum
iph->check = csum ((unsigned short *) datagram, iph->tot_len);

//TCP Header
tcph->source  = htons (sourceport);
tcph->dest    = htons (destinationport);
tcph->seq     = htonl (seqn);
tcph->ack_seq = htonl (ackn);
tcph->doff    = 5;  //tcp header size
tcph->fin     = fin;
tcph->syn     = syn;
tcph->rst     = rst;
tcph->psh     = push;
tcph->ack     = ack;
tcph->urg     = urg;
tcph->window  = htons (win); /* 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) + strlen(data) );

int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
pseudogram = malloc(psize);

memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));

tcph->check = csum( (unsigned short*) pseudogram , psize);

//IP_HDRINCL to tell the kernel that headers are included in the packet
int one = 1;
const int *val = &one;

if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
{
    perror("Error setting IP_HDRINCL");
    exit(0);
}
while (1)
{
    //Send the packet
    if (sendto (s, datagram, iph->tot_len ,  0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
    {
        perror("sendto failed");
    }
    //Data send successfully
    else
    {
        printf ("  Packet Send. Length : %d \n" , iph->tot_len);
    }
sleep(2);
}

到目前为止它工作得很好,我可以在源机器中看到wireshark生成一个syn数据包并将其发送到我的目标机器。

在我的目标主机中,我有一个侦听端口8000的简单服务器。

我基本上做的是使用这个TCP数据包生成器我正在向我的服务器发送一个syn数据包,然后服务器用syn,ack响应,但是然后源机器(上面的程序)发回第一个。这不能完成3次握手。

我想要做的是使用我的TCP.c程序完成3次握手。

如果你能告诉我该怎么做,我真的很感激吗?我是否使用recv()来接收syn / ack,然后将新数据包制作为ack并用它响应或响应什么?

问候:)

1 个答案:

答案 0 :(得分:2)

正如Ron Maupin指出的那样,返回的SYN / ACK由本地IP栈处理,因为它属于TCP。由于没有半开插座,因此会发回RST。

如果你确实想要接管低级TCP,你需要

  1. 防止SYN / ACK到达本地IP堆栈(通过pcap或类似的数据包捕获和过滤的组合 - 最简单的方法)
  2. 通过从NIC解除IP协议绑定来完全停用IP堆栈(很难 - 您需要模拟完整堆栈)
  3. 或注册为TCP的协议处理程序(很难 - 你需要融入堆栈; thx Luis)。
  4. 修改

    为了能够取消配置IP堆栈,有两种方法(其中一种方法实际上是不可行的,因为缺少tcp / ip协议的系统达到了几乎无法使用的状态):

    • 通过重新配置内核并完全禁用tcp模块来停用全局tcp。正如我之前所说,这通常是不可行的。

    • 停用网卡上的IP。您可以通过不在网卡上配置任何IP地址来执行此过程,因此它通过丢弃数据包来处理协议,您只能通过使用RAW套接字附加到协议来访问数据包。几乎所有这些技术都需要root访问权限,因此对普通用户进程隐藏。配置网卡的IP地址时,将其连接到IP。遗憾的是,仅仅取消TCP连接是非常困难的,因为许多用于数据包调度的原始NetBSD回调都是硬编码的,并且网络代码的许多功能和效率来自硬连线。只有重新编译没有TCP支持的内核才能实现这一目标。