linux部分内核源码解析

对于三次握手协议中的第三次握手的验证报文的合法性源码的解析。

tcp_check_req()来处理接收到的TCP段,判断是否是合法的TCP包。处理过程如下:

  1. 解析并获取段中的TCP选项。
  2. 校验TCP序号。
  3. 如果是SYN段,则作为SYN段再处理一次。
  4. 检测ACK段确认序号是否有效,无效则立即返回不作处理。
  5. 检测ACK段序号是否有效,无效则丢弃该段。
  6. 如果是RST段或者是新的SYN段,则向客户端返送RST段进行复位。
  7. 校验通过,创建相应的“子”传输控制块。
  8. 连接请求块插入已完成的连接的队列中,等到用户进程的accept()调用。

代码如下:

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
struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct request_sock **prev)
{
struct tcp_options_received tmp_opt;
const struct tcphdr *th = tcp_hdr(skb); /*获取段中的TCP选项*/
__be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST | TCP_FLAG_SYN | TCP_FLAG_ACK); /*获取TCP首部中的RST,SYN,ACK字段*/
bool paws_reject = false;
tmp_opt.saw_tstamp = 0;
if (th->doff > (sizeof(struct tcphdr) >> 2)) {
tcp_parse_options(skb, &tmp_opt, 0, NULL);
if(tmp_opt.saw_tstamp) { /*判断TCP选项中是否带有时间戳*/
tmp_opt.ts_recent = req->ts_recent; /*记录该时间戳*/
tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ) << req->retrans); /*记录下有效时间*/
paws_reject = tcp_paws_reject(&tmp_opt, th->rst); /*校验TCP序号是否有效*/
}
}
if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn && flg == TCP_FLAG_SYN
&& ! paws_reject) {/*如果接收到的是客户端重发的SYN段,则作为SYN段处理*/
req->rsk_ops->rtx_syn_ack(sk, req, NULL); /*调用连接请求,向客户端发送SYN+ACK,即重新握手*/
return NULL;
}
if ((flg & TCP_FLAG_ACK) && (TCP_SKB_CB(skb)->ack_seq !=
tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk)))) /* 如果接收段包含ACK标志,但确认序号不对,则返回“父”传输控制块,在上层函数处理
return sk;
/* 如果发生了回绕,或者接收序号不在接收窗口内 */
if (paws_reject || ! tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq, tcp_rsk(req)->rcv_isn + 1, tcp_rsk(req)->rcv_isn + 1 + req->rcv_wnd)) { /*如果ACK段序号无效或者序号不在接收窗口,则返回NULL,直接丢弃接收的段。*/
if (! (flg & TCP_FLAG_RST)) /*判断是不是RST段*/
req->rsk_ops->send_ack(sk, skb, req); /*如果不是,则给对端发送ACK段*/
if (paws_reject)
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
return NULL;
}
if (tmp_opt.saw_tstamp && ! after(TCP_SKB_CB(skb)->seq, tcp_rsk(req)->rcv_isn + 1)) /*如果有时间戳选项,同时ACK段序号正常*/
req->ts_recent = tmp_opt.rcv_tsval; /* 保存ACK段的时间戳 */
if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn) { /*如果ACK段的序号在接收窗口之外,则说明这是一个无效的SYN段*/
flg &= ~TCP_FLAG_SYN; /*去掉SYN标志*/
}
if (flg & (TCP_FLAG_RST | TCP_FLAG_SYN)) { /*如果段里有RST和SYN标志*/
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS); /*统计一下*/
goto embryonic_reset; /*跳转到rest复位未连接完成的连接*/
}
if (! (flg & TCP_FLAG_ACK)) /*按照正常的流程,该段中应该有ACK标志,如果没有,则直接返回NULL,丢弃该段*/
return NULL;
if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr)
tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr;
else if (req->retrans)
tcp_rsk(req)->snt_synack = 0;
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
if (child == NULL)
goto listen_overflow; /*目前为止,第三次握手的ACK是有效的*/
inet_csk_reqsk_queue_unlink(sk, req, prev); /* 把连接请求块从半连接队列中删除 */
inet_csk_reqsk_queue_removed(sk, req); /* 更新半连接队列的长度,如果为0,则删除定时器 */
inet_csk_reqsk_queue_add(sk, req, child); /* 把完成三次握手的连接请求块,和新的sock关联起来,并把它移入全连接队列中 */
return child;
listen_overflow:
if (! sysctl_tcp_abort_on_overflow) {
inet_rsk(req)->acked = 1;
return NULL;
}
/*如果是由于服务器繁忙或者其他原因导致连接建立失败,且未设置sysctl_tcp_abort_on_overflow,则设置连接请求块中的acked标志,表示已接收到作为第三次握手的ACK段。*/
embryonic_reset:
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);
if (! (flg & TCP_FLAG_RST))
req->rsk_ops->send_reset(sk, skb);
inet_csk_reqsk_queue_drop(sk, req, prev); /* 把连接请求块从半连接队列中删除,更新半连接队列 */
return NULL;
}

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
,