Search code examples
csocketstcpip

if I have populated all fields of iphdr then how to calculate the tcphdr->doff and iphdr->ihl | tcp data offset and length of ip header


On my system in /usr/include/netinet/iphdr this is the struct iphdr looks like

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. */
  };

Assume I have crafted all the ip header fields with values. Now I want to calculate ip header length which is iphdr->ihl field on my system. I am assuming this is ip header length, so the length of my made ip header can't be sizeof(struct iphdr) then what that might be.

Also the struct tcphdr looks like this

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

so assume again that I have populated all the field of tcphdr with values now I want doff(data offset). how to calculate it

  struct iphdr *iph=buffer;
   // populate the fields of iph
   //but what is iph->ihl? to calculate how?
   int iphdrlen = iph->ihl*4;
   struct tcphdr *tcph=(struct tcphdr *)(buffer + iphdrlen);
   //populate all the fields of tcph
   //but also how to calculate tcph->doff

should it be like for iphdr->ihl

//after populating all the fields of iph then  just do following to get iph->ihl

  iph->ihl=sizeof(iph);

and should it be like for tcph->doff

 //after populating all the fields of tcph just do following

  tcph->doff = (unsigned int)(sizeof(iph))+sizeof(tcph)));

and that will work, so will it?


Solution

  • IP header has two "parts" - a mandatory fixed sized part, which is represented by struct iphdr and the latter is represented by options that optionally can be present (see the comment /*The options start here. */). The header length is the whole header length with options. So, unless you have actually added some options it is only the length of the header, which you can either calculate or read in the standard:

    IHL: 4 bits

    Internet Header Length is the length of the internet header in 32 bit words, and thus points to the beginning of the data. Note that the minimum value for a correct header is 5.

    Now, one byte is 4 bits, and 32 bits are 4 words. Thus it is either sizeof(struct iphdr)/4 or you can set it to 5.

    TCP data offset is basically the same, but for TCP. Since TCP header can also contain options, the same logic applies. According to the standard (scroll to next page):

    Data Offset: 4 bits

    The number of 32 bit words in the TCP Header. This indicates where the data begins. The TCP header (even one including options) is an integral number of 32 bits long.

    So, basically it is calculated the same way. It is not a pointer, it is a number which indicates length of the header. According to wikipedia if your header has no options, the value of this field is also 5.