這幾天有點犯糊塗,搞不清楚連接建立之後,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