理解tcp选项的解析时有一些困难,这里记录一下。
(u8*) tcp + sizeof(struct tcp_hdr) /*是选项开始位置,因为tcp头部中包含选项,所以th->off这个头部长度是含有option的长度的,但是tcp_hdr结构的定义中不含选择,所以,tcp指针的地址+tcp_hdr结构长度就是选项的开始位置。
int length = (th->off * 4) - sizeof(struct tcp_hdr); /* 选项的总长度 ,其中th->off 是tcp头部长度*/
以下是Nmap中,tcp 选项解析的函数定义,这是函数调用 :
tcppacketoptinfo((u8*) tcp + sizeof(struct tcp_hdr),
tcp->th_off*4 - sizeof(struct tcp_hdr), tcpoptinfo, sizeof(tcpoptinfo));//第一个参数,选项首地址,第二个参数,选项长度,第三个和第四个是解析得到的结果的存储位置和内容。
(开始一直不明白,前两个参数怎么传这个,后来恍然大悟了。很多东西都没有办法问别人,因为每个人都有研究的内容,可能没有办法静下心帮你读一段代码,还是要靠自己领悟。)
以下是Nmap中,tcp 选项解析的函数定义:
/* Get an ASCII information about a tcp option which is pointed by optp, with a length of len. The result is stored in the result buffer. The result may look like "<mss 1452,sackOK,timestamp 45848914 0,nop,wscale 7>" */ void tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize) { assert(optp); assert(result); char *p, ch; u8 *q; int opcode; u16 tmpshort; u32 tmpword1, tmpword2; unsigned int i=0; p = result; *p = '\0'; q = optp; ch = '<'; while (len > 0 && bufsize > 2) { Snprintf(p, bufsize, "%c", ch); bufsize--; p++; opcode = *q++;//opcode = *q++;这样opcode中存入了option的第一个字符,即option中的type。 if (!opcode) { /* End of List */ //然后开始判断opcode的几种情况0,1,2,3,4,5---具体这五种情况是怎么回事儿,参考关于tcp 选项的介绍即可。 Snprintf(p, bufsize, "eol"); bufsize -= strlen(p); p += strlen(p); len--; } else if (opcode == 1) { /* No Op */ Snprintf(p, bufsize, "nop"); bufsize -= strlen(p); p += strlen(p); len--; } else if (opcode == 2) { /* MSS */ if (len < 4) break; /* MSS has 4 bytes */ q++; memcpy(&tmpshort, q, 2); Snprintf(p, bufsize, "mss %u", ntohs(tmpshort)); bufsize -= strlen(p); p += strlen(p); q += 2; len -= 4; } else if (opcode == 3) { /* Window Scale */ if (len < 3) break; /* Window Scale option has 3 bytes */ q++; Snprintf(p, bufsize, "wscale %u", *q); bufsize -= strlen(p); p += strlen(p); q++; len -= 3; } else if (opcode == 4) { /* SACK permitted */ if (len < 2) break; /* SACK permitted option has 2 bytes */ Snprintf(p, bufsize, "sackOK"); bufsize -= strlen(p); p += strlen(p); q++; len -= 2; } else if (opcode == 5) { /* SACK *//* SACK ,==5时稍微复杂,sockoptlen= N*8+2,以8字节为一块,并且分左4块和右四块,所以程序中(sackoptlen - 2) % 8来处理有多少块,memcpy(&tmpword1, q + i, 4); memcpy(&tmpword2, q + i + 4, 4); 分别四字节存入*/ unsigned sackoptlen = *q; if ((unsigned) len < sackoptlen) break; /* This would break parsing, so it's best to just give up */ if (sackoptlen < 2) break; q++; if ((sackoptlen - 2) == 0 || ((sackoptlen - 2) % 8 != 0)) { Snprintf(p, bufsize, "malformed sack"); bufsize -= strlen(p); p += strlen(p); } else { Snprintf(p, bufsize, "sack %d ", (sackoptlen - 2) / 8); bufsize -= strlen(p); p += strlen(p); for (i = 0; i < sackoptlen - 2; i += 8) { memcpy(&tmpword1, q + i, 4); memcpy(&tmpword2, q + i + 4, 4); Snprintf(p, bufsize, "{%u:%u}", tmpword1, tmpword2); bufsize -= strlen(p); p += strlen(p); } } q += sackoptlen - 2; len -= sackoptlen; } else if (opcode == 8) { /* Timestamp */ if (len < 10) break; /* Timestamp option has 10 bytes */ q++; memcpy(&tmpword1, q, 4); memcpy(&tmpword2, q + 4, 4); Snprintf(p, bufsize, "timestamp %u %u", ntohl(tmpword1), ntohl(tmpword2)); bufsize -= strlen(p); p += strlen(p); q += 8; len -= 10; } ch = ','; } if (len > 0) { *result = '\0'; return; } Snprintf(p, bufsize, ">"); }