TCP e Controle de Congestionamento no Linux

Table of Contents

Introdução

Esse arquivo é um resumo de leitura, ou seja, alguns documentos foram lidos e os pontos importantes foram anotados aqui. O tópico principal dos artigos lidos é TCP, Controle de Congestionamento e sua implementação no Linux.

TCP e Controle de Congestionamento

TCP é o protocolo de transporte mais utilizado na Internet. Ele implementa na Internet uma confiança de transmissão, com seu controle de congestionamento e segmentação.

O controle de congestionamento do TCP é usado para evitar que a rede sofra um "colapso", ou seja, seja desligada e a comunicação encerre. Isso é feito utilizando-se duas tecnologias: ACKs e Janela de Congestionamento. ACKs são mensagens de recebimento enviadas pelo receptor, e Janela de Cong. é usada para estimar a capacidade do gargalo de uma conexão, além de controlar a quantidade máxima de dados que pode ser transmitida.

A medição da capacidade desse gargalo no TCP é feito utilizando o processo AIMD (Additive Increase Multiplicative Decrease) ou seja, "cresce adicionando, decresce multiplicando". O AIMD é implementado seguindo duas fases:

  • Slow Start

    Usada no início da conexão, é a fase onde a janela de congestionamento (cwnd) cresce exponencialmente. Primeiro, é enviado um segmento (cwnd = 1 segmento), se o emissor receber o ACK desse segmento, é enviado dois segmentos (cwnd = 2 segmentos), se o emissor receber o ACK dos dois, é enviado quatro segmentos (1, 2, 4, 8, 16, 32,…) até atingir um limiar (threshold) pré-definido. No Linux, esse limiar é definido na variável ssthresh.

  • Congestion avoidance

    Após a cwnd atingir o limiar ssthresh, essa segunda fase é iniciada, fazendo com que a cwnd cresça de forma linear. Em caso de timeout, o limiar é diminuído pela metade e a conexão volta para a fase de slow start.

O algoritmo original que realiza o controle de congestionamento sofreu diversas alterações na sua estrutura para implementar melhores formas de controle ou controles para conexões específicas. Algumas das outras implementações é o New Reno.

New Reno

O TCP New Reno, descrito na RFC 5681, implementa algumas melhorias no Fast Recovey, descritas a seguir:

  • Fast Retransmit e Fast Recovery:

    Fast Retransmit permite que o servidor reenvie um pacote que foi perdido sem a necessidade de esperar atingir o RTO, sendo feito através do envio de ACKs duplicados pelo cliente.

    Utilizando um exemplo em que um servidor envia os pacotes #1, #2, #3, #4, #5, #6, #7 e #8, o pacote #3 foi perdido e o cliente recebe inicialmente #1, #2, #4, #5 e #6. O cliente então deve enviar um ACK duplicado ACK #2 para o servidor, informando ao servidor que o pacote #3 não foi recebido. À medida que o cliente receber os outros pacotes ele continua enviando DupACKS. No terceiro DupACK o servidor pode assumir que o pacote #3 foi perdido. Dessa forma, o servidor pode reenviar o pacote #3 sem precisar esperar o RTO. O Fast Recovery opera em conjunto com o Fast Retransmit, permitindo que a conexão seja reestabelecida após uma perda de pacotes sem que o slow start seja novamente ativado, caso fosse esperar atingir o RTO. O servidor então reenvia o pacote #3 e a comunicação é reestabelecida.

    O New Reno melhora o Fast Retransmit pois, se no exemplo anterior o pacote #5 tivesse sido perdido, após receber o pacote #3 (já tendo recebido o #4 e #6) o cliente enviaria DupACKs #4, informando que recebeu até o pacote #4 mas não o #5, reiniciando o processo de Fast Retransmit. O New Reno evita a necessidade de esperar o terceiro DupACK, pois, dado que anteriormente nesta conexão já ouve perda de pacote, o próximo único DupACK recebido já é suficiente para retransmissão.

SACKs (Selective ACKs)

Definida na RFC 2018, Selective ACKs são ACKs usados para informar o não recebimento de uma série de pacotes de um única vez. No Fast Retransmit o servidor toma conhecimento de um pacote perdido por vez. Com o SACK, o cliente pode informar de uma só vez todos os pacotes que ele já recebeu e quais foram perdidos. De forma detalhada, seguindo o exemplo anterior, o SACK evita o segundo DupACK #4, pois ao receber #1, #2, #4, #6, #7 e #8 o cliente enviaria um ACK #2, SACK 4, SACK 6 - 8. Assim, o servidor pode entende que os pacotes #3 e #5 não foram recebidos e reenviá-los

Outros algoritmos que implementam controle de congestionamento foram criados, implementando diferentes formas do AIMD. Esses algoritmos podem ser usados no Linux como plugins, e muitos deles já estão implementados.

Controle de Congestionamento no Linux

A implementação do controle de congestionamento (que será chamado CCG neste documento) no Linux pode ser dividida em 4 categorias diferentes:

  1. O controle propriamente dito
  2. A interface entre a implementação do CCG com o resto do TCP
  3. Estado de Recovery
  4. Implementação de outros algoritmos de CCG

Essas categorias serão melhor explicadas adiante, como o Estado de Recovey e outros algoritmos de CCG.

Data Types

O TCP no Linux possui alguns tipos importantes de dados usados durante uma conexão, sendo eles:

  • tcp_ca_state

    Controle de Estado usado pelo CCG do TCP, que serve para indicar o estado de uma conexão para melhorar uma possível recuperação de uma conexão. Os possíveis estados são:

    1. open: Significa que a conexão está aberta, em estado normal e sem "avisos". Assim, os pacotes recebidos nesse estado passam pelo fast path1. Fast Path é usado para diminuir a quantidade de processamento que deve ser feito em um pacote afim de acelerar o encaminhamento dele. Se o TCP detecta que o pacote foi retransmitido mais que 3 vezes o fast path não é usado.
    2. disorder: É o "mesmo" que open, porém usado quando uma conexão possui SACKs ou dupACKs. Aqui, já é utilizado o slow path.
    3. CWR: É um estado ativado quando a conexão possui algumas Congestion Notification, como ECN2.
    4. recovery: Indica que a janela de congestionamento foi reduzida e a conexão está realizando fast-retransmit3
    5. loss: Indica que a janela de congestionamento foi reduzida devido a timeout ou SACK reneging
  • tcp_congestion_ops

    Interface para utilizar diferentes algoritmos de CCG no TCP do Linux. É uma struct de ponteiros para chamadas de funções, definidas em tcp.h. As principais chamadas de funções são destacadas:

    1. init()

      /* initialize private data (optional) */
      void (*init)(struct sock *sk);
      

      Essa função é chamada antes de inicializar o algoritmo do CCG.

    2. pkts_acked()

      /* hook for packet ack accounting (optional) */
      void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample);
      

      Quando um pacote é confirmado (ACKed), essa função é chamada, para guardar informações sobre os ACKs, como a quantidade. ack_sample é uma struct e que possui uma variável pkts_acked que armazena o número de pacotes confirmados.

    3. cong_avoid()

      /* do new cwnd calculation (required) */
      void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);
      

      Essa função é usada para aumentar a janela de congestionamento, após um ACK ser recebido.

    4. undo_cwnd()

      /* new value of cwnd after loss (required) */
      u32 (*undo_cwnd)(struct sock *sk);
      

      Função para "reiniciar" a janela de congestionamento em caso de perda de pacote.

Recovery

A janela de congestionamento de uma conexão é ajustada de acordo com os ACKs recebidos ou de acordo com sinais de congestionamento, como timeouts ou ECN. ECN4 é um mecanismo que permite dispositivos compatíveis enviarem pacotes TCP com a flag ECN_Echo no seu cabeçalho, indicando que há congestionamento na rede.

Medidas para gerenciar um recovery

Para gerenciar ACKs e detectar congestionamento, o emissor utiliza dois elementos básicos: retransmission queue e retransmission timer.

A transmissão de um pacote é seguida do início de um temporizador de retransmissão. Esse temporizador, quando chega a zero sem receber um ACK referente a ele, é o indicador de que aquele pacote foi perdido, e assim sua retransmissão é ativada.

A transmissão de um pacote é sempre seguida, também, da adição de uma cópia desse pacote na fila de retransmissão até o recebimento do ACK referente àquele pacote, que então é retirado da fila. A fila de retransmissão é definida como sk_write_queue, sendo um membro da struct sock dentro de include/net/sock.h.

Na fila de retransmissão existe três tags (1 bit) que marcam cada pacote:

  • SACKED(S), marcados na variável sacked_out. Calcula o número de pacotes que chegaram no receptor fora de ordem e dessa forma não foram reconhecidos. Se SACK é utilizado, basta contar o número de SACKs, caso não, deve ser calculado o número de ACKs duplicados.
  • RETRANS(R), marcados na variável retrans_out. Calcula (conta) a quantidade de pacotes retransmitidos.
  • LOST(L), marcados na variável lost_out. Calcula perdidos. Seu cálculo pode ser feito usando o FACK ou NewReno.

De toda forma, caso ocorra um Retransmission Timeout (RTO), o emissor em recovery e a janela de congestionamento é iniciada em um segmento e é retransmitida, já que ainda não foi reconhecida. Assim, depois de um RTO toda a fila é considerada perdida, e o lost_out é igual a packets_out.

Controle do Estado de Recovery

Dois tipos de ACK podem ser usados por receptores, Quick ACK e Delayed ACK. O Quick é geralmente utilizado no início de conexões, para que a janela de congestionamento cresça mais rapidamente. O delayed é geralmente usado após a conexão ter chegado a uma janela estável, para diminuir o número de processos do protocolo. No delayed ACK é geralmente enviado um ACK para vários pacotes. É possível alternar entre os tipos de ACK de acordo com a conexão.

Os ACKs são recebidos no tcp_acks(), no tcp_input.h. Essa função processa o número de sequência para determinar quais ações devem ser tomadas a partir do ACK recebido. Diversas ações podem ser necessárias de acordo com esse processamento, como limpar a fila de retransmissão, marcar um pacote SACKed, reenviar pacotes de ACKs duplicados, entre outros. Duas funções importantes, que podem ser acionadas pelo tcp_acks() de acordo com o ACK recebido, são descritas a seguir.

  • tcp_sacktag_write_queue()
    static int tcp_sacktag_write_queue(struct sock *sk,
                                       const struct sk_buff *ack_skb,
                                       u32 prior_snd_una,
                                       struct tcp_sacktag_state *state)
    

    Essa é a função responsável por processar SACKs recebidos. Ela checa, por exemplo, que bloco SACK está dentro do limite de sequência esperado, como por exemplo estar entre SND.UNA e SND.NXT5.

  • tcp_fastretrans_alert()
    static void tcp_fastretrans_alert(struct sock *sk,
                                      const u32 prior_snd_una,
                                      int num_dupack,
                                      int *ack_flag,
                                      int *rexmit)
    

    Aqui é implementado a lógica do recovery do TCP, Ele é acionado quando um ACK recebido não está em estado "Open" ou quando é recebido um SACK, ACK Duplicado ou ECN ECE. Essa função é chamada quando possivelmente existe algo de errado na conexão, então uma série de verificações são feitas, através de algumas funções:

    • tcp_check_sack_reneging()

      /* If ACK arrived pointing to a remembered SACK, it means that our
       * remembered SACKs do not reflect real state of receiver i.e.
       * receiver _host_ is heavily congested (or buggy).
       *
       * To avoid big spurious retransmission bursts due to transient SACK
       * scoreboard oddities that look like reneging, we give the receiver a
       * little time (max(RTT/2, 10ms)) to send us some more ACKs that will
       * restore sanity to the SACK scoreboard. If the apparent reneging
       * persists until this RTO then we'll clear the SACK scoreboard.
       */
      static bool tcp_check_sack_reneging(struct sock *sk, int flag)
      

      Verifica se o SACK recebido aponta para um SACK já armazenado. Caso sim, quer dizer que o SACK já armazenado não condiz com um problema de conexão: o receptor pode estar com problemas (congestionado ou com bugs).

    • tcp_time_to_recover()

      /* This function decides, when we should leave Disordered state
       * and enter Recovery phase, reducing congestion window.
       *
       * Main question: may we further continue forward transmission
       * with the same cwnd?
       */
      static bool tcp_time_to_recover(struct sock *sk, int flag)
      

      Verifica parâmetros para determinar se deve entrar em Recovery. Alguns dos parâmetros é a quantidade de pacotes perdidos.

    • tcp_update_scoreboard()

      static void tcp_update_scoreboard(struct sock *sk, int fast_rexmit)
      

      Essa função marca os pacotes perdidos. Ele é considerado perdido se os o último pacote SACKed tem um número sequencial maior ou igual ao espaço do pacote DUPACK.

    • tcp_xmit_retransmit_queue()

      /* This gets called after a retransmit timeout, and the initially
       * retransmitted data is acknowledged.  It tries to continue
       * resending the rest of the retransmit queue, until either
       * we've sent it all or the congestion window limit is reached.
       */
      void tcp_xmit_retransmit_queue(struct sock *sk)
      

      Implementado no tcp_output.h, decide o que será reenviado numa retransmissão para "preencher buracos" causados pela perda de pacotes.

Algoritmos de Controle de Congestionamento

É possível a utilização de outros algoritmos de CCG no Linux, e isso é feito através do arquivo tcp_cong.c. Nele está a implementação do New Reno e também a possibilidade de utilizar outros algoritmos de CCG. Funções como tcp_slow_start() operam a cwnd e são chamadas a partir de lugares diferentes como tcp_input.c ou de implementações de outros algoritmos. No Linux, cada implementação de um algoritmo de CCG é feito criando arquivo tcp_<algoritmo>.c. Assim, a implementação do Cubic é feita no arquivo tcp_cubic.c, a implementação do Vegas é feita no tcp_vegas.c e etc.

Para um algoritmo de CCG ser inicializado no Linux, ele deve criar um static record do struct tcp_congestion_ops, descrita no capítulo de Controle de Congestionamento. A seguir temos um exemplo da declaração dessa struct para uso do New Reno e Vegas.

/* inicializa a struct do new reno (tcp_cong.c) */
struct tcp_congestion_ops tcp_reno = {
        .flags      = TCP_CONG_NON_RESTRICTED,
        .name       = "reno",
        .owner      = THIS_MODULE,
        .ssthresh   = tcp_reno_ssthresh,
        .cong_avoid = tcp_reno_cong_avoid,
        .undo_cwnd  = tcp_reno_undo_cwnd,
};

/* inicializa a struct do vegas (tcp_vegas.c) */
static struct tcp_congestion_ops tcp_vegas __read_mostly = {
        .init       = tcp_vegas_init,
        .ssthresh   = tcp_reno_ssthresh,
        .undo_cwnd  = tcp_reno_undo_cwnd,
        .cong_avoid = tcp_vegas_cong_avoid,
        .pkts_acked = tcp_vegas_pkts_acked,
        .set_state  = tcp_vegas_state,
        .cwnd_event = tcp_vegas_cwnd_event,
        .get_info   = tcp_vegas_get_info,

        .owner      = THIS_MODULE,
        .name       = "vegas",
};

Essas implementações são registradas no sistema utilizando da função tcp_register_congestion_control(), implementada no tcp_cong.c

/* função que registra o struct tcp_vegas no sistema */
static int __init tcp_vegas_register(void)
{
        BUILD_BUG_ON(sizeof(struct vegas) > ICSK_CA_PRIV_SIZE);
        tcp_register_congestion_control(&tcp_vegas);
        return 0;
}

Referências

Comentários no Linux

O conteúdo abaixo foi retirado do código do Linux, do arquivo tcp_input.c. Ele foi usado como referência para a escrita do documento e por isso foi optado por deixá-lo como parte do conteúdo das referências.

/* This procedure tags the retransmission queue when SACKs arrive.
 *
 * We have three tag bits: SACKED(S), RETRANS(R) and LOST(L).
 * Packets in queue with these bits set are counted in variables
 * sacked_out, retrans_out and lost_out, correspondingly.
 *
 * Valid combinations are:
 * Tag  InFlight        Description
 * 0    1               - orig segment is in flight.
 * S    0               - nothing flies, orig reached receiver.
 * L    0               - nothing flies, orig lost by net.
 * R    2               - both orig and retransmit are in flight.
 * L|R  1               - orig is lost, retransmit is in flight.
 * S|R  1               - orig reached receiver, retrans is still in flight.
 * (L|S|R is logically valid, it could occur when L|R is sacked,
 *  but it is equivalent to plain S and code short-curcuits it to S.
 *  L|S is logically invalid, it would mean -1 packet in flight 8))
 *
 * These 6 states form finite state machine, controlled by the following events:
 * 1. New ACK (+SACK) arrives. (tcp_sacktag_write_queue())
 * 2. Retransmission. (tcp_retransmit_skb(), tcp_xmit_retransmit_queue())
 * 3. Loss detection event of two flavors:
 *      A. Scoreboard estimator decided the packet is lost.
 *         A'. Reno "three dupacks" marks head of queue lost.
 *      B. SACK arrives sacking SND.NXT at the moment, when the
 *         segment was retransmitted.
 * 4. D-SACK added new rule: D-SACK changes any tag to S.
 *
 * It is pleasant to note, that state diagram turns out to be commutative,
 * so that we are allowed not to be bothered by order of our actions,
 * when multiple events arrive simultaneously. (see the function below).
 *
 * Reordering detection.
 * --------------------
 * Reordering metric is maximal distance, which a packet can be displaced
 * in packet stream. With SACKs we can estimate it:
 *
 * 1. SACK fills old hole and the corresponding segment was not
 *    ever retransmitted -> reordering. Alas, we cannot use it
 *    when segment was retransmitted.
 * 2. The last flaw is solved with D-SACK. D-SACK arrives
 *    for retransmitted and already SACKed segment -> reordering..
 * Both of these heuristics are not used in Loss state, when we cannot
 * account for retransmits accurately.
 *
 * SACK block validation.
 * ----------------------
 *
 * SACK block range validation checks that the received SACK block fits to
 * the expected sequence limits, i.e., it is between SND.UNA and SND.NXT.
 * Note that SND.UNA is not included to the range though being valid because
 * it means that the receiver is rather inconsistent with itself reporting
 * SACK reneging when it should advance SND.UNA. Such SACK block this is
 * perfectly valid, however, in light of RFC2018 which explicitly states
 * that "SACK block MUST reflect the newest segment.  Even if the newest
 * segment is going to be discarded ...", not that it looks very clever
 * in case of head skb. Due to potentional receiver driven attacks, we
 * choose to avoid immediate execution of a walk in write queue due to
 * reneging and defer head skb's loss recovery to standard loss recovery
 * procedure that will eventually trigger (nothing forbids us doing this).
 *
 * Implements also blockage to start_seq wrap-around. Problem lies in the
 * fact that though start_seq (s) is before end_seq (i.e., not reversed),
 * there's no guarantee that it will be before snd_nxt (n). The problem
 * happens when start_seq resides between end_seq wrap (e_w) and snd_nxt
 * wrap (s_w):
 *
 *         <- outs wnd ->                          <- wrapzone ->
 *         u     e      n                         u_w   e_w  s n_w
 *         |     |      |                          |     |   |  |
 * |<------------+------+----- TCP seqno space --------------+---------->|
 * ...-- <2^31 ->|                                           |<--------...
 * ...---- >2^31 ------>|                                    |<--------...
 *
 * Current code wouldn't be vulnerable but it's better still to discard such
 * crazy SACK blocks. Doing this check for start_seq alone closes somewhat
 * similar case (end_seq after snd_nxt wrap) as earlier reversed check in
 * snd_nxt wrap -> snd_una region will then become "well defined", i.e.,
 * equal to the ideal case (infinite seqno space without wrap caused issues).
 *
 * With D-SACK the lower bound is extended to cover sequence space below
 * SND.UNA down to undo_marker, which is the last point of interest. Yet
 * again, D-SACK block must not to go across snd_una (for the same reason as
 * for the normal SACK blocks, explained above). But there all simplicity
 * ends, TCP might receive valid D-SACKs below that. As long as they reside
 * fully below undo_marker they do not affect behavior in anyway and can
 * therefore be safely ignored. In rare cases (which are more or less
 * theoretical ones), the D-SACK will nicely cross that boundary due to skb
 * fragmentation and packet reordering past skb's retransmission. To consider
 * them correctly, the acceptable range must be extended even more though
 * the exact amount is rather hard to quantify. However, tp->max_window can
 * be used as an exaggerated estimate.
 */

[...]

/* Linux NewReno/SACK/ECN state machine.
 * --------------------------------------
 *
 * "Open"       Normal state, no dubious events, fast path.
 * "Disorder"   In all the respects it is "Open",
 *              but requires a bit more attention. It is entered when
 *              we see some SACKs or dupacks. It is split of "Open"
 *              mainly to move some processing from fast path to slow one.
 * "CWR"        CWND was reduced due to some Congestion Notification event.
 *              It can be ECN, ICMP source quench, local device congestion.
 * "Recovery"   CWND was reduced, we are fast-retransmitting.
 * "Loss"       CWND was reduced due to RTO timeout or SACK reneging.
 *
 * tcp_fastretrans_alert() is entered:
 * - each incoming ACK, if state is not "Open"
 * - when arrived ACK is unusual, namely:
 *      * SACK
 *      * Duplicate ACK.
 *      * ECN ECE.
 *
 * Counting packets in flight is pretty simple.
 *
 *      in_flight = packets_out - left_out + retrans_out
 *
 *      packets_out is SND.NXT-SND.UNA counted in packets.
 *
 *      retrans_out is number of retransmitted segments.
 *
 *      left_out is number of segments left network, but not ACKed yet.
 *
 *          left_out = sacked_out + lost_out
 *
 *     sacked_out: Packets, which arrived to receiver out of order
 *                 and hence not ACKed. With SACKs this number is simply
 *                 amount of SACKed data. Even without SACKs
 *                 it is easy to give pretty reliable estimate of this number,
 *                 counting duplicate ACKs.
 *
 *       lost_out: Packets lost by network. TCP has no explicit
 *                 "loss notification" feedback from network (for now).
 *                 It means that this number can be only _guessed_.
 *                 Actually, it is the heuristics to predict lossage that
 *                 distinguishes different algorithms.
 *
 *      F.e. after RTO, when all the queue is considered as lost,
 *      lost_out = packets_out and in_flight = retrans_out.
 *
 *              Essentially, we have now a few algorithms detecting
 *              lost packets.
 *
 *              If the receiver supports SACK:
 *
 *              RFC6675/3517: It is the conventional algorithm. A packet is
 *              considered lost if the number of higher sequence packets
 *              SACKed is greater than or equal the DUPACK thoreshold
 *              (reordering). This is implemented in tcp_mark_head_lost and
 *              tcp_update_scoreboard.
 *
 *              RACK (draft-ietf-tcpm-rack-01): it is a newer algorithm
 *              (2017-) that checks timing instead of counting DUPACKs.
 *              Essentially a packet is considered lost if it's not S/ACKed
 *              after RTT + reordering_window, where both metrics are
 *              dynamically measured and adjusted. This is implemented in
 *              tcp_rack_mark_lost.
 *
 *              If the receiver does not support SACK:
 *
 *              NewReno (RFC6582): in Recovery we assume that one segment
 *              is lost (classic Reno). While we are in Recovery and
 *              a partial ACK arrives, we assume that one more packet
 *              is lost (NewReno). This heuristics are the same in NewReno
 *              and SACK.
 *
 * Really tricky (and requiring careful tuning) part of algorithm
 * is hidden in functions tcp_time_to_recover() and tcp_xmit_retransmit_queue().
 * The first determines the moment _when_ we should reduce CWND and,
 * hence, slow down forward transmission. In fact, it determines the moment
 * when we decide that hole is caused by loss, rather than by a reorder.
 *
 * tcp_xmit_retransmit_queue() decides, _what_ we should retransmit to fill
 * holes, caused by lost packets.
 *
 * And the most logically complicated part of algorithm is undo
 * heuristics. We detect false retransmits due to both too early
 * fast retransmit (reordering) and underestimated RTO, analyzing
 * timestamps and D-SACKs. When we detect that some segments were
 * retransmitted by mistake and CWND reduction was wrong, we undo
 * window reduction and abort recovery phase. This logic is hidden
 * inside several functions named tcp_try_undo_<something>.
 */

Footnotes:

1

Fast path is an alternative routing mechanism to the routing table. In fast path, the responses to incoming network traffic are sent back by using the same interface as the incoming traffic. By avoiding the routing table lookup, fast path provides a quick access to data.

2

Explicit Congestion Notification. ECN allows end-to-end notification of network congestion without dropping packets. ECN is an optional feature that may be used between two ECN-enabled endpoints when the underlying network infrastructure also supports it.

3

When a sender receives some duplicate ACKs, it can assume that the packet witch the ACK asks for has been dropped. The sender then will fast retransmit the dropped packet without waiting for that packet's RTO (timeout).

4

Conventionally, TCP/IP networks signal congestion by dropping packets. When ECN is successfully negotiated, an ECN-aware router may set a mark in the IP header instead of dropping a packet in order to signal impending congestion. The receiver of the packet echoes the congestion indication to the sender, which reduces its transmission rate as if it detected a dropped packet.

5

Send (SND) Pointers são ponteiros usados para determinar, na implementação, o que faz parte da janela deslizante, o que já foi reconhecido, etc. Send Unacknowledged, ou SND.UNA é o número sequencial do primeiro byte de dados que foi enviado mas ainda não foi reconhecido (unACKed). Os bytes anteriores ao SND.UNA são bytes enviados e reconhecidos. Send Next, ou SND.NXT é o número de sequência do próximo byte de dado que será enviado para o receptor. Send Window, ou SND.WND é o tamanho da janela deslizante.

Eduardo Freitas Licensed under CC BY-SA 4.0