在TCP输入数据段的处理过程中,如果发现输入段携带了ACK信息,则会调用tcp_ack()进行ACK相关的处理。实际中,ACK信息总是会携带的,因为携带ACK不需要任何的附加开销,所以对于输入的每一个段(输了RST等特殊段),这个过程总是要执行的,这篇笔记就来看看TCP对ACK确认的处理过程。
1. 发送窗口
收到确认后,一项重要的工作就是更新发送窗口,所以这里再来认识一下发送窗口相关的几个关键字段,如下图所示:
注意,图中的发送窗口,指的是接收方通知给发送方的接收窗口大小,即流量控制窗口,并非拥塞窗口。
2. ACK处理
2.1 参数flag
tcp_ack()有个非常重要的参数flag,其贯穿整个ACK的处理过程,它记录了从输入段中能够获取到的任何信息(比如是否携带了数据、是否重复ACK、是否是SACK等),供后面的拥塞控制、RTT采样等操作参考。flag可能是如下值的组合。
flag | 值 | 描述 |
---|---|---|
FLAG_DATA | 0x01 | ACK段中携带了数据 |
FLAG_WIN_UPDATE | 0x02 | 收到ACK段后更新了发送窗口,可能更新了左边界,也有可能更新了右边界(通告窗口变大) |
FLAG_DATA_ACKED | 0x04 | ACK段确认了新数据 |
FLAG_RETRANS_DATA_ACKED | 0x08 | ACK段携带的数据已经收到过了 |
FLAG_SYN_ACKED | 0x10 | ACK段确认了SYN段 |
FLAG_DATA_SACKED | 0x20 | ACK段确认了新的数据 |
FLAG_ECE | 0x40 | 该ACK段携带了ECE标志 |
FLAG_DATA_LOST | 0x80 | SACK检测到了数据丢失 |
FLAG_SLOWPATH | 0x100 | 该ACK段是由慢速路径处理的 |
FLAG_ONLY_ORIG_SACKED | 0x200 | |
FLAG_SND_UNA_ADVANCED | 0x400 | ACK段更新了snd_una,即收到ACK后,发送窗口左边界可以右移 |
FLAG_DSACKING_ACK | 0x800 | ACK段中包含有DSACK信息 |
FLAG_NONHEAD_RETRANS_ACKED | 0x1000 | |
FLAG_SACK_RENEGING | 0x2000 | 检测到之前SACK确认过的数据段被对端丢弃了(这是协议允许的) |
此外,还定义了一些基本flag的组合:
#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED)
//用于判断输入的数据段是否为重复段
#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE)
#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED)
#define FLAG_ANY_PROGRESS (FLAG_FORWARD_PROGRESS|FLAG_SND_UNA_ADVANCED)
2.2 tcp_ack()
/* This routine deals with incoming acks, but not outgoing ones. */
static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
//TCB中尚未被确认的最小序号
u32 prior_snd_una = tp->snd_una;
//ACK段中的序号
u32 ack_seq = TCP_SKB_CB(skb)->seq;
//ACK段中的确认号
u32 ack = TCP_SKB_CB(skb)->ack_seq;
u32 prior_in_flight;
u32 prior_fackets;
int prior_packets;
int frto_cwnd = 0;
/* If the ack is newer than sent or older than previous acks
* then we can probably ignore it.
*/
//确认的是还没有发送的数据,这是无意义的确认,直接返回
if (after(ack, tp->snd_nxt))
goto uninteresting_ack;
//该确认号已经收到过了。这种可能是重复ACK,也有可能是正常的,比如该AC段有延时。
//这种ACK有可能还携带了有效的SACK信息
if (before(