【问题标题】:How can I set the TCP options for sending packets?如何设置发送数据包的 TCP 选项?
【发布时间】:2012-08-31 08:23:40
【问题描述】:

我正在编写可以向主机发送 tcp 数据包的软件。

我能够创建带有 IP 标头、TCP 标头和数据的数据包,但我无法管理如何添加 TCP 选项,例如 MSS、NOP、STACK、窗口缩放或时间戳。

我的意思是我无法向 TCP 标头添加选项,计算正确的校验和以向主机发送良好的 TCP 数据包。

我可以在没有 TCP 选项的情况下发送正确的 TCP 数据包。

你认为我在正确的补丁上吗?有人可以帮帮我吗?

/* TCP Header structure */
struct tcphdr
{
    u_int16_t   th_sport;           /* source port */
    u_int16_t   th_dport;           /* destination port */
    u_int32_t   th_seq;             /* sequence number */
    u_int32_t   th_ack;             /* acknowledgement number */
#if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int8_t    th_x2:4;            /* (unused) */
    u_int8_t    th_off:4;           /* data offset */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
    u_int8_t    th_off:4;           /* data offset */
    u_int8_t    th_x2:4;            /* (unused) */
#endif
    u_int8_t    th_flags;
    # define    TH_FIN        0x01
    # define    TH_SYN        0x02
    # define    TH_RST        0x04
    # define    TH_PUSH       0x08
    # define    TH_ACK        0x10
    # define    TH_URG        0x20
    # define    TH_ECE        0x40
    # define    TH_CWR        0x80
    u_int16_t   th_win;             /* window */
    u_int16_t   th_sum;             /* checksum */
    u_int16_t   th_urp;             /* urgent pointer */
};

struct tcp_option_mss
{
    uint8_t     kind;               /* 2 */
    uint8_t     len;                /* 4 */
    uint16_t    mss;
}           __attribute__((packed));

struct tcphdr_mss
{
    struct tcphdr       tcphdr;
    struct tcp_option_mss   mss;
};

/* IP Header structure */

struct ip
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int    ip_hl:4;               /* header length */
    unsigned int    ip_v:4;                /* version */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned int    ip_v:4;                /* version */
    unsigned int    ip_hl:4;               /* header length */
#endif
    u_int8_t    ip_tos;                    /* type of service */
    u_short     ip_len;                     /* total length */
    u_short     ip_id;                      /* identification */
    u_short     ip_off;                     /* fragment offset field */
    # define    IP_RF 0x8000                /* reserved fragment flag */
    # define    IP_DF 0x4000                /* dont fragment flag */
    # define    IP_MF 0x2000                /* more fragments flag */
    # define    IP_OFFMASK 0x1fff           /* mask for fragmenting bits */
    u_int8_t    ip_ttl;                    /* time to live */
    u_int8_t    ip_p;                      /* protocol */
    u_short     ip_sum;                     /* checksum */
    struct in_addr  ip_src, ip_dst;      /* source and dest address */
};


int send_packet(int sock, long dest_ip , long source_ip, long port, u_int8_t th_flags, unsigned long seq, unsigned long ack, unsigned long port1, unsigned char * data, unsigned long data_i)
{
    char                    *   packet;

    struct ip               *   pkt_ip;
    struct tcphdr               *   pkt_tcp;
    struct tcphdr_mss           *   tcp_header;
    struct sockaddr_in              sin;

    packet = malloc(sizeof(struct ip) + sizeof(struct tcphdr_mss) + data_i);

    if (packet == NULL)
    {
        if (ECHO)
            fprintf(stderr, "Error in allocating memory\n");
        exit(EXIT_FAILURE);
    }

    memset(packet, 0, sizeof(struct ip) + sizeof(struct tcphdr_mss));

    pkt_ip              = (struct ip *)     packet;
    pkt_tcp             = (struct tcphdr *) (packet + sizeof(struct ip));

    pkt_tcp->th_sport       = htons(port1);
    pkt_tcp->th_dport       = htons(port);
    pkt_tcp->th_seq         = htonl(seq);
    pkt_tcp->th_ack         = htonl(ack);
    pkt_tcp->th_off         = sizeof(struct tcphdr) / 4 + 1;
    pkt_tcp->th_flags       = th_flags;
    pkt_tcp->th_win         = htons(32768);
    pkt_tcp->th_sum         = 0;

    tcp_header          = malloc(sizeof(struct tcphdr));
    tcp_header->tcphdr      = *pkt_tcp;
    tcp_header->mss.kind        = 2;
    tcp_header->mss.len     = 4;
    tcp_header->mss.mss     = htons(32000);

    pkt_ip->ip_v            = 4;
    pkt_ip->ip_hl           = sizeof(struct ip) >> 2;
    pkt_ip->ip_tos          = 0;
    pkt_ip->ip_len          = htons(sizeof(struct ip) + sizeof(struct tcphdr) + data_i);

    if (ipid > 65000)
        ipid = 0;
    ipid++;
    pkt_ip->ip_id           = ipid;
    pkt_ip->ip_off          = 0;
    pkt_ip->ip_ttl          = 64;
    pkt_ip->ip_p            = IPPROTO_TCP ;
    pkt_ip->ip_sum          = 0;
    pkt_ip->ip_src.s_addr       = source_ip;
    pkt_ip->ip_dst.s_addr       = dest_ip;

    pkt_ip->ip_sum          = checksum((unsigned short*)pkt_ip, sizeof( struct ip) );
    pkt_tcp->th_sum         = in_cksum_tcp(pkt_ip->ip_src.s_addr, pkt_ip->ip_dst.s_addr, (unsigned short *) pkt_tcp, sizeof(struct tcphdr_mss), data, data_i);

    memcpy(((char *)pkt_tcp + sizeof(struct tcphdr_mss)), data, data_i);

    memset(&sin, 0, sizeof(sin));
    sin.sin_family          = AF_INET;
    sin.sin_addr.s_addr     = pkt_ip->ip_dst.s_addr;

    if (sendto(sock, packet, sizeof(struct ip) + sizeof(struct tcphdr_mss) + data_i, 0, (struct sockaddr *) &sin, sizeof(sin)) < 0)
    {
        perror("sendto");
        free(packet);
        return -1;
    }

    free(packet);

    return 0;
}

【问题讨论】:

  • 定义“无法管理”。这里还没有真正的问题。
  • 您是否尝试发送原始 IP 数据包只是来设置这些选项?
  • 我正在尝试发送带有建立连接选项的 TCP 数据包。
  • _RFC 1071, Computing the Internet Checksum,第 4.1 节有用于创建 Internet 校验和的 C 代码。

标签: c networking tcp


【解决方案1】:

行内:

   pkt_tcp->th_sum         = in_cksum_tcp(pkt_ip->ip_src.s_addr, pkt_ip->ip_dst.s_addr, (unsigned short *) pkt_tcp, sizeof(struct tcphdr_mss), data, data_i);

根据我在野外看到的in_cksum_tcp函数:

unsigned short in_cksum_tcp(int src, int dst, unsigned short *addr, int len, unsigned char * data, int data_i)

您传递的是选项标头的大小 (sizeof(struct tcphdr_mss)),而不是完整 TCP 标头的大小 (sizeof (tcphdr) + sizeof(tcphdr_mss))。我认为这可能是问题所在(您没有正确计算 TCP 校验和)。

检查创建原始数据包的问题的一个好方法是将带有 libpcap 的数据包保存到 pcap 文件并使用 wireshark 打开。您可以轻松检查数据包的完整性。

【讨论】:

    【解决方案2】:

    以下是计算数据包正确校验和的函数

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

    调用该函数以获取 ip 和 tcp 的校验和。 除此之外,为了使用诸如 MSS、NOP、STACK 之类的选项,您需要在 TCP 标头中声明所有这些。当您在程序中使用所有这些时,您必须相应地设置 th_off 的值

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-26
      • 2017-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-15
      相关资源
      最近更新 更多