8

linux部分内核源码解析

 2 years ago
source link: https://err0rzz.github.io/2018/06/15/linux%E9%83%A8%E5%88%86%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

linux部分内核源码解析

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

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

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

代码如下:

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;


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK