连接请求块(request_sock)之于TCP三次握手,就如同网络数据包(sk_buff)之于网络协议栈,都是核心的数据结构。

内核版本:3.6

Author:zhangskd @ csdn blog

存储队列

连接请求块的存储队列:包括全连接队列、半连接队列。

  1. /**
  2. * @icsk_accept_queue: FIFO of established children
  3. */
  4. struct inet_connection_sock {
  5. ...
  6. /* 存放SYN_RECV、ESTABLISHED状态的连接请求块tcp_request_sock */
  7. struct request_sock_queue icsk_accept_queue;
  8. ...
  9. };
  1. /* struct request_sock_queue - queue of request_sock
  2. * @rskq_accept_head: FIFO head of established children
  3. * @rskq_accept_tail: FIFO tail of established children
  4. * @syn_wait_lock: serializer
  5. * @rskq_defer_accept: User waits for some data after accept()
  6. */
  7.  
  8. struct request_sock_queue {
  9. struct request_sock *rskq_accept_head; /* ESTABLISHED状态请求块的头,等待accept */
  10. struct request_sock *rskq_accept_tail; /* ESTABLISHED状态请求块的尾 */
  11.  
  12. rwlock_t syn_wait_lock; /* listen_opt及其成员的读写锁 */
  13. u8 rskq_defer_accept;
  14.  
  15. /* 3 byte hole, try to pack */
  16. struct listen_sock *listen_opt; /* SYN_RECV状态的请求块,等待ACK */
  17. };

连接请求块(request_sock)保存在两个队列中:

(1) 处于SYN_RECV状态,即半连接队列

保存在icsk->icsk_accept_queue.listen_opt中,这个listen_sock实例在listen()后创建。

当客户端的ACK到达时,连接请求块会被移动到ESTABLISHED状态的连接请求块队列中。

注意,半连接队列是在listen()时创建的。

(2) 处于ESTABLISHED状态,即全连接队列

保存在icsk->icsk_accept_queue.rskq_accept_head和icsk->icsk_accept_queue.rskq_accept_tail

所指定的FIFO队列中。此队列的连接请求块等待accept()的获取。

listen_sock用于保存SYN_RECV状态的连接请求块,它的实例在listen()时创建。

  1. /* struct listen_sock - listen state
  2. *
  3. * @max_qlen_log - log_2 of maximal queued SYNs/REQUSTs
  4. */
  5.  
  6. struct listen_sock {
  7. u8 max_qlen_log; /* 半连接队列最大长度的log2 */
  8. u8 synflood_warned; /* SYN Flood标志 */
  9. /* 2 bytes hole, try to use */
  10. int qlen; /* 当前连接请求块的数目 */
  11. int qlen_young; /* 当前未重传过SYNACK的请求块数目 */
  12. int clock_hand;
  13. u32 hash_rnd; /* 一个随机数,用于计算hash值 */
  14. u32 nr_table_entries; /* 半连接队列最大长度 */
  15. struct request_sock *syn_table[0]; /* 连接请求块的指针数组 */
  16. };

连接请求块

最基本表示。

  1. /* struct request_sock - mini sock to represent a connection request */
  2. struct request_sock {
  3. struct request_sock *dl_next; /* Must be first member! */
  4.  
  5. u16 mss; /* 客户端通告的MSS */
  6. u8 retrans; /* SYNACK的重传次数 */
  7. u8 cookie_ts; /* syncookie: encode tcpopts in timestamp */
  8. u32 window_clamp; /* 本端的最大通告窗口 */
  9. u32 rcv_wnd; /* 本端的接收窗口大小 */
  10. u32 ts_recent; /* 下个发送段的时间戳回显值 */
  11. unsigned long expires; /* SYNACK的超时时间 */
  12. const struct request_sock_ops *rsk_ops; /* 指向tcp_request_sock_ops,操作函数 */
  13. struct sock *sk; /* 连接建立之前无效 */
  14. u32 secid;
  15. u32 peer_secid;
  16. };

inet层表示。

  1. struct inet_request_sock {
  2. struct request_sock req;
  3.  
  4. #if IS_ENABLED(CONFIG_IPV6)
  5. u16 inet6_rsk_offset;
  6. #endif
  7.  
  8. __be16 loc_port; /* 本端端口 */
  9. __be32 loc_addr; /* 本端IP */
  10. __be32 rmt_addr; /* 客户端IP */
  11. __be16 rmt_port; /* 客户端端口 */
  12.  
  13. kmemcheck_bitfield_begin(flags);
  14. u16 snd_wscale : 4, /* 对端的扩大因子 */
  15. rcv_wscale : 4, /* 本端的扩大因子 */
  16. tstamp_ok : 1, /* 连接是否支持TIMESTAMP选项 */
  17. sack_ok : 1, /* 连接是否支持SACK选项 */
  18. wscale_ok : 1, /* 连接是否支持Window Scale选项 */
  19. ecn_ok : 1, /* 连接是否支持ECN选项 */
  20. acked : 1,
  21. no_srccheck : 1;
  22. kmemcheck_bitfield_end(flags);
  23.  
  24. struct ip_options_rcu *opt; /* IP选项 */
  25. };

TCP层表示为tcp_request_sock。

  1. struct tcp_request_sock {
  2. struct inet_request_sock req;
  3.  
  4. #ifdef CONFIG_TCP_MD5SIG
  5. /* Only used by TCP MD5 Signature so far. */
  6. const struct tcp_request_sock_ops *af_specific;
  7. #endif
  8.  
  9. u32 rcv_isn; /* 客户端的初始序列号 */
  10. u32 snt_isn; /* 本端的初始序列号 */
  11. u32 snt_synack; /* synack sent time */
  12. };

操作函数

request_sock_ops为处理连接请求块的函数指针表,对于TCP,它的实例为tcp_request_sock_ops。

  1. struct request_sock_ops {
  2. int family; /* 所属的协议族 */
  3. int obj_size; /* 连接请求块的大小 */
  4. struct kmem_cache *slab; /* 连接请求块的高速缓存 */
  5. char *slab_name;
  6.  
  7. /* 重传SYNACK */
  8. int (*rtx_syn_ack) (struct sock *sk, struct request_sock *req, struct request_values *rvp);
  9.  
  10. /* 发送ACK */
  11. void (*send_ack) (struct sock *sk, struct sk_buff *skb, struct request_sock *req);
  12.  
  13. /* 发送RST */
  14. void (*send_reset) (struct sock *sk, struct sk_buff *skb);
  15.  
  16. /* 析构函数 */
  17. void (*destructor) (struct request_sock *req);
  18.  
  19. /* SYNACK超时处理函数 */
  20. void (*syn_ack_timeout) (struct sock *sk, struct request_sock *req);
  21. };

对于TCP,它的实例为tcp_request_sock_ops。

  1. struct request_sock_ops tcp_request_sock_ops__read_mostly = {
  2. .family = PF_INET,
  3. .obj_size = sizeof(struct tcp_request_sock),
  4.  
  5. .rtx_syn_ack = tcp_v4_rtx_synack,
  6. .send_ack = tcp_v4_reqsk_send_ack,
  7. .destructor = tcp_v4_reqsk_destructor,
  8. .send_reset = tcp_v4_send_reset,
  9. .syn_ack_timeout = tcp_syn_ack_timeout,
  10. };

建立连接时处理

(1)分配

从缓存块中分配一个request_sock实例,并指定此实例的操作函数集。

  1. static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops)
  2. {
  3. struct request_sock *req = reqsk_alloc(ops); /* 分配一个连接请求块 */
  4. struct inet_request_sock *ireq = inet_rsk(req);
  5.  
  6. if (req != NULL) {
  7. kmemcheck_annotate_bitfield(ireq, flags);
  8. ireq->opt = NULL;
  9. }
  10.  
  11. return req;
  12. }
  13.  
  14. static inline struct request_sock *reqsk_alloc(const struct request_sock_ops *ops)
  15. {
  16. struct request_sock *req = kmem_cache_alloc(ops->slab, GFP_ATOMIC); /* 从缓存块中分配 */
  17. if (req != NULL)
  18. req->rsk_ops = ops; /* 指定此连接请求块的操作函数集 */
  19. return req;
  20. }

(2)释放

释放一个request_sock实例。

  1. static inline void reqsk_free(struct request_sock *req)
  2. {
  3. req->rsk_ops->destructor(req); /* 对于TCP是tcp_v4_reqsk_destructor() */
  4. __reqsk_free(req);
  5. }
  6.  
  7. /* IPv4 request_sock destructor. */
  8. static void tcp_v4_reqsk_destructor(struct request_sock *req)
  9. {
  10. kfree(inet_rsk(req)->opt); /* 释放IP选项 */
  11. }
  12.  
  13. static inline void __reqsk_free(struct request_sock *req)
  14. {
  15. kmem_cache_free(req->rsk_ops->slab, req);
  16. }

(3)初始化

初始化连接请求块,包括request_sock、inet_request_sock、tcp_request_sock。

  1. static inline void tcp_openreq_init(struct request_sock *req,
  2. struct tcp_options_received *rx_opt, struct sk_buff *skb)
  3. {
  4. struct inet_request_sock *ireq = inet_rsk(req);
  5.  
  6. req->rcv_wnd = 0; /* 本端的初始接收窗口大小 */
  7. req->cookie_ts = 0; /* syncookie: encode tcpopts in timestamp */
  8. tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; /* 客户端的初始序列号 */
  9. req->mss = rx_opt->mss_clamp; /* 客户端通告的MSS */
  10. req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; /* 客户端SYN的时间戳 */
  11. ireq->tstamp_ok = rx_opt->tstamp_ok; /* 连接是否使用TIMESTAMP选项 */
  12. ireq->sack_ok = rx_opt->sack_ok; /* 连接是否使用SACK选项 */
  13. ireq->snd_wscale = rx_opt->snd_wscale; /* 对端的窗口扩大因子 */
  14. ireq->wscale_ok = rx_opt->wscale_ok; /* 连接是否使用Window Scaling选项 */
  15. ireq->acked = 0;
  16. ireq->ecn_ok = 0;
  17. ireq->rmt_port = tcp_hdr(skb)->source; /* 对端的端口 */
  18. ireq->loc_port = tcp_hdr(skb)->dest; /* 本端的端口 */
  19. }

(4)入队列

把连接请求块链入半连接队列中。

  1. void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, unsigned long timeout)
  2. {
  3. struct inet_connection_sock *icsk = inet_csk(sk);
  4. struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; /* 半连接队列 */
  5.  
  6. /* 根据目的IP、目的端口和随机数,计算出该连接请求块的hash值 */
  7. const u32 h = inet_synq_hash(inet_rsk(req)->rmt_addr, inet_rsk(req)->rmt_port, lopt->hash_rnd,
  8. lopt->nr_table_entries);
  9.  
  10. /* 设置连接请求块的超时时间、按照hash值把它链入半连接队列 */
  11. reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);
  12.  
  13. /* 更新半连接队列长度,如果之前为0,则启动定时器 */
  14. inet_csk_reqsk_queue_added(sk, timeout);
  15. }

设置连接请求块的超时时间、按照hash值把它链入半连接队列。

  1. static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, u32 hash,
  2. struct request_sock *req, unsigned long timeout)
  3. {
  4. struct listen_sock *lopt = queue->listen_opt; /* 半连接队列 */
  5. req->expires = jiffies + timeout; /* SYNACK的超时时间 */
  6. req->retrans = 0; /* SYNACK的重传次数 */
  7. req->sk = NULL; /* 连接尚未建立 */
  8. req->dl_next = lopt->syn_table[hash]; /* 指向下一个req */
  9.  
  10. write_lock(&queue->syn_wait_lock);
  11. lopt->syn_table[hash] = req; /* 把请求链入半连接队列 */
  12. write_unlock(&queue->syn_wait_lock);
  13. }
  1. static inline void inet_csk_reqsk_queue_added(struct sock *sk, const unsigned long timeout)
  2. {
  3. /* 更新半连接队列长度,如果之前的长度为0 */
  4. if (reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue) == 0)
  5. inet_csk_reset_keepalive_timer(sk, timeout); /*启动定时器 */
  6. }
  7.  
  8. static inline int reqsk_queue_added(struct request_sock_queue *queue)
  9. {
  10. struct listen_opt *lopt = queue->listen_opt; /* 半连接队列 */
  11.  
  12. const int prev_qlen = lopt->qlen; /* 之前的半连接队列长度 */
  13. lopt->qlen_young++; /* 更新未重传过的请求块数 */
  14. lopt->qlen++; /* 更新半连接队列长度 */
  15. return prev_qlen;
  16. }
  17.  
  18. void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long len)
  19. {
  20. sk_reset_timer(sk, &sk->sk_timer, jiffies + len);
  21. }

根据目的IP、目的端口和随机数,计算出该连接请求块的hash值。

  1. static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd,
  2. const u32 synq_hsize)
  3. {
  4. return jhash_2words((__force u32) raddr, (__force u32) rport, rnd) & (synq_hsize - 1);
  5. }
  6.  
  7. static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
  8. {
  9. return jhash_3words(a, b, 0, initval);
  10. }
  11.  
  12. static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
  13. {
  14. a += JHASH_INITVAL;
  15. b += JHASH_INITVAL;
  16. c += initval;
  17.  
  18. __jhash_final(a, b, c);
  19. return c;
  20. }
  21.  
  22. /* An arbitrary initial parameter */
  23. #define JHASH_INITVAL 0xdeadbeef
  24.  
  25. /* __jhash_final - final mixing of 3 32-bit values (a, b, c) into c */
  26. #define __jhash_final(a, b, c) \
  27. {
  28. c ^= b; c -= rol32(b, 14); \
  29. a ^= c; a -= rol32(c, 11); \
  30. b ^= a; b -= rol32(a, 25); \
  31. c ^= b; c -= rol32(b, 16); \
  32. a ^= c; a -= rol32(c, 4); \
  33. b ^= a; b -= rol32(a, 14); \
  34. c ^= b; c -= rol32(b, 24); \
  35. }
  36.  
  37. /**
  38. * rol32 - rotate a 32-bit value left
  39. * @word: value to rotate
  40. * @shift: bits to roll
  41. */
  42. static inline __u32 rol32(__u32 word, unsigned int shift)
  43. {
  44. return (word << shift) | (word >> (32 - shift));
  45. }

(5) 出队列

把连接请求块从半连接队列中删除。

  1. static inline void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req,
  2. struct request_sock **prev)
  3. {
  4. inet_csk_reqsk_queue_unlink(sk, req, prev); /* 把连接请求块从半连接队列中删除 */
  5. inet_csk_reqsk_queue_removed(sk, req); /* 更新半连接队列长度,如果为0,则删除定时器 */
  6. reqsk_free(req); /* 释放连接请求块 */
  7. }
  8.  
  9. static inline void inet_csk_reqsk_queue_unlink(struct sock *sk, struct request_sock *req,
  10. struct request_sock **prev)
  11. {
  12. reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req, prev);
  13. }

把连接请求块从半连接队列中删除。

  1. static inline void reqsk_queue_unlink(struct request_sock_queue *queue, struct request_sock *req,
  2. struct request_sock **prev_req)
  3. {
  4. write_lock(&queue->syn_wait_lock);
  5. *prev_req = req->dl_next; /* 改变了指针的值,相当于删除了req指向的实例 */
  6. write_unlock(&queue->syn_wait_lock);
  7. }
  8.  
  9. static inline void inet_csk_reqsk_queue_removed(struct sock *sk, struct request_sock *req)
  10. {
  11. /* 如果半连接队列长度为0,则删除定时器 */
  12. if (reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req) == 0)
  13. inet_csk_delete_keepalive_timer(sk);
  14. }

更新未重传过的连接请求块数、更新半连接队列长度。

  1. static inline int reqsk_queue_removed(struct request_sock_queue *queue, struct request_sock *req)
  2. {
  3. struct listen_sock *lopt = queue->listen_opt;
  4. if (req->retrans == 0)
  5. --lopt->qlen_yong;
  6. return --lopt->qlen;
  7. }

TCP连接建立系列 — 连接请求块的更多相关文章

  1. TCP连接建立系列 — TCP选项解析

    本文主要分析:在收到客户端的SYN包时,服务器端是如何解析它所携带的TCP选项,并结合本端情况决定是否予以支持. 内核版本:3.6 Author:zhangskd @ csdn blog 概述 收到客 ...

  2. TCP连接建立系列 — 服务端接收ACK段(一)

      http://blog.csdn.net/zhangskd/article/details/17923917 分类: Linux TCP/IP Linux Kernel 2014-01-07 09 ...

  3. TCP连接建立系列 — 服务端接收ACK段(二)

    本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 创建新sock 协议族相关的操作函数,我们要看的是TCP ...

  4. TCP连接建立系列 — 服务端接收SYN段

    本文主要分析:服务器端接收到SYN包时的处理路径. 内核版本:3.6 Author:zhangskd @ csdn blog 接收入口 1. 状态为ESTABLISHED时,用tcp_rcv_esta ...

  5. TCP连接建立系列 — 客户端发送SYN段

    主要内容:客户端调用connect()时的TCP层实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd connect的TCP层实现 SOCK_STRE ...

  6. TCP连接建立系列 — 客户端接收SYNACK和发送ACK

    主要内容:客户端接收SYNACK.发送ACK,完成连接的建立. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 接收入口 tcp_v4_rcv |--&g ...

  7. TCP连接建立系列 — 客户端的端口选取和重用

    主要内容:connect()时的端口选取和端口重用. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 端口选取 connect()时本地端口是如何选取的呢 ...

  8. TCP连接建立系列 — 服务端发送SYNACK段

    本文主要分析:服务器端如何构造和发送SYNACK段. 内核版本:3.6 Author:zhangskd @ csdn blog 发送入口 tcp_v4_send_synack()用于发送SYNACK段 ...

  9. 动手学习TCP:TCP连接建立与终止

    TCP是一个面向连接的协议,任何一方在发送数据之前,都必须先在双方之间建立一条连接.所以,本文就主要看看TCP连接的建立和终止. 在开始介绍TCP连接之前,先来看看TCP数据包的首部,首部里面有很多重 ...

随机推荐

  1. 2.Cocos2dx 3.2中的重力系统Box2D

     1 添加Box2D相关的库 步骤1:右击项目所在的解决方案à添加->现有项目àE:\Installed\cocos2d-x-3.2\cocos2d-x-3.2\external\Box2D ...

  2. Android控制软键盘的弹出和隐藏

    弹出软键盘 前提:必须要有一个可以编辑的控件(EditText),并且当前已经获取焦点 /** * 弹出软键盘 */ public void openKeyboard(View view) { // ...

  3. 5.1、Android Studio用Logcat编写和查看日志

    Android Studio在Android Monitor中包含了一个logcat的tab,可以打印系统事件,比如垃圾回收发生时,实时打印应用消息. 为了显示需要的信息,你可以创建过滤器,更改需要显 ...

  4. [ExtJS5学习笔记]第六节 Extjs的类系统Class System命名规则及定义和调试

    本文地址: http://blog.csdn.net/sushengmiyan/article/details/38479079 本文作者:sushengmiyan ----------------- ...

  5. linux2.6内核链表

    一.        链表数据结构简介      链表是一种常用的组织有序数据的数据结构,它通过指针将一系列数据节点连接成一条数据链,是线性表的一种重要实现方式.相对于数组,链表具有更好的动态性,建立链 ...

  6. 一个ExtJS实例

    聊聊ExtJS 这几天接触了一个项目 前台用的是extjs 发现这个东西还是有点意思的  就把前台的部分 剥离了下来 有兴趣的朋友可以当做模板学习 不多说先上效果图 这篇文章 可以看作是ext知识的一 ...

  7. Java基础---Java---IO流-----对象的序列化、管道流、RandomAccessFile、数据类型的流对象DataStream、ByteArrayStream

    ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化. ObjectOutputStream 和 ObjectInputStream ...

  8. UNIX网络编程——TCP—经受时延与nagle算法、滑动窗口、拥塞窗口

    1.经受时延: TCP在接收到数据时并不立即发送ACK,相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送,时延为200ms,超过时延范围,发送确认. 2.nagle算法: 一个TCP连接 ...

  9. debian 安装jdk

    JDK下载http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6- ...

  10. Android 优质精准的用户行为统计和日志打捞方案

    Android 自定义优质精准的用户行为和日志打捞方案 Tamic csdn博客 :http://blog.csdn.net/sk719887916/article/details/51398416 ...