简体   繁体   English

当我使用原始套接字发送syn-packet时,为什么服务器不响应syn-ack数据包?

[英]Why does not the server respond with syn-ack packets when I send syn-packets with raw sockets?

I am experimenting with raw sockets and I have just written a small program that sends TCP packets with the syn flag set. 我正在尝试使用原始套接字,我刚刚编写了一个小程序,它发送带有syn标志集的TCP数据包。 I can see the packets coming with Wireshark on the server side and they look good, but the server never responds with any syn-ack packets. 我可以在服务器端看到与Wireshark一起发送的数据包,它们看起来不错,但是服务器永远不会响应任何syn-ack数据包。

I have compared the syn packets that my program constructs (see code below) with the ones that hping3 sends (because the packets of hping3 always get a syn-ack ). 我已经将我的程序构造的syn数据包(见下面的代码)与hping3发送的数据包进行了比较(因为hping3的数据包总是得到一个syn-ack )。 The only that differs between my syn packets and hping3's syn packets are the ip identification number, tcp source port (which is randomized in hping3), tcp sequence number (which is also randomized in hping3) and the ip checksum field. 我的syn包和hping3的syn包之间唯一不同的是ip identification号, tcp source port (在hping3中随机化), tcp sequence number (在hping3中也随机化)和ip checksum字段。 All these four fields are based on some random numbers, that is why they differ. 所有这四个字段都基于一些随机数,这就是它们不同的原因。 All other fields are equal! 所有其他领域都是平等的! But my program does not get any syn-acks but hping3 does! 但我的程序没有得到任何同步,但hping3呢!

I am using Kali Linux for sending the packets (of course as root) and CentOS for the server. 我正在使用Kali Linux发送数据包(当然是root用户)和CentOS用于服务器。

Have I missed something essential or just missunderstood anything? 我错过了必要的东西或者只是误解了什么吗?

Removed code 删除了代码

EDIT 编辑
Here is the entire packet captured by Wireshark on the client side (divided into 4 images below). 这是Wireshark在客户端捕获的整个数据包(下面分为4个图像)。 Note that the packets sent by hping3 are totally equal except the values for ip identification, source port, sequence number and checksum: 请注意,除了ip标识,源端口,序列号和校验和的值之外,hping3发送的数据包完全相同:

Images removed 图像已删除


Here is the hex dump of the packet. 这是数据包的十六进制转储。

Hexdump removed Hexdump已移除

EDIT 2 编辑2

Ok, now I have created the pseudo header according to RFC793 . 好的,现在我已根据RFC793创建了伪标头。 The pseudo header is just used for the tcp checksum calculation. 伪标头仅用于tcp校验和计算。 Now the IP header seems to be correct, but Wireshark complains about that the packet does not contain a full TCP header and it really seems corrupted because some of the fields contains strange values that I have not set. 现在IP头似乎是正确的,但Wireshark抱怨该数据包不包含一个完整的TCP头,它真的好像已经损坏,因为一些字段包含我没有设置的奇怪值。

First I allocate a buffer (called tcp_header ) with space for the tcp header and the pseudo header. 首先,我为tcp头和伪头分配一个带空格的缓冲区(称为tcp_header )。 Second, I create a buffer for the ip header containing space for ip, tcp and pseudo headers. 其次,我为包含ip,tcp和伪标头空间的ip头创建了一个缓冲区。
First I fill the tcp_header with its data and then I copy it to the ip_header before sending it with the sendto function. 首先,我用tcp_header填充其数据,然后将其复制到ip_header然后再发送sendto函数。

Does something go wrong when I copy the contents of tcp_packet to ip_packet or am I doing something else wrong? 当我将tcp_packet的内容tcp_packetip_packet或者我做错了什么时,会出现问题吗?

#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#define __FAVOR_BSD 1
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <string.h>
#include <iostream>
#include <net/ethernet.h>
#include <time.h>

#define PCKT_LEN 1024

struct pseudohdr
{
    __u32 saddr;
    __u32 daddr;
    __u8  zero;
    __u8  protocol;
    __u16 lenght;
};

#define PSEUDOHDR_SIZE sizeof(struct pseudohdr)

unsigned short csum(unsigned short *buf, int len) {
    unsigned long sum;
    for(sum=0; len>0; len-=2)
        sum += *buf++;
    sum = (sum >> 16) + (sum &0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}


int main(int argc, char** argv) {

    srand(time(NULL));

    char *ip_packet = new char[sizeof(struct iphdr) + 
                               sizeof(struct tcphdr)]();

    char *tcp_packet = new char[sizeof(struct pseudohdr) + 
                                sizeof(struct tcphdr)]();

    struct pseudohdr *pseudoheader = (struct pseudohdr*) tcp_packet;
    class tcphdr *tcp = (struct tcphdr *) (tcp_packet + sizeof(struct pseudohdr));
    class iphdr *ip = (struct iphdr *) ip_packet;


    class sockaddr_in sin, din;

    int sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if(sd < 0) {
       perror("socket() error");
       exit(-1);
    } else {
        printf("socket()-SOCK_RAW and tcp protocol is OK.\n");
    }

    // Randomize src port
    int srcport = rand()%100+25000;

    sin.sin_family = AF_INET;           // Address family
    sin.sin_addr.s_addr = inet_addr("192.168.2.80");
    sin.sin_port = htons(srcport); // Source port

    din.sin_family = AF_INET;
    din.sin_addr.s_addr = inet_addr("192.168.2.6");
    din.sin_port = htons(80); // Destination port

    /* tcp pseudo header */
    memcpy(&pseudoheader->saddr, &sin.sin_addr.s_addr, 4);
    memcpy(&pseudoheader->daddr, &din.sin_addr.s_addr, 4);
    pseudoheader->protocol      = 6; /* tcp */
    pseudoheader->lenght        = htons(sizeof(struct pseudohdr) + sizeof(struct tcphdr));


    ip->ihl = 5;
    ip->version = 4;
    ip->tos = 0;
    ip->tot_len = sizeof(class iphdr) + sizeof(class tcphdr);
    ip->id = htons((getpid() & 255) + rand()%100+30000);
    ip->frag_off = 0;
    ip->ttl = 32;
    ip->protocol = 6; // TCP
    ip->check = 0; // Done by kernel
    memcpy(&ip->saddr, (char*)&sin.sin_addr, sizeof(ip->saddr));
    memcpy(&ip->daddr, (char*)&din.sin_addr, sizeof(ip->daddr));


    // The TCP structure
    tcp->th_sport = htons(srcport);
    tcp->th_dport = htons(80);      // Destination port
    tcp->th_seq = htonl(rand()%100+1000);
    tcp->th_ack = htonl(rand()%30);
    tcp->th_off = 5;
    tcp->th_flags = TH_SYN;
    tcp->th_win = htons(1024);
    tcp->th_urp = 0;

    // Now calculate tcp checksum
    tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr));

    // Copy tcp_packet to ip_packet
    memcpy(ip_packet + sizeof(struct iphdr), tcp_packet+sizeof(struct pseudohdr), sizeof(struct tcphdr));

    // Bind socket to interface
    int one = 1;
    const int *val = &one;
    const char opt[] = "eth0";

    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) < 0) {
        perror("setsockopt() error");
        exit(-1);
    }
    else
        printf("setsockopt() is OK\n");

    if(sendto(sd, ip_packet, ip->tot_len, 0, (sockaddr*)&din, sizeof(din)) < 0) {
       perror("sendto() error");
       exit(-1);
    }
    else
        printf("Send OK!");

    close(sd);
    return 0;
}

The tcp contents of the packet: 数据包的tcp内容:

Images removed 图像已删除

Edit 3 编辑3

Now I have found something interesting. 现在我发现了一些有趣的东西。 Study the cheksums on this picture: 研究这张照片上的cheksums: 在此输入图像描述

The checksum is in network order and shall thus be read in reversed order, as 0x06c0 (and not is as it is stated above as 0xc006). 校验和按网络顺序排列,因此应按相反的顺序读取,如0x06c0 (而不是如上所述为0xc006)。 That is equal to the decimal value of 1728 . 这等于1728的十进制值。 Wireshark says the correct cheksum should be 0x12c0 which gives a decimal value of 4800 . Wireshark说正确的cheksum应该是0x12c0 ,它给出一个4800的十进制值。
4800-1728=3072 . 4800-1728=3072 That is the difference between the actual checksum and the correct checksum calculated by Wireshark in all packets that is sent by my program. 这是我的程序发送的所有数据包中Wireshark计算的实际校验和与正确校验和之间的差异。

So, if I simply add that value to the cheksum result: 所以,如果我只是将该值添加到cheksum结果中:

tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr)) + 3072;

...then all packets get the correct checksum and receives a corresponding SYN-ACK . ...然后所有数据包都获得正确的校验和并收到相应的SYN-ACK

Why the magic number 3072??? 为什么神奇数字3072 ???

I am not content with the check sum algorithm you are using. 我不满足于您使用的校验和算法。 The one suggested by Stevens: 史蒂文斯建议的那个:

uint16_t
in_cksum(uint16_t *addr, int len)
{
        int                     nleft = len;
        uint32_t                sum = 0;
        uint16_t                *w = addr;
        uint16_t                answer = 0;

        /*
         * Our algorithm is simple, using a 32 bit accumulator (sum), we add
         * sequential 16 bit words to it, and at the end, fold back all the
         * carry bits from the top 16 bits into the lower 16 bits.
         */
        while (nleft > 1)  {
                sum += *w++;
                nleft -= 2;
        }

                /* 4mop up an odd byte, if necessary */
        if (nleft == 1) {
                *(unsigned char *)(&answer) = *(unsigned char *)w ;
                sum += answer;
        }

                /* 4add back carry outs from top 16 bits to low 16 bits */
        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
        sum += (sum >> 16);                     /* add carry */
        answer = ~sum;                          /* truncate to 16 bits */
        return(answer);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 通过原始套接字接收“ syn”数据包时,如何响应“ syn ack”数据包? - How do I respond with a “syn ack” packet when receiving a “syn” packet through a raw socket? 在Linux中拦截/重新路由TCP SYN数据包到C ++程序 - Intercepting/Rerouting TCP SYN packets to C++ program in linux 如何使用winsock中的原始套接字发送tcp数据包? - How to send tcp packets using raw sockets in winsock? 原始套接字在C ++中发送TCP SYN-FIN- .. - Raw Socket send TCP SYN-FIN-.. in c++ 为什么将TCP数据包发送到本地主机需要这么长时间? - Why does it take so long to send TCP packets to localhost? TCP 服务器挂起,不响应 SYN - TCP server hangs, not responding to SYN 发送带有syn标志设置的原始tcp数据包只是通过lo接口,而不是我想要的eth0 - Sending a raw tcp packet with syn flag set just goes through the lo interface, not eth0 as I want 发送任意(原始)数据包 - Sending arbitrary (raw) packets 为什么在使用winsock时为什么没有收到UDP数据包? - Why aren't I receiving the UDP packets when using winsock? 我正在从localhost向localhost发送UDP数据包,并且有时会丢弃数据包。 我如何阻止这种情况以及为什么会发生这种情况? - I am sending UDP packets from localhost to localhost and runs on packets sometimes drop. How do I stop this and why does it happen?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM