理解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, ">");
}