sack-shift-sacked-norack test
// Test shifting of newly-SACKed ranges onto the previous already-SACKed skb.
// This variant tests non-FACK SACK with SACKs coming in the order
// 2 6 8 3 9, to test what happens when we get a new SACKed range
// (for packet 3) that is on the right of an existing SACKed range
// (for packet 2).
`../common/defaults.sh`
// Establish a connection and send 10 MSS.
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8>
+.1 < . 1:1(0) ack 1 win 1024
+0 accept(3, ..., ...) = 4
+0 write(4, ..., 10000) = 10000
+0 > P. 1:10001(10000) ack 1
+.1 < . 1:1(0) ack 1 win 257 <sack 2001:3001,nop,nop>
+.001 < . 1:1(0) ack 1 win 257 <sack 2001:3001 6001:7001,nop,nop>
+0 %{ print("1---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
+.001 < . 1:1(0) ack 1 win 257 <sack 2001:3001 6001:7001 8001:9001,nop,nop>
+0 %{ print("2---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// 3 SACKed packets, so we enter Fast Recovery.
+0 > . 1:1001(1000) ack 1
+0 %{ assert tcpi_ca_state == TCP_CA_Recovery, tcpi_ca_state }%
// +0 %{ assert tcpi_lost == 6, tcpi_lost }%
+0 %{ print("3---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// SACK for 3001:4001.
// This SACK for an adjacent range causes the sender to
// shift the newly-SACKed range onto the previous skb.
+.007 < . 1:1(0) ack 1 win 257 <sack 2001:4001 6001:7001 8001:9001,nop,nop>
+0 > . 1001:2001(1000) ack 1
// +0 %{ assert tcpi_lost == 5, tcpi_lost }%
+0 %{ assert tcpi_reordering == 6, tcpi_reordering }% // 8001:9001 -> 3001:4001 is 6
+0 %{ print("4---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// SACK for 9001:10001.
+.01 < . 1:1(0) ack 1 win 257 <sack 2001:4001 6001:7001 8001:10001,nop,nop>
// +0 %{ assert tcpi_lost == 5, tcpi_lost }% //3526
+0 %{ print("5---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// ACK for 1:1001 as packets from t=0.303 arrive.
+.083 < . 1:1(0) ack 1001 win 257 <sack 2001:4001 6001:7001 8001:10001,nop,nop>
// +0 %{ assert tcpi_lost == 4,tcpi_lost }%
+0 %{ print("6---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// ACK for 1:4001 as packets from t=0.310 arrive.
+.017 < . 1:1(0) ack 4001 win 257 <sack 6001:7001 8001:10001,nop,nop>
// +0 %{ assert tcpi_lost == 3,tcpi_lost }%
+0 %{ print("7---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// ACK for 1:7001 as packets from t=0.320 arrive.
+.01 < . 1:1(0) ack 7001 win 257 <sack 8001:10001,nop,nop>
+0 %{ print("8---tcpi_ca_state:", tcpi_ca_state, "tcpi_sacked:", tcpi_sacked, "tcpi_lost:", tcpi_lost, "tcpi_reordering:", tcpi_reordering) }%
// ACK for all data as packets from t=0.403 arrive.
+.1 < . 1:1(0) ack 10001 win 257
+0 %{
assert tcpi_ca_state == TCP_CA_Open, tcpi_ca_state
assert tcpi_unacked == 0, tcpi_unacked
assert tcpi_sacked == 0, tcpi_sacked
assert tcpi_lost == 0, tcpi_lost
assert tcpi_retrans == 0, tcpi_retrans
}%
//+0 `sleep 1000000`

tcp_ack逻辑是:先处理sack tcp_sacktag_write_queue ---->tcp_clean_rtx_queue ---->tcp_fastretrans_alert
从disorder进入到recover的时候, tcp_sacktag_write_queue 首先处理sack报文,由于最后<sack 2001:3001 6001:7001 8001:9001,nop,nop> 只是新增了8001---9001
所以:tcp_sacktag_write_queue 处理sack报文时,此次state中record 值为8001, 当然全局的tcp_highest_sack_seq 会设置为最新的 9001 skb;
所以tcp_check_sack_reordering检查recording的时候 state->record 和highest_sack 值举例小于默认reordering==3 mss,所以不会更新reordering
tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb, u32 prior_snd_una, struct tcp_sacktag_state *state) { //更新序号位于[start_seq,end_seq)之间的skb的记分牌 skb = tcp_sacktag_walk(skb, sk, next_dup, state, start_seq, end_seq, dup_sack); ----------------------- //更新乱序信息 if (inet_csk(sk)->icsk_ca_state != TCP_CA_Loss || tp->undo_marker) tcp_check_sack_reordering(sk, state->reord, 0); } static struct sk_buff *tcp_sacktag_walk() { TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(sk,state,TCP_SKB_CB(skb)>sacked, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,dup_sack,tcp_skb_pcount(skb), tcp_skb_timestamp_us(skb)); if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) tcp_advance_highest_sack(sk, skb); return skb; } static u8 tcp_sacktag_one(struct sock *sk, struct tcp_sacktag_state *state, u8 sacked,u32 start_seq, u32 end_seq, int dup_sack, int pcount, u64 xmit_time) { struct tcp_sock *tp = tcp_sk(sk); if (!(sacked & TCPCB_SACKED_ACKED)) {//skb还没有被SACK确认过,现在来进行标记 ---------------------- if (!(sacked & TCPCB_RETRANS)) {//该skb确实没有被重传过 /* New sack for not retransmitted frame,* which was in hole. It is reordering. */ if (before(start_seq, tcp_highest_sack_seq(tp)) && before(start_seq, state->reord)) state->reord = start_seq; if (!after(end_seq, tp->high_seq)) state->flag |= FLAG_ORIG_SACK_ACKED; //??? if (state->first_sackt == 0) state->first_sackt = xmit_time; state->last_sackt = xmit_time; } if (sacked & TCPCB_LOST) {//如果skb被判定为丢失,那么收到了SACK,可以清除TCPCB_LOST标记 sacked &= ~TCPCB_LOST; tp->lost_out -= pcount; } } sacked |= TCPCB_SACKED_ACKED;//核心在这里,设置TCPCB_SACKED_ACKED标记 state->flag |= FLAG_DATA_SACKED;//更新flag,表示SACK确认了数据 tp->sacked_out += pcount;//更新sacked_out计数 /* Out-of-order packets delivered */ state->sack_delivered += pcount; /* Lost marker hint past SACKed? Tweak RFC3517 cnt */ if (tp->lost_skb_hint && before(start_seq, TCP_SKB_CB(tp->lost_skb_hint)->seq)) tp->lost_cnt_hint += pcount; } ---------------------------- return sacked; }
此时收到 +.001 < . 1:1(0) ack 1 win 257 <sack 2001:3001 6001:7001 8001:9001,nop,nop>
进入recover状态;此时recording还是=3, 来看lost数据;
此时状态为disorder,所以进入 tcp_identify_packet_loss,但是此时开启sack 关闭了fack,所以不会执行任何逻辑;
直接进入tcp_enter_recovery状态初始化tcp_init_cwnd_reduction;
然后调用tcp_update_scoreboard来标记lost报文
tcp_fastretrans_alert ---->tcp_identify_packet_loss -----》tcp_enter_recovery ---->tcp_update_scoreboard
static void tcp_update_scoreboard(struct sock *sk, int fast_rexmit) { struct tcp_sock *tp = tcp_sk(sk); //sacked_upto 可能表示已经成功接收并被确认的数据包数量(sack最大seq/mss= pkt包数)减去 乱序级别。 //这个值可能用于指示已经确认但尚未被处理的数据包数量 // tp->sacked_out 表示sack中最高的seq---最低的seq 除去mss 有多少个段 pkt if (tcp_is_sack(tp)) { int sacked_upto = tp->sacked_out - tp->reordering; if (sacked_upto >= 0) tcp_mark_head_lost(sk, sacked_upto, 0); else if (fast_rexmit) tcp_mark_head_lost(sk, 1, 1); } }
最后调用 tcp_mark_head_lost(sk, sacked_upto, 0); sacked_upto=0;
tcp_skb_mark_lost,如果报文没有被标记过丢失(TCPCB_LOST),也没有被SACK确认(TCPCB_SACKED_ACKED),
将其设置TCPCB_LOST标志,根据代码; 标记数大于sacked_upto就会退出,所以,会标记sacked前面的数据;也就是1---2001,一共2 mss 数据
所以最后结果是:tcpi_ca_state: 3 tcpi_sacked: 3 tcpi_lost: 2 tcpi_reordering: 3
但是在收到:<sack 2001:4001 6001:7001 8001:9001,nop,nop>时; tcpi_ca_state: 3 tcpi_sacked: 4 tcpi_lost: 2 tcpi_reordering: 6
原因是: 此次sack 相比以前sack数据变化为:增加了3001-40001,所以highest-sack---state-recored===// 8001:9001 -> 3001:4001 is 6
再看后续的sack;可知:ack 1001 win 257 <sack 2001:4001 6001:7001 8001:10001,nop,nop> 就是表示 ack了lost数据1-1001,所以lost-1= 2-1=1;
再看:ack 4001 win 257 <sack 6001:7001 8001:10001,nop,nop> 也就是lost数据被ack完了;素以lost=0


浙公网安备 33010602011771号