做 TCP 警告意味着数据包被忽略,"Wireshark (Warning/Malformed):Short segment.Segment/fragment does not contain a full TCP header (might be NMAP )"

do TCP warning means packet ignored,"Wireshark (Warning/Malformed):Short segment.Segment/fragment does not contain a full TCP header (might be NMAP )"

我正在模拟 TCP,例如任何收到的 tcp 数据包,因此应该有一个响应,为此我用 C 编写了我的服务器程序并创建了 TUN 接口,以便客户端数据包读取我的代码,我的代码的问题是只是我收到 SYN 个数据包,并且我用 SYN + ACK 个数据包响应它。序列号和端口是正确的。在 wire shark 中,我看到了我的 SYN + ACK 响应,但我的客户端继续发送 SYN 数据包并在中间路由器请求消息中,在 wire shark 中它说

Expert Info (Warning/Malformed): Short segment. Segment/fragment does not contain a full TCP header (might be NMAP or someone else deliberately sending unusual packets)

这是什么意思,我有点确定我将所有有效字段都作为值包括在内,但为什么我不断收到此警告,而我的客户端似乎忽略了我的 SYN +ACK 数据包。谁能看看这段代码

这是我的主要功能

int main(int argc, char **argv)
{
    const char *tun_ip = NULL;    /*virtual*/
    const char *remote_ip = NULL; /*physical*/
    ip4_addr_t local_ip4 = 0L;
    pthread_t tid_recv;//, tid_trans;
        void *thread_ret = NULL;
        

    _progname = argv[0];
    if (argc != 3) 
    {
        usage();
        exit(EXIT_FAILURE);
    }

    tun_ip = argv[1];
    remote_ip = argv[2];
       if (0 >= inet_pton(AF_INET, tun_ip, &local_ip4)) 
       {
        debug("%s: invalid IP address %s\n", _progname, tun_ip);
        exit(EXIT_FAILURE);
        }
    
    set_signal(SIGINT,  sigexit);
    set_signal(SIGQUIT, sigexit);

    
    _tun_fd = open_tun_iface(local_ip4);
    if (_tun_fd < 0 ) 
    {
        exit(EXIT_FAILURE);
    }
     


    _udp_fd = open_udp_socket();
    if (_udp_fd < 0 ) 
    {
        exit(EXIT_FAILURE);
    }
    
    
    if (0 >= inet_pton(AF_INET, remote_ip, &_remote_ip)) 
    {
        debug("%s: invalid IP address %s\n", _progname, remote_ip);
        exit(EXIT_FAILURE);
    }
    
    pthread_create(&tid_recv,  NULL, receiver,    NULL);
    
     while (!_do_exit)
        sleep(1);

    debug("** Shutting down...\n");
    close_tun_iface();
    shutdown(_udp_fd, 2); _udp_fd = -1;
    pthread_join(tid_recv,  &thread_ret);

    return 0;
}

这是我的接收者和响应者 TCP 线程

void * receiver(void *data)
{

    //struct sockaddr_in cliaddr = {0};
    int recvlen = -1;
    int writelen = -1;
    //socklen_t clilen = sizeof(cliaddr);

    while (!_do_exit)
    {
        //recvlen = rrecvfrom(_udp_fd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &clilen);
        char buf[VPN_MAX_MTU] = {0};
        char buf_1[VPN_MAX_MTU] = {0};
        memset(buf,0,VPN_MAX_MTU);
        memset(buf_1,0,VPN_MAX_MTU);
        memset(buf,0,VPN_MAX_MTU);
        memset(buf_1,0,VPN_MAX_MTU);
        

        char *str_source=malloc(18);
        char *str_dest=malloc(18);
        memset(str_source,0,18);
        memset(str_dest,0,18);
        recvlen=read(_tun_fd,buf,VPN_MAX_MTU);
        if(recvlen>0)
        {

    //BUFFER received here        
        struct iphdr *iph=(struct iphdr *)buf;  
        struct iphdr *ip=(struct iphdr *)buf_1;
        int y=0;
        for(int b=0;b<(sizeof(struct iphdr)+sizeof(struct tcphdr));b++)
        {
            if(y==20)
            {
                y=0;
                //printf("\n");
            }
            
            //printf("%x ",buf[b]<<24);
            
            
            y++;
        
        }
    //      tcph->check=(tcp_chksum(iph,tcph));
        //iph->check = csum(iph, sizeof(*iph));
        char str_src[18]={0};
        char str_dest_t[18]={0};
           
           
        //printf("IN %s %s\n",get_ip_str_1(iph->saddr,str_src),get_ip_str_1(iph->daddr,str_dest_t));
        memcpy(&ip->daddr,&iph->saddr,sizeof(uint32_t));
        memcpy(&ip->saddr,&iph->daddr,sizeof(uint32_t));
        //printf("OUT %s %s\n",get_ip_str_1(ip->saddr,str_src),get_ip_str_1(ip->daddr,str_dest_t));
        //Create ip
        
        //DOUBLE CHECK FOR BYTE ORDER
        
        //ip->tot_len=iph->tot_len;
        populate_ip_some(iph,ip);
        ip->tos=0;
        ip->tos=iph->tos;
        ip->ihl         = 5;
        ip->version     = 4;
        ip->tot_len     = sizeof(struct iphdr) + sizeof(struct tcphdr);
        ip->protocol    = 6;
        ip->check=0; 
        //DOUBLE CHECK FOR BYTE ORDER
            ip->check = csum(ip, sizeof(*ip));
        ip->id=htons(100);

        //printf("before %d \n",htons(iph->check));
        iph->check=0; 
        //printf("middle %d\n",iph->check);
        //DOUBLE CHECK FOR BYTE ORDER
            iph->check = csum(iph, sizeof(*iph));

        int i=iph->ihl*4;
        struct tcphdr *tcph=(struct tcphdr *)(buf+i);
        //printf("tcp before %x\n",htons(tcph->check));
        tcph->check=0;
        printf("TCP START\n");
        tcph->check=(tcp_chksum(iph,tcph));
            printf("TCP END\n");
        //printf("tcp after %d\n",(tcph->check));
        //printf("i == %d\n",i);
        //POSSIBLY PRINT IPH for fun
        //for(int a=0;a<recvlen;a++)
            //printf("%x\n",buf[a]);
        //GET ihl SEND --  tcp
        int j=(ip->ihl*4);
        //printf("j == %d\n",j);
        int x=0;
        
        //SEEK filling
        struct tcphdr *tcp=(struct tcphdr *)(buf_1+20);
        populate_tcp_some(tcph,tcp);//Do LOOK AT THIS FUNCTION TO [SEE/CORRECT IT] >:)
        if(tcph->syn==1)
        {
               printf("syn\n");
               populate_tcp_some(tcph,tcp);
               tcp->seq=htons(1);
               tcp->ack_seq=1;
               tcp->syn=1;
               tcp->ack=1;

               tcp->source=htons(80);
    //         printf("received tcp syn = %d\n",tcph->syn);
        }
        else
        {
               populate_tcp_some(tcph,tcp);
               tcp->syn=0;
               tcp->ack=1;
    //         printf("sending tcp syn = %d ack = %d\n",tcp->syn,tcp->ack);
           
        }
        populate_tcp_some(tcph,tcp);
        tcp->dest=tcph->source;
        //printf("%d %d SOURCE PORT \n",ntohs(tcph->source),ntohs(tcp->dest));
        
        tcp->source=htons(80);
        printf("%d %d PORTS \n",ntohs(tcp->source),ntohs(tcp->dest));
        tcp->check=0;
        //TCP CHECKSUM ABOUT TRIPPLE WOW
        tcp->check=tcp_chksum(ip,tcp);
        
        //printf("tcpH = %d |  tcp = %d\n",tcph->check,htons(tcp->check));
        //IF needed make payload data
        //WRITE
        if (recvlen > 0) 
        {
            writelen = write(_tun_fd, buf_1, sizeof(struct iphdr)+sizeof(struct tcphdr));
            //debug("SR:%04d\n", recvlen);
            //debug("TW:%04d\n", writelen);
            
            if (writelen < 0) 
            {
            //debug("%s: rwrite() %s [%d]\n", _progname, strerror(errno), errno);
               //break;//NO NEED
            }
        }
        else if (recvlen < 0) 
        {
            //debug("%s: rrecvfrom() %s\n", _progname, strerror(errno));
               //break;//NO NEED
        }
        else if (recvlen == 0) 
        {
            //why
        }
    //FINALLY THEN SEND || DO WIRE SHARK 
        }
        
        // ...:)__ :) __:) ___:)___ (: __(:__ (;...  

    }

    debug("** Receiver ending.\n");
    pthread_exit(NULL);
}

这就是我设置 tun 接口的方式

int open_tun_iface(ip4_addr_t local_ip4)
{
    struct ifreq ifr_tun;
    int fd = -1;
    sock = -1;
  //  int mtu = VPN_PATH_MTU;

    if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
        debug("%s: Cannot open /dev/net/tun: %s. Do modprobe tun; lsmod\n", _progname, strerror(errno));
        return -1;
    }

    memset( &ifr_tun, 0, sizeof(ifr_tun) );
    ifr_tun.ifr_flags = IFF_TUN | IFF_NO_PI;// | IFF_NO_PI;
    if ((ioctl(fd, TUNSETIFF, (void *)&ifr_tun)) < 0) {
        debug("%s: TUNSETIFF error: %s\n", _progname, strerror(errno));
        close(fd);
        return -1;
    }

#if 0
    if (ioctl(fd, TUNSETPERSIST, 1) < 0) {
        debug("%s: TUNSETPERSIST error: %s\n", _progname, strerror(errno));
        close(fd);
        return -1;
    }
#endif

 sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
            printf("interface socket error\n");
        debug("%s: Cannot open udp socket: %s\n", _progname, strerror(errno) );
        close(fd);
        return -1;
    }

    if (set_ip(&ifr_tun, sock, local_ip4) < 0) {
        close(fd);
        close(sock);
        return -1;
    }


    if (ioctl(sock, SIOCGIFFLAGS, &ifr_tun) < 0) {
        debug("%s: SIOCGIFFLAGS: %s\n", _progname, strerror(errno));
        printf("SIOCSIFFLAGS\n");
        close(fd);
        close(sock);
        
        return -1;
    }

    ifr_tun.ifr_flags |= IFF_UP;
    ifr_tun.ifr_flags |= IFF_RUNNING;

    if (ioctl(sock, SIOCSIFFLAGS, &ifr_tun) < 0)  {
        debug("%s: SIOCSIFFLAGS: %s\n", _progname, strerror(errno));
        printf("SIOCSIFFLAGS\n");
        exit(0);    
        close(fd);
        close(sock);
        return -1;
    }


    /*mtu = get_if_mtu("eth0", sock);*/
  /*  mtu = path_mtu_to_ip(_remote_ip, 32);
    if (mtu <= 0) {
        mtu = INTERNET_MTU;
    }

    if (mtu + VPN_OVERHEAD > VPN_MIN_MTU)
        mtu -= VPN_OVERHEAD;

    if (0 != set_mtu(&ifr_tun, sock, mtu)) {
        close(fd);
        close(sock);
        return -1;
    }
*/
    debug("** TUN opened: %s\n", ifr_tun.ifr_name);
    //close(sock);
    
    return fd;
}

这是我的校验和计算

通用 csum 函数

uint16_t csum(const void *data, const int length)
{
    uint16_t *accumalator = (uint16_t *)data;
    uint64_t sum = 0;

    /* Take care of the first 16-bit even blocks */
    for (int i = 0; i < length/2; ++i) {
        sum += *(accumalator+i);

        if (sum >= 0x10000) {
            sum -= 0xffff;
        }
    }

    /* Handle the ending partial block */
    if (length % 2 != 0) {
        accumalator = accumalator+ length/2; /* Point accumalator to the end block */
        uint16_t end_block = 0;
        memcpy(&end_block, accumalator, sizeof(length));
        sum += ntohs(end_block);
        if (sum >= 0x10000) {
            sum -= 0xffff;
        }
    }
  
    return htons(~sum);
}

这是我计算和处理 TCP 校验和计算

uint16_t tcp_chksum(struct iphdr *snd_iph, struct tcphdr *snd_tcph)
{
    struct psuedo_header psh;

    psh.src_addr = snd_iph->saddr;
    psh.dst_addr = snd_iph->daddr;
    psh.rsvd = 0;
    psh.proto = IPPROTO_TCP;
    psh.len_tcp = htons(sizeof(struct tcphdr)); /* No options, and no data */

    int pseudogram_size = sizeof(struct tcphdr) + sizeof(struct psuedo_header);
    //int pseudogram_size = sizeof(*snd_tcph) + sizeof(psh);
    char *pseudogram = malloc(pseudogram_size);

    memcpy(pseudogram, (char *)&psh, sizeof(struct psuedo_header));
    memcpy(pseudogram + sizeof(struct psuedo_header), snd_tcph, sizeof(struct tcphdr));

    return((csum1(pseudogram, pseudogram_size)));
    //return (htons(csum(snd_tcph, sizeof(struct my_tcph)) + csum(&psh, sizeof(struct psuedo_header))));
}

int populate_ip_some(struct iphdr *o1,struct iphdr *o2) { o2->ihl=o1->ihl; //o2->版本=o1->版本; o2->id=htons(ntohs(o1->id)+1); o2->frag_off=o1->frag_off; o2->ttl=o1->ttl; o2->tos=0; //o2->协议=o1->协议; //o2->检查=0; return 1;

}

int populate_tcp_flags(struct tcphdr *o1,struct tcphdr *o2) {

if(o1->syn==1 && o1->ack==0)
{

    printf("syn received\n\n");
    o2->syn=1;
    o2->ack=1;
    o2->rst=0;
    o2->fin=0;
    return 1;
}
if(o1->syn ==0 && o1->ack==1)
{
    printf("ack received\n\n");
    o2->syn=0;
    o2->ack=0;
    o2->rst=0;
    o2->fin=0;
    return 1;
}
if(o1->syn==0 && o1->fin==1)
{
    printf("fin received\n\n");
    o2->syn=0;
    o2->ack=1;
    o2->fin=0;
    o2->rst=0;
    return 1;
}

return 1;

}

uint32_t *ik;
int x=0;
int populate_tcp_some(struct tcphdr *o1,struct tcphdr *o2)
{
    if(x==0)
    {
        ik=malloc(sizeof(int)*100);
        if(ik==NULL)
        {printf("heap\n");exit(0);}
    }
    *(ik+x)=x;
    o2->seq=htons(*(ik+x));
    uint32_t host=ntohs(o1->seq);
    if(o1->syn==1)
    o2->ack_seq=1;
    else
    {
        //DOUBLE CHECK FOR TCP. ACK_SEQ SHOULD HAVE BEEN sizeof(OF TOTAL PACKET) + 1
        //DOUBLE CHECK MAY NEED TO PASS IPHDR ipH   
        o2->ack_seq=(htons(host+1));
    }   
    o2->doff=o1->doff;
    o2->res1=o1->res1;
    o2->window=o1->window;
    o2->check=0;
    o2->urg_ptr=o1->urg_ptr;
    x++;
    return 1;
}

这是我的 netinet/ip.h,它有 iphdr 定义

struct iphdr
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
    uint8_t tos;
    uint16_t tot_len;
    uint16_t id;
    uint16_t frag_off;
    uint8_t ttl;
    uint8_t protocol;
    uint16_t check;
    uint32_t saddr;
    uint32_t daddr;
    /*The options start here. */
  };

这是 netinet/tcp.h 可以找到我的 tcphdr 结构的地方

struct tcphdr
  {
    __extension__ union
    {
      struct
      {
    uint16_t th_sport;  /* source port */
    uint16_t th_dport;  /* destination port */
    tcp_seq th_seq;     /* sequence number */
    tcp_seq th_ack;     /* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
    uint8_t th_x2:4;    /* (unused) */
    uint8_t th_off:4;   /* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
    uint8_t th_off:4;   /* data offset */
    uint8_t th_x2:4;    /* (unused) */
# endif
    uint8_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
    uint16_t th_win;    /* window */
    uint16_t th_sum;    /* checksum */
    uint16_t th_urp;    /* urgent pointer */
      };
      struct
      {
    uint16_t source;
    uint16_t dest;
    uint32_t seq;
    uint32_t ack_seq;
# if __BYTE_ORDER == __LITTLE_ENDIAN
    uint16_t res1:4;
    uint16_t doff:4;
    uint16_t fin:1;
    uint16_t syn:1;
    uint16_t rst:1;
    uint16_t psh:1;
    uint16_t ack:1;
    uint16_t urg:1;
    uint16_t res2:2;
# elif __BYTE_ORDER == __BIG_ENDIAN
    uint16_t doff:4;
    uint16_t res1:4;
    uint16_t res2:2;
    uint16_t urg:1;
    uint16_t ack:1;
    uint16_t psh:1;
    uint16_t rst:1;
    uint16_t syn:1;
    uint16_t fin:1;
# else
#  error "Adjust your <bits/endian.h> defines"
# endif
    uint16_t window;
    uint16_t check;
    uint16_t urg_ptr;
      };
    };
};

为了有问题的答案。

OP 正在尝试创建一个 SYNACK 数据包来应答传入的 SYN 数据包。 TCP SYN 数据包从 OS 堆栈生成,并使用选项。

Wireshark 抱怨 header 太短。可以看出,tcp header中的header长度设置为40字节,而实际的header只存在20字节(整个包为40字节:20字节IP header 和 20 字节 tcp header).

问题是字段 tcp->doff,即 tcp header,长度是从传入的 SYN 数据包中复制的。虽然未显示,但传入的 SYN 数据包可能包含 TCP 选项,因此它的 header 是 40 字节,而不是 20 字节。因此复制 tcp->doff 会导致问题中的错误消息。

作为参考,tcp header 长度字段是 tcp header 长度的 32 位或 4 字节的倍数。最小 tcp header 是 20 个字节或 5 个 tcp header 单位。或者 tcp->doff = sizeof(struct tcphdr)/4 也应该有效。