关于tcp options详解见:
源码分析:
void tcp_parse_options(const struct sk_buff *skb,
struct tcp_options_received *opt_rx, int estab,
struct tcp_fastopen_cookie *foc)
{
const unsigned char *ptr;
const struct tcphdr *th = tcp_hdr(skb);
int length = (th->doff * 4) - sizeof(struct tcphdr); //tcp头的长度 - tcp结构体的长度 = tcp option长度
ptr = (const unsigned char *)(th + 1); //指向了option位置的指针
opt_rx->saw_tstamp = 0;
//详见option的定义:http://book.51cto.com/art/201306/400263.htm
while (length > 0) {
int opcode = *ptr++;
int opsize;
switch (opcode) {
case TCPOPT_EOL:
return;
case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
length--;
continue;
default:
opsize = *ptr++;
if (opsize < 2) /* "silly options" */
return;
if (opsize > length)
return; /* don't parse partial options */
switch (opcode) {
case TCPOPT_MSS: //最大尺寸协商
if (opsize == TCPOLEN_MSS && th->syn && !estab) { //opcode=TCPOPT_MASS时,其opsize=TCPOLEN_MSS
u16 in_mss = get_unaligned_be16(ptr); //获取MSS(Maxitum Segment Size 最大分段尺寸)长度
if (in_mss) {
if (opt_rx->user_mss &&
opt_rx->user_mss < in_mss)
in_mss = opt_rx->user_mss;
opt_rx->mss_clamp = in_mss; //接收的长度
}
}
break;
case TCPOPT_WINDOW: //wscale, 正常window窗口尺寸为两个字节,即最大支持65536,为了加大window空间,采样wscale系数
//特别强调一点snd_wscale最大值为14
if (opsize == TCPOLEN_WINDOW && th->syn &&
!estab && sysctl_tcp_window_scaling) {
__u8 snd_wscale = *(__u8 *)ptr;
opt_rx->wscale_ok = 1;
if (snd_wscale > 14) {
net_info_ratelimited("%s: Illegal window scaling value %d >14 received\n",
__func__,
snd_wscale);
snd_wscale = 14;
}
opt_rx->snd_wscale = snd_wscale;
}
break;
case TCPOPT_TIMESTAMP: //时间,用于计算RTT(用于计算发送方发送数据到接收端响应数据的过程所耗费的时间)
if ((opsize == TCPOLEN_TIMESTAMP) &&
((estab && opt_rx->tstamp_ok) ||
(!estab && sysctl_tcp_timestamps))) {
opt_rx->saw_tstamp = 1;
opt_rx->rcv_tsval = get_unaligned_be32(ptr);
opt_rx->rcv_tsecr = get_unaligned_be32(ptr + 4);
}
break;
case TCPOPT_SACK_PERM: //是否支持SACK(选择性确认)
if (opsize == TCPOLEN_SACK_PERM && th->syn &&
!estab && sysctl_tcp_sack) {
opt_rx->sack_ok = TCP_SACK_SEEN;
tcp_sack_reset(opt_rx);
}
break;
case TCPOPT_SACK: //SACK:选择性确认
if ((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
!((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK) &&
opt_rx->sack_ok) { //选择性确认标识是否有效
TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *)th;
}
break;
#ifdef CONFIG_TCP_MD5SIG
case TCPOPT_MD5SIG:
/*
* The MD5 Hash has already been
* checked (see tcp_v{4,6}_do_rcv()).
*/
break;
#endif
case TCPOPT_EXP:
/* Fast Open option shares code 254 using a
* 16 bits magic number. It's valid only in
* SYN or SYN-ACK with an even size.
*/
if (opsize < TCPOLEN_EXP_FASTOPEN_BASE ||
get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC ||
foc == NULL || !th->syn || (opsize & 1))
break;
foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE;
if (foc->len >= TCP_FASTOPEN_COOKIE_MIN &&
foc->len <= TCP_FASTOPEN_COOKIE_MAX)
memcpy(foc->val, ptr + 2, foc->len);
else if (foc->len != 0)
foc->len = -1;
break;
}
ptr += opsize-2;
length -= opsize;
}
}
}
EXPORT_SYMBOL(tcp_parse_options);