这几天有点犯糊涂,搞不清楚连接建立之后,SACK选项的解析在什么地方处理。内核中唯一能解析SACK选项的函数就是tcp_parse_options,但是就找不到tcp_rcv_established函数在哪里调用它。这里犯了一个错误,一直以为tcp_validate_incoming函数仅是验证报文,其中的tcp_fast_parse_options只是快速解析timestamps选项。
static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
const struct tcphdr *th, int syn_inerr)
{
struct tcp_sock *tp = tcp_sk(sk);
bool rst_seq_match = false;
/* RFC1323: H1. Apply PAWS check first. */
if (tcp_fast_parse_options(sock_net(sk), skb, th, tp) &&
tp->rx_opt.saw_tstamp &&
tcp_paws_discard(sk, skb)) {
if (!th->rst) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
if (!tcp_oow_rate_limited(sock_net(sk), skb,
LINUX_MIB_TCPACKSKIPPEDPAWS,
&tp->last_oow_ack_time))
tcp_send_dupack(sk, skb);
goto discard;
}
/* Reset is accepted even if it did not pass PAWS. */
然而,在tcp_fast_parse_options函数的处理中,如果报文存在SACK选项数据,timestamps选项的快速解析必然失败。快速解析是建立在TCP选项数据中仅有timestamps一个选项的基础上的。当有SACK选项时,必须执行后面的正常选项解析函数tcp_parse_options,其将解析所有的选项数据,包括SACK选项。
static bool tcp_fast_parse_options(const struct net *net,
const struct sk_buff *skb, const struct tcphdr *th, struct tcp_sock *tp)
{
/* In the spirit of fast parsing, compare doff directly to constant
* values. Because equality is used, short doff can be ignored here.
*/
if (th->doff == (sizeof(*th) / 4)) {
tp->rx_opt.saw_tstamp = 0;
return false;
} else if (tp->rx_opt.tstamp_ok &&
th->doff == ((sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4)) {
if (tcp_parse_aligned_timestamp(tp, th))
return true;
}
tcp_parse_options(net, skb, &tp->rx_opt, 1, NULL);
if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
tp->rx_opt.rcv_tsecr -= tp->tsoffset;
return true;
内核版本 5.0