# Linux E 100 网卡 与 TCP 层原理 四

# tcp_v4_rcv 函数

该函数用于处理传输层 TCP 协议的报文。在看源码前,读者应该复习下 TCP 头部的信息 和 TCP 状态转换关系。为了方便读者查阅,这里将TCP头部的图和转换关系放置在下面。

img

img

int tcp_v4_rcv(struct sk_buff *skb)
{
    struct tcphdr *th;
    struct sock *sk;
    int ret;if (skb->pkt_type != PACKET_HOST) // 报文不是发给自己的,直接丢弃
        goto discard_it;
    ...
    if (!pskb_may_pull(skb, sizeof(struct tcphdr))) // 检测 TCP 头部长度是否满足要求
        goto discard_it;
    th = skb->h.th; // 取出头部指针
    if (th->doff < sizeof(struct tcphdr) / 4) // 检测首部长度位
        goto bad_packet;
    if (!pskb_may_pull(skb, th->doff * 4)) // 检测报文中首部长度是否为 doff 指定的大小(doff 标识该tcp头部有多少个32bit字(4字节)因为4位最大能表示15,所以tcp头部最长是60字节)
        goto discard_it;if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
         tcp_v4_checksum_init(skb) < 0)) // 检测 16 位的 TCP 校验和(由发送端填充,接收端对tcp报文段执行CRC算法以校验tcp报文段在传输过程中是否损坏。注意,这个校验不仅包括tcp头部,也包括数据部分。TCP 可靠传输中 检测报文正确的必要手段)
        goto bad_packet;
    th = skb->h.th; // 上述处理可能修改了 tcp 头部指针,所以这里重新获取一次
    // 将 报文头部中的 标志位设置到 tcb 控制块中
    TCP_SKB_CB(skb)->seq = ntohl(th->seq);
    TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
                    skb->len - th->doff * 4);
    TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
    TCP_SKB_CB(skb)->when    = 0;
    TCP_SKB_CB(skb)->flags   = skb->nh.iph->tos;
    TCP_SKB_CB(skb)->sacked  = 0;
​
    sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
                 skb->nh.iph->daddr, ntohs(th->dest),
                 tcp_v4_iif(skb)); // 查找用于处理该 TCP 信息的 sock 结构
    if (!sk) // 如果没有 sock 可以处理该 报文,那么直接丢弃
        goto no_tcp_socket;
​
process: // 设置好信息后,开始处理 TCP 报文
    if (sk->sk_state == TCP_TIME_WAIT) // sock 状态为 TIME_WAIT 状态
        goto do_time_wait;if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) // 执行 xfrm 框架(XFRM是 Linux 2.6 内核为安全处理引入的一个可扩展功能框架,用来在数据包经过路由路径的过程中对其进行修改,包含 3 种数据结构:策略(xfrm policy),模板(template)和状态(xfrm state)。这里了解下即可)
        goto discard_and_relse;if (sk_filter(sk, skb, 0)) // 回调放置在 sock 结构中的 sk_filter 过滤器(类比于责任链模式)
        goto discard_and_relse;
    ...
    if (!sock_owned_by_user(sk)) { // 若当前sk没有被用户锁定,那么调用 tcp_v4_do_rcv 处理该数据包
        if (!tcp_prequeue(sk, skb)) // 若没有开启 tcp_low_latency 参数,那么 skb 直接添加到 prequeue中,入队之后,若达到队列长度上限或者内存上限,则将队列中的skb出队,再调用 tcp_v4_do_rcv 处理 (此时,处理 tcp 报文的操作将由 进程上下文来处理,而非当前 软中断上下文,所以将会对数据包的处理造成延迟,因为需要唤醒等待数据的进程在内核中处理)
            ret = tcp_v4_do_rcv(sk, skb); // 若 tcp_low_latency 低延迟参数开启,那么立即在当前 软中断 处理中完成 TCP 包的处理
    } else // 否则将数据包添加到 backlog 队列中,等待回调处理
        sk_add_backlog(sk, skb);
    ...
}// sock 被锁定,那么将数据包暂时放置到 sk_backlog 队列中,sock 解锁后将会处理该队列中的数据
#define sk_add_backlog(__sk, __skb)             
do {    if (!(__sk)->sk_backlog.tail) {             
        (__sk)->sk_backlog.head =           
             (__sk)->sk_backlog.tail = (__skb);     
    } else {                        
        ((__sk)->sk_backlog.tail)->next = (__skb);  
        (__sk)->sk_backlog.tail = (__skb);      
    }                           
    (__skb)->next = NULL;                   
} while(0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

# tcp_v4_do_rcv 函数

该函数用于处理 TCP 报文。至此将会根据 sock 的 TCP 状态来完成状态流转处理。该函数较为简单:根据状态分派处理流程到不同函数。

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
    if (sk->sk_state == TCP_ESTABLISHED) { //  当前为 ESTABLISHED 已经建立连接状态(大部分处理都处于该状态,所以将其提前判断)
        TCP_CHECK_TIMER(sk);
        if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
            goto reset;
        TCP_CHECK_TIMER(sk);
        return 0;
    }if (skb->len < (skb->h.th->doff << 2) || tcp_checksum_complete(skb)) // 头部长度和校验和检测没问题
        goto csum_err;if (sk->sk_state == TCP_LISTEN) { // sock 状态处于 监听连接状态,那么处理 TCP 握手
        struct sock *nsk = tcp_v4_hnd_req(sk, skb);
        if (!nsk)
            goto discard;if (nsk != sk) {
            if (tcp_child_process(sk, nsk, skb))
                goto reset;
            return 0;
        }
    }
    ...
    if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len)) // 处理其他状态的报文
        goto reset;
    return 0;
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# tcp_rcv_established 函数

该函数用于处理 ESTABLISHED 状态的报文。该函数处理较为复杂不过也有迹可循:

1、PAWS 机制 校验

2、如果处理当前进程上下文,sock 也属于该进程处理,那么直接复制数据到进程指定的 iovec 中

3、否则将 skb 调用 __skb_queue_tail(&sk->sk_receive_queue, skb) 函数添加到 sock 的接受队列 sk_receive_queue 中

直接总结:该函数将TCP数据包的处理分为2类:fast path(快速路径) 和 slow path(全路径),其含义显而易见。这样分类的目的当然是加快数据包的处理,因为在正常情况下,数据包是按顺序到达的,网络状况也是稳定的,这时可以按照fast path 快速将数据包存放入 receive queue 接受队列,而在其他的情况下则需要走slow path 流程了。

int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, struct tcphdr *th, unsigned len){
    struct tcp_opt *tp = tcp_sk(sk); // 获取 sock 中对 TCP的状态信息
    tp->saw_tstamp = 0;
    if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags && // 这里注意:pred_flags标志位,该标志位被前一个报文所设置,用于判断当前报文是否为预期顺序到达的报文,若是,那么直接进入 fast path 快速入队
        TCP_SKB_CB(skb)->seq == tp->rcv_nxt) { // TCP seq 序列号 与 TCP 状态信息中的 rcv_nxt 接受序列号相同,那么该数据为我们想要的正确且顺序到达的数据
        int tcp_header_len = tp->tcp_header_len;
         // 检测 timestamp 时间戳 (也即 PAWS 机制 (Protection Against Wrapped Sequences),基于TCP的 Timestamps 选项实现,用于拒绝接收到的过期的重复报文。PAWS 机制假设每个报文都携带有 TSopt 选项数据,其中的时间戳 TSval 保持单调递增,所以,当接收到一个报文的 TSval 值 小于 之前在此连接中接收到的时间戳(ts_recent),即认定此报文为过期报文,将其丢弃)
        if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) {
            __u32 *ptr = (__u32 *)(th + 1);
            if (*ptr != ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
                      | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP))
                goto slow_path; // 报文中的时间戳校验失败,进入 慢处理路径// 保存最后一次处理该 seq 的报文的事件戳数据( rcv_tsval 为处理时间戳,rcv_tsecr为回复时间戳)
            tp->saw_tstamp = 1;
            ++ptr; 
            tp->rcv_tsval = ntohl(*ptr);
            ++ptr;
            tp->rcv_tsecr = ntohl(*ptr);
            if ((s32)(tp->rcv_tsval - tp->ts_recent) < 0) // 如果 PAWS 机制检测失败(序号回转),转入慢速路径处理
                goto slow_path;
        }if (len <= tcp_header_len) { // 接收到的数据包小于或等于 TCP 头部长度
            if (len == tcp_header_len) { // 若等于TCP头部长度,那么不包含任何数据的 TCP 报文,直接丢弃
                if (tcp_header_len ==
                    (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
                    tp->rcv_nxt == tp->rcv_wup)
                    tcp_store_ts_recent(tp);
                tcp_ack(sk, skb, 0); // 处理接收到的 ack 确认信号
                __kfree_skb(skb);  // 释放 skb
                tcp_data_snd_check(sk); // 检测是否需要发送数据到客户端,若需要那么将响应报文发出并检查发送缓冲区大小
                return 0;
            } else { // 接收到的数据包小于或等于 TCP 头部长度,此时该报文为错误报文,直接丢弃
                TCP_INC_STATS_BH(TcpInErrs);
                goto discard;
            }
        } else { // TCP 头部存在数据,那么进一步处理
            int eaten = 0;
            if (tp->ucopy.task == current &&
                tp->copied_seq == tp->rcv_nxt &&
                len - tcp_header_len <= tp->ucopy.len &&
                sock_owned_by_user(sk)) { // 当前处理代码为当前进程上下文,而非中断上下文
                __set_current_state(TASK_RUNNING);
                if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) { // 将TCP报文中的数据复制到进程指定的 iovec 向量中
                    if (tcp_header_len ==
                        (sizeof(struct tcphdr) +
                         TCPOLEN_TSTAMP_ALIGNED) &&
                        tp->rcv_nxt == tp->rcv_wup)
                        tcp_store_ts_recent(tp);
                    __skb_pull(skb, tcp_header_len);
                    tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; // 更新下一次期望接受到的seq
                    NET_INC_STATS_BH(TCPHPHitsToUser);
                    eaten = 1; // 标识成功接收到数据
                }
            }
            if (!eaten) { // 非当前进程上下文或者复制数据失败
                if (tcp_checksum_complete_user(sk, skb)) // 完整检测校验和
                    goto csum_error;
                ...
                if ((int)skb->truesize > sk->sk_forward_alloc) // skb 长度 大于 预分配长度
                    goto step5;
                NET_INC_STATS_BH(TCPHPHits);
                __skb_pull(skb,tcp_header_len); // 将 skb 的数据指针 去掉 tcp 头部
                __skb_queue_tail(&sk->sk_receive_queue, skb); // 将 skb 添加到 sock 接收队列中
                tcp_set_owner_r(skb, sk); // 设置当前 skb 归属于 sk
                tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; // 更新下一次需要接受到的数据(SEQ + FIN + SYN + datalen)
            }
            tcp_event_data_recv(sk, tp, skb); // 数据成功接受,那么设置相关值并且尝试增加滑动窗口
            ...
            if (eaten) {  // skb 处理成功,那么发送 ack 确认信号
                if (tcp_in_quickack_mode(tp)) { // 快速发送 ack
                    tcp_send_ack(sk);
                } else {
                    tcp_send_delayed_ack(sk); // 累积确认
                }
            } else {
                __tcp_ack_snd_check(sk, 0);
            }
​
no_ack:
            if (eaten)
                __kfree_skb(skb);
            else // 若添加到了 sk_receive_queue 队列中,那么回调 sk_data_ready 方法通知并唤醒进程处理数据
                sk->sk_data_ready(sk, 0);
            return 0;
        }
    }
​
slow_path: // 慢路径(也即全路径处理)
    if (len < (th->doff<<2) || tcp_checksum_complete_user(sk, skb)) // 检测头部长度和校验和
        goto csum_error;/*
     * RFC1323 规定:先进行 PAWS 检测
     */
    if (tcp_fast_parse_options(skb, th, tp) && tp->saw_tstamp &&
        tcp_paws_discard(tp, skb)) {
        if (!th->rst) { // 检测失败,但是对于 RST 重置报文 需要接收处理,其他报文丢弃
            NET_INC_STATS_BH(PAWSEstabRejected);
            tcp_send_dupack(sk, skb); // 发送SACK或者ACK。注:SACK 为 Selective Acknowledgment 选择确认机制。TCP通信时,如果某个TCP报文段丢失,则TCP模块会重传最后被确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段也可能重复发送,从而降低了TCP性能。SACK技术正是为改善这种情况而产生的,它使TCP模块只重新发送丢失的TCP报文段,不用发送所有未被确认的TCP报文段。选择性确认选项用在连接初始化时,表示是否支持SACK技术。我们可以通过修改/proc/sys/net/ipv4/tcp_sack内核变量来启用或关闭选择性确认选项。
            goto discard;
        }
    }
    
    // 标准慢处理流程开始
    
    // 检查TCP报文序号是否有效
    if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) { 
        // 在除 SYN-SENT 状态外的所有状态中,所有重置(RST)段都检查 seq 字段进行验证。如果一个传入的TCP报文是不可接受的,应该发送一个 ack 确认号(除非接受报文设置了RST位,如果设置了,则丢弃该段并返回)。
        if (!th->rst)
            tcp_send_dupack(sk, skb);
        goto discard;
    }if(th->rst) { // 处理重置报文
        tcp_reset(sk);
        goto discard;
    }
    tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
    if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) { // syn 同步信号,但 seq 校验失败(也即旧的syn报文)
        TCP_INC_STATS_BH(TcpInErrs);
        NET_INC_STATS_BH(TCPAbortOnSyn);
        tcp_reset(sk);
        return 1;
    }
​
step5:
    if(th->ack) // 处理 ack 报文应答
        tcp_ack(sk, skb, FLAG_SLOWPATH);// 处理 TCP 该报文中 urg 标志位的紧急报文处理,快速提交给进程
    tcp_urg(sk, skb, th);
 
    // 处理 tcp 数据段
    tcp_data_queue(sk, skb);
    tcp_data_snd_check(sk);
    tcp_ack_snd_check(sk);
    return 0;
csum_error:
    TCP_INC_STATS_BH(TcpInErrs);
discard:
    __kfree_skb(skb); // 丢弃报文时,将 skb 释放
    return 0;
}
​
​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

# tcp_data_queue 函数

该函数用于处理 tcp 报文中的数据。可以看到其中的处理流程:

1、若数据包顺序到达,那么添加到 sk_receive_queue 接受队列,如果非sock 工作的进程上下文 则唤醒进程

2、若数据包乱序到达,那么将其添加到 out_of_order_queue 乱序队列,等待下一次报文到达时处理

static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
{
    struct tcphdr *th = skb->h.th;
    struct tcp_opt *tp = tcp_sk(sk);
    int eaten = -1;if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) // seq  等于 end_seq ,也即报文没有数据,直接丢弃
        goto drop;
​
    th = skb->h.th; 
    __skb_pull(skb, th->doff*4); // 将 skb 的数据指针移动到数据区域TCP_ECN_accept_cwr(tp, skb);if (tp->dsack) { // DSACK 也即 数据报文里存在选择确认报文段
        tp->dsack = 0;
        tp->eff_sacks = min_t(unsigned int, tp->num_sacks,
                            4 - tp->tstamp_ok);
    }// 将数据排队传递到处理进程。数据包按顺序进入接收队列。乱序的数据包发送到 out_of_order_queue。
    if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) { // 顺序数据包处理
        if (tcp_receive_window(tp) == 0) // 接收窗口为0,丢弃报文
            goto out_of_window;// 接受数据进程为当前进程,同时报文处于接收窗口之内,那么将当前数据包调用 skb_copy_datagram_iovec 函数复制到 用户指定的 iovec向量中
        if (tp->ucopy.task == current &&
            tp->copied_seq == tp->rcv_nxt && tp->ucopy.len &&
            sock_owned_by_user(sk) && !tp->urg_data) {
            int chunk = min_t(unsigned int, skb->len,
                            tp->ucopy.len);__set_current_state(TASK_RUNNING);local_bh_enable(); // 复制过程中开启软中断,该函数同时会检测软中断执行
            if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) {
                tp->ucopy.len -= chunk;
                tp->copied_seq += chunk;
                eaten = (chunk == skb->len && !th->fin);
            }
            local_bh_disable();
        }if (eaten <= 0) { // 若数据包没有复制到进程 iovec 中,那么需要将其放入 sk_receive_queue 接受队列
queue_and_out:
            if (eaten < 0 &&
                (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
                 !tcp_rmem_schedule(sk, skb))) {
                if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
                    goto drop;
            }
            tcp_set_owner_r(skb, sk);
            __skb_queue_tail(&sk->sk_receive_queue, skb);
        }
        tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; // 更新下一次希望接收到的数据序列号
        if(skb->len) // skb 存在数据,那么计算内存,同时尝试增加接受窗口
            tcp_event_data_recv(sk, tp, skb);
        if(th->fin) // 处理 fin 终止标志位报文
            tcp_fin(skb, sk, th);if (skb_queue_len(&tp->out_of_order_queue)) { // 若存在乱序队列,那么尝试对乱序队列中的报文处理:与当前报文seq 相邻的数据包
            tcp_ofo_queue(sk);
            if (!skb_queue_len(&tp->out_of_order_queue))
                tp->ack.pingpong = 0;
        }
        if (tp->num_sacks) // 处理 SACK
            tcp_sack_remove(tp);
        tcp_fast_path_check(sk, tp); // 设置 下一次 处理报文时的 头部预测位 (也即下一次判定是否可以进入 fast path 的依据)
        if (eaten > 0) // 处理完成释放skb
            __kfree_skb(skb);
        else if (!sock_flag(sk, SOCK_DEAD))
            sk->sk_data_ready(sk, 0); // 同时通知进程当前数据包的到来(这里调用的前提注意:前面的 tp->ucopy.task == current 条件判断失败,也即:当前非处理数据包的进程上下文)
        return;
    }if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { // 之前已经接收过的报文,直接丢弃并且立即发送ack
        NET_INC_STATS_BH(DelayedACKLost);
        tcp_dsack_set(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
​
out_of_window:
        tcp_enter_quickack_mode(tp);
        tcp_schedule_ack(tp);
drop:
        __kfree_skb(skb);
        return;
    }// 滑动窗口外的数据报文,例如 0 窗口 探测报文处理
    if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))
        goto out_of_window;tcp_enter_quickack_mode(tp);
    
    // 区间报文: seq < rcv_next < end_seq ,也即报文中包含之前接收的数据与后续需要的数据
    if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
        
        SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
               tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
               TCP_SKB_CB(skb)->end_seq);tcp_dsack_set(tp, TCP_SKB_CB(skb)->seq, tp->rcv_nxt); // 使用 SACK 机制通知发送端信息
        // 计算当前实际接收窗口,如果窗口关闭,则丢弃末尾的 TCP 数据包
        if (!tcp_receive_window(tp))
            goto out_of_window;
        goto queue_and_out; // 否则添加到接收队列
    }TCP_ECN_check_ce(tp, skb);
    // sk 分配的内存大于 sk 实际接收缓冲区大小,那么尝试减少已分配的内存,试图再次使套接字在其内存限制内
    if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
        !tcp_rmem_schedule(sk, skb)) {
        if (tcp_prune_queue(sk) < 0 || !tcp_rmem_schedule(sk, skb))
            goto drop;
    }// 处理乱序到达的 TCP 报文(rcv_next < seq)
    tp->pred_flags = 0;
    tcp_schedule_ack(tp);
    SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
           tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
    tcp_set_owner_r(skb, sk);
    if (!skb_peek(&tp->out_of_order_queue)) { // 乱序队列为空,那么初始化
        if (tp->sack_ok) { // 设置 SACK 的 selective_acks 为已经收到的 报文序列,用于报告发送端当前发生乱序而未确认的报文段
            tp->num_sacks = 1;
            tp->dsack     = 0;
            tp->eff_sacks = 1;
            tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;
            tp->selective_acks[0].end_seq =
                        TCP_SKB_CB(skb)->end_seq;
        }
        __skb_queue_head(&tp->out_of_order_queue,skb);  // 将报文段添加到 乱序队列中
    } else { // 乱序队列不为空,那么将该报文段找到合适位置插入乱序队列中的正确位置
        struct sk_buff *skb1 = tp->out_of_order_queue.prev; // 乱序队列末尾结点(双向列表 prev 指向末尾)
        u32 seq = TCP_SKB_CB(skb)->seq; // 当前报文段的序列号
        u32 end_seq = TCP_SKB_CB(skb)->end_seq; // 当前报文段的长度末尾序号if (seq == TCP_SKB_CB(skb1)->end_seq) { // 当前报文段的序列号正好等于最后的报文段,那么直接插入
            __skb_append(skb1, skb);
            if (!tp->num_sacks ||
                tp->selective_acks[0].end_seq != seq)
                goto add_sack; 
            tp->selective_acks[0].end_seq = end_seq;
            return;
        }
        
        // 否则遍历链表找到合适位置插入当前报文段
        do {
            if (!after(TCP_SKB_CB(skb1)->seq, seq))
                break;
        } while ((skb1 = skb1->prev) !=
             (struct sk_buff*)&tp->out_of_order_queue);// 检测当前 skb 的报文段是否与前一个报文段重叠
        if (skb1 != (struct sk_buff*)&tp->out_of_order_queue &&
            before(seq, TCP_SKB_CB(skb1)->end_seq)) {
            if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
                __kfree_skb(skb);
                tcp_dsack_set(tp, seq, end_seq);
                goto add_sack;
            }
            if (after(seq, TCP_SKB_CB(skb1)->seq)) { // 处理部分重叠
                tcp_dsack_set(tp, seq, TCP_SKB_CB(skb1)->end_seq);
            } else { 
                skb1 = skb1->prev;
            }
        }
        __skb_insert(skb, skb1, skb1->next, &tp->out_of_order_queue); // 插入乱序队列
        
        // 清除重叠部分的报文段为整个段
        while ((skb1 = skb->next) !=
               (struct sk_buff*)&tp->out_of_order_queue &&
               after(end_seq, TCP_SKB_CB(skb1)->seq)) {
               if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
                   tcp_dsack_extend(tp, TCP_SKB_CB(skb1)->seq, end_seq);
                   break;
               }
               __skb_unlink(skb1, skb1->list);
               tcp_dsack_extend(tp, TCP_SKB_CB(skb1)->seq, TCP_SKB_CB(skb1)->end_seq);
               __kfree_skb(skb1);
        }
add_sack:
        if (tp->sack_ok)
            tcp_sack_new_ofo_skb(sk, seq, end_seq);
    }
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188