Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I set the TCP options for sending packets?

Tags:

c

networking

tcp

I'm programming software which can send tcp packets to an host.

I'm able to create a packet with IP header, TCP Headers and data but I can't manage how to add TCP options like MSS, NOP, STACK, Window scaling or Timestamp.

I mean i'm not able to add options to the TCP header, calculate the correct checksum to send a good TCP packet to the host.

I can just send correct TCP packets without TCP options.

Do you think I'm on the correct patch? Could somebody please help me?

/* 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;
}
like image 836
Shark Avatar asked Nov 04 '22 16:11

Shark


2 Answers

In the line:

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

According to the in_cksum_tcp function that i've seen in the wild:

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

You are passing the size of the options header (sizeof(struct tcphdr_mss)), instead the size of the complete TCP header (sizeof (tcphdr) + sizeof(tcphdr_mss)). I think this could be the problem (you don't compute correctly the TCP checksum).

A good way to check wich is the problem creating raw packets is to save the packet with libpcap to a pcap file and open with the wireshark. You could check easily the integrity of the packet.

like image 178
Jon Ander Ortiz Durántez Avatar answered Nov 09 '22 09:11

Jon Ander Ortiz Durántez


Below is a function which calculate correct checksum for a packet

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

call the function to get checksum for ip and tcp. other then this inorder to use option like options like MSS, NOP, STACK you need to declare all this in your TCP header. and when you are using all this in your program you have to set the value of th_off accordingly

like image 32
shubendrak Avatar answered Nov 09 '22 09:11

shubendrak