【问题标题】:Crafting TCP/IP Packets制作 TCP/IP 数据包
【发布时间】:2017-02-02 14:09:55
【问题描述】:

我正在学习使用 C/C++ 进行套接字编程,我认为最好的方法是深入研究它。我可以使用 socket.h send() 将数据发送到套接字,因此我想通过制作网络数据包来更深入。

我尝试了但仍然无法确定我的数据的哪一部分是无效的,因为我得到了 Invalid argument errno 22。这是我的十六进制 IP 标头:

45 00 28 00 
d4 31 00 00 
ff 06 3c 6e 
c0 a8 01 06 
c0 a8 01 01

这是我的 TCP 标头:

00 50 00 50 
00 00 00 00 
00 00 00 00 
50 02 16 d0 
15 1b 00 00 

感谢任何提示。

注意:我正在阅读 beej.ushere 以供学习。

编辑:这是我的代码:

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;
};

int main(int argc, char* argv[]) {
    int sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
    if (sockfd == -1) {
        perror("Failed to create socket");
        exit(1);
    }

    // Datagram to represent the packet
    char datagram[4096];
    memset(datagram, 0, 4096); // zero out the packet buffer

    //Data part
    char *data = datagram + sizeof(struct ip) + sizeof(struct tcphdr);
    strcpy(data, "");

    // some address resolution
    char source_ip[32];
    strcpy(source_ip, "192.168.1.6");
    struct sockaddr_in sai;
    sai.sin_family = AF_INET;
    sai.sin_port = htons(80);

    sai.sin_addr.s_addr = inet_addr("192.168.1.1");
    cout << "sai.sin_addr.s_addr=" << sai.sin_addr.s_addr << endl;

    //Fill in the IP Header
    struct ip *iph = (struct ip *) datagram;
    iph->ip_hl = 5;
    iph->ip_v = 4;
    iph->ip_tos = 0;
    iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data);
    iph->ip_id = htons(54321);
    iph->ip_off = 0;
    iph->ip_ttl = 255;
    iph->ip_p = IPPROTO_TCP;
    iph->ip_sum = 0;
    iph->ip_src.s_addr = inet_addr(source_ip);
    iph->ip_dst.s_addr = sai.sin_addr.s_addr;

    //Ip checksum
    unsigned short checksum = csum((unsigned short *) datagram, iph->ip_len);
    iph->ip_sum = checksum;
    cout << "iph->ip_sum=" << checksum << endl;

    unsigned char *pIph = (unsigned char *) datagram;
    for (int i = 0; i < 20; i++) {
        cout << setfill('0') << setw(2) << hex << (int) pIph[i] << " ";
        if (i + 1 >= 4 && (i + 1) % 4 == 0) {
            cout << endl;
        }
    }

    //TCP Header
    struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof(struct ip));
    struct pseudo_header psh;
    tcph->th_sport = htons(80);
    tcph->th_dport = htons(80);
    tcph->th_seq = 0;
    tcph->th_ack = 0;
    tcph->th_off = 5;
    tcph->th_flags = TH_SYN;
    tcph->th_win  = htons(5840); /* maximum allowed window size */
    tcph->th_sum = 0;
    tcph->th_urp = 0;

    //Now the TCP checksum
    psh.source_address = inet_addr(source_ip);
    psh.dest_address = sai.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);

    char *pseudogram = malloc(psize);

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

    checksum = csum((unsigned short*) pseudogram, psize);
    tcph->th_sum = checksum;
    cout << "tcph->th_sum=" << checksum << endl;

    unsigned char *pTcph = (unsigned char *) tcph;
    for (int i = 0; i < 20; i++) {
        cout << setfill('0') << setw(2) << hex << (int) pTcph[i] << " ";
        if (i + 1 >= 4 && (i + 1) % 4 == 0) {
            cout << endl;
        }
    }

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

    struct sockaddr *pSa = (struct sockaddr *) &sai;

    // Send the packet
    if (sendto(sockfd, datagram, iph->ip_len, 0, pSa, sizeof(sai)) < 0) { // failed here
        perror("sendto failed");

    } else { //Data send successfully
        printf("Packet Send. Length : %d \n", iph->ip_len);
    }

    return 1;
}

【问题讨论】:

  • 您是否有理由要创建和处理自己的原始数据包?如果您只是想学习网络编程,那么 IMO 并不是一个好的开始。而是学习如何使用普通的SOCK_STREAMSOCK_DGRAM 套接字来使用标准的TCP 和UDP 协议。然后,如果您想实现自己的网络堆栈(出于某种自虐的原因),您可以这样做,但要慢慢来,一次一步,然后从顶部开始.
  • 您在哪里确切遇到errno设置为22?请在失败的地方发布您的部分代码。
  • 是的,我想制作原始数据包是有原因的。我想写一个ICMP隧道程序。

标签: c++ c sockets cygwin


【解决方案1】:

在您的 IPv4 标头中:

45 00[28 00] 
d4 31 00 00 
ff 06 3c 6e
c0 a8 01 06 
c0 a8 01 01

你的数据包长度是 10240 (0x2800) 吗?

你确定不是 40 (0x0028)?

45 00[00 28] 
d4 31 00 00 
ff 06[64 46] // checksum updated
c0 a8 01 06 
c0 a8 01 01

编辑:既然您已经发布了代码……

你应该替换:

iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data);

作者:

iph->ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data));

【讨论】:

  • 只是为了详细说明一下,标题中的字段是big endian,也就是说,对于多字节字段,最高有效字节在前。
  • 重新表述,数据包中的所有整数都按网络字节顺序。将多字节数据放入网络时,总是希望将字节顺序从host转换为network,即hton s() 函数可以。
  • 我怀疑是字节顺序错误,我仍然试图理解用 wiki 和我的主机 + cygwin 编写的区别,它使用 LE。只是想知道,标志和偏移字段小于一个字节(3 位和 13 位),应该如何构造?这些应该被视为 2 个字节吗?
猜你喜欢
  • 2012-03-30
  • 1970-01-01
  • 2016-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-20
  • 2012-03-01
  • 1970-01-01
相关资源
最近更新 更多