读取TCP选项字段

时间:2017-03-12 17:16:51

标签: c linux networking tcp

我正在连接linux / tcp.h,而我试图阅读TCP选项,我似乎无法找到如何做到这一点。我在线阅读了一些内容,根据一些在线消息来源,我必须迭代所有剩余的数据包"直到我点击我想要的选项? (现在我将尝试专注于" MSS"选项)。任何人都可以为我提供代码示例吗?

struct iphdr *iph = ((struct iphdr *) full_packet);
    fprintf(stdout, "IP{v=%u; ihl=%u; tos=%u; tot_len=%u; id=%u; ttl=%u; protocol=%u; "
        ,iph->version, iph->ihl*4, iph->tos, ntohs(iph->tot_len), ntohs(iph->id), iph->ttl, iph->protocol);

    if (iph->protocol == 6){

        struct tcphdr *tcp = ((struct tcphdr *) (full_packet + (iph->ihl << 2)));
        fprintf(stdout, "TCP{sport=%u; dport=%u; seq=%u; ack_seq=%u; flags=u%ua%up%ur%us%uf%u; window=%u; urg=%u}\n",
            ntohs(tcp->source), ntohs(tcp->dest), ntohl(tcp->seq), ntohl(tcp->ack_seq)
            ,tcp->urg, tcp->ack, tcp->psh, tcp->rst, tcp->syn, tcp->fin, ntohs(tcp->window), tcp->urg_ptr);\
    }

到目前为止,我在阅读/解析IP / TCP数据方面是什么

谢谢!

Bump ...感谢任何帮助!

进度更新(感谢@WillisBlackburn):

struct iphdr *iph = ((struct iphdr *) full_packet);

fprintf(stdout, "IP{v=%u; ihl=%u; tos=%u; tot_len=%u; id=%u; ttl=%u; protocol=%u; "
    ,iph->version, iph->ihl*4, iph->tos, ntohs(iph->tot_len), ntohs(iph->id), iph->ttl, iph->protocol);

if (iph->protocol == 6){

    struct tcphdr *tcp = ((struct tcphdr *) (full_packet + (iph->ihl << 2)));

    uint8_t *p = (uint8_t *)tcp + 20;
    uint8_t *end = (uint8_t *)tcp + tcp->doff * 4;
    uint16_t mss = 0; 
    printf("\nThe offset is %d\n", tcp->doff);
    printf("Let's check what's at location p: %u is supposed to be less than %d\n",(*(uint8_t *)tcp + 20), (uint8_t)end);
    while (p < end) {
        uint8_t kind = *p++;
        if (kind == 0) {
            printf("The kind is 0?\n");
            break;
        }
        if (kind == 1) {
            // No-op option with no length.
            continue;
        }
        uint8_t size = *p++;
        if (kind == 2) {
            mss = ntohs(*(uint16_t *)p);
            printf("The MSS value is: %d\n", mss);
        }
        p += (size - 2);
    }

    fprintf(stdout, "TCP{sport=%u; dport=%u; seq=%u; ack_seq=%u; flags=u%ua%up%ur%us%uf%u; window=%u; urg=%u}\n",
        ntohs(tcp->source), ntohs(tcp->dest), ntohl(tcp->seq), ntohl(tcp->ack_seq)
        ,tcp->urg, tcp->ack, tcp->psh, tcp->rst, tcp->syn, tcp->fin, ntohs(tcp->window), tcp->urg_ptr);

}

更多进展:

找到一些很酷的读取TCP选项的实现;但是,我在我的代码中实现它并不十分确定。人们的帮助吗?这是我发现的:

https://github.com/multipath-tcp/mptcp/blob/214e17c446d98e238c3bc8a3177990eae6a7059b/net/ipv4/tcp_input.c#L3674-L3789

https://github.com/multipath-tcp/mptcp/blob/02bc9ec8da25e0e71f9d1fec02be4632a0846092/include/net/tcp.h#L170-L210

它似乎比威利斯·布莱克本更复杂,并且可能会因为太大而解决这个问题? (200+带有一些数据包,而不是&#34; 2&#34;我们期望用MSS(+ TCP数据包中的其他选项)。

帮助表示赞赏!!!

如果您想谈论它,我将在#adamc频道的chat.freenode.net(niven.freenode.net)中!

最新更新:

所以,我的基础是MTCP,但这里是代码:(添加了用户测试):

const unsigned char *ptr;
const struct tcphdr *th = ((struct tcphdr *) (full_packet + (iph->ihl << 2)));
int length = (th->doff * 4) - sizeof(struct tcphdr);

ptr = (const unsigned char *)(th + 1);

while(length > 0){
  printf("Got in the while loop\n");
  int opcode = *ptr++;
  int opsize;

  switch(opcode) {
    case 0:
      printf("Got the initial val (EOL)\n");
      return;
    case 1:
      printf("Got the NOP val as well!\n");
      length--;
      continue;
    default:
      printf("Entered default.\n");
      opsize = *ptr++;
      printf("Does stuff after setting the OPSize.\n");
      if(opsize < 2) {
        printf("OPSize = %d < 2;Length = %d\n", opsize, length);
        return;
      }
      if(opsize > length) {
        printf("OPSize = %d > Length = %d\n", opsize, length);
        return;
      }
      switch(opcode) {
        printf("Switching the OPCode\n");
        case 2: //TCP MSS
          if(opsize == 4 && th->syn) {
            printf("MSS: %d\n", ptr);
          }
        case 3: //TCP Window
          if(opsize==3 && th->syn) {
            uint8_t wscale = *(uint8_t *)ptr;
            if(wscale>14) {
              printf("Illgal wscale value: %d\n", wscale);
            }
            printf("WSCALE: %d\n", *(uint8_t*)ptr);
          }
          break;
        case 8: //Timestamp
          if(opsize==10) {
            printf("Timestamp is present!!!\n");
          }
          break;
        case 4: //TCP SACK
          if((opsize >= (2+8)) && !((opsize-2)%8)) {
            printf("SACK val: %d\n", ((ptr-2)- (unsigned char *)th));
          }

        default:
          printf("Entered default of switching OPCode\n");
          return;
      }
      ptr += opsize-2;
      length -= opsize;
  }
}

所以我现在就以下问题: 当我提出请求时,它给了我以下内容:

IP{v=4; ihl=20; tos=0; tot_len=64; id=48952; ttl=61; protocol=6; Got in the while loop
Entered default.
Does stuff after setting the OPSize.
OPSize = 127 > Length = 12

所以基本上,它进入了while循环,输入了默认值(在opsizes之间切换)和所有内容;但是,OPSize是127(大于长度),所以我相信我在获取尺寸方面做错了。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:2)

有两种可能的答案:如何获取TCP连接的MSS(最大段大小)以及如何解析TCP标头。

如果您只想知道连接的MSS是什么,那么您可以在打开的套接字上使用getsockopt函数。传递文件描述符,IPPROTO_TCP作为级别,TCP_INFO作为选项,struct tcp_info(来自tcp.h)将保存输出的地址,以及sizeof (struct tcp_info)。内核将填写struct tcp_info字段,您可以从tcpi_snd_msstcpi_rcv_mss获取MSS。

如果您确实想要解析TCP标头本身,那么您必须了解标头的布局。您使用的struct tcphdr不包含struct tcphdr字段后面的选项。每个选项字段都是标识选项类型的单个字节,可选地后跟第二个字节,指定选项的大小(包括种类和大小字节),然后是其他数据。

可能没有任何选择。您必须首先查看TCP标头(doff)的数据偏移字段。它是32位字。由struct tcphdr定义的标准头的大小为20个字节,因此只有doff大于5(4个字节= 20个字节)时才能出现选项。

假设有选项,您可以像这样阅读它们。请注意,MSS的选项类型为2,选项类型0表示选项列表的结尾,但仅当选项列表的末尾不符合doff的数据开头时。选项类型0和1(无操作)是单个字节。其他选项类型的大小字节跟在种类之后,并指定选项字段的大小(包括种类和大小字段)。

uint8_t *p = (uint8_t *)tcp + 20; // or sizeof (struct tcphdr)
uint8_t *end = (uint8_t *)tcp + tcp->doff * 4;
uint16_t mss = 0; 
while (p < end) {
    uint8_t kind = *p++;
    if (kind == 0) {
        break;
    }
    if (kind == 1) {
        // No-op option with no length.
        continue;
    }
    uint8_t size = *p++;
    if (kind == 2) {
        mss = ntohs(*(uint16_t *)p);
    }
    p += (size - 2);
}

我没有测试过这个,但它应该非常接近。

我找不到任何确认只有TCP选项0和1不包含大小字节的文档。 TCP的规范是RFC 793,它(在撰写本文时)已有35年历史,仅提及选项类型0,1和2.它表明大小字节是否存在取决于选项类型。但这意味着,为了正确解析标题,您的程序必须知道all the possible options it might encounter。例如,如果标头的选项段包含字节16,2,4,1和0,那么这是指类型16的选项,后跟大小字节2,后跟选项类型4(因为大小) 2表示该选项仅包括种类和大小)?或者它是指类型16的选项,没有大小字节,后面是类型2(MSS)的选项,后跟大小字节4?除非您知道选项类型16“Skeeter”是否应跟随大小字节,否则您无法知道。