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

 

posted @ 2024-12-17 19:57  codestacklinuxer  阅读(25)  评论(0)    收藏  举报