linux tcp repair及tcp热迁移
概念
比如docker等容器在不同的机器之间无缝迁移(可能由于调度,维护,交割等原因),是常见的需求场景
但是又希望不能中断服务,因此各种虚拟机和容器的热迁移就得到很多关注。
linux也在3.5版本中引入TCP_REPAIR socket选项来支持热迁移
获取状态及还原
当需要迁移的时候,为迁移的socket进入repair模式
setsockopt设置TCP_PREPAIR选项
进入repair模式的要求:
- 需要CAP_NET_ADMIN, 用户命名空间需要有网络管理能力
- socket处于CLOSE状态或ESTABLISHED状态
从内核读取缓存数据
内核缓存区中未发送或未被确认的数据,或者未被应用程序读取的数据
setsockopt设置TCP_REPAIR_QUEUE选项的值分别为TCP_SEND_QUEUE和TCP_RECV_QUEUE, 从两个缓存中读取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
int flags, int *addr_len)
{
...
if (unlikely(tp->repair)) {
err = -EPERM;
if (!(flags & MSG_PEEK)) //只支持peek方式
goto out;
if (tp->repair_queue == TCP_SEND_QUEUE) //从发送队列中读取
err = tcp_peek_sndq(sk, msg, len);
goto out;
err = -EINVAL;
if (tp->repair_queue == TCP_NO_QUEUE)
goto out;
//正常的peek流程,peek接收队列数据
/* 'common' recv queue MSG_PEEK-ing */
}
...
}
|
迁移完,把这些数据还原到对应的socket缓存中,通过send()接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
{
...
if (unlikely(tp->repair)) {
if (tp->repair_queue == TCP_RECV_QUEUE) {
copied = tcp_send_rcvq(sk, msg, size); //放到接收队列
goto out_nopush;
}
err = -EINVAL;
if (tp->repair_queue == TCP_NO_QUEUE)
goto out_err;
//TCP_SEND_QUEUE, 正常模式处理
/* 'common' sending to sendq */
}
...
//正常的copy到内核发送缓存,准备发送的处理过程
...
}
|
读取和还原tcp协商信息
当握手的时候,会用tcp扩展选项来协商支持的情况,比如sack,timestamp,wscale等
getsockopt TCP_REPAIR_OPTIONS选项来获取这些值, 迁移之后setsockopt还原这些值
读取和还原序号
1
2
3
4
5
6
7
8
|
case TCP_QUEUE_SEQ:
if (tp->repair_queue == TCP_SEND_QUEUE)
val = tp->write_seq;
else if (tp->repair_queue == TCP_RECV_QUEUE)
val = tp->rcv_nxt;
else
return -EINVAL;
break;
|
读取和还原MSS
1
2
3
4
5
6
7
|
case TCP_MAXSEG:
val = tp->mss_cache;
if (!val && ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)))
val = tp->rx_opt.user_mss;
if (tp->repair)
val = tp->rx_opt.mss_clamp;
break;
|
读取和还原滑动窗口信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
case TCP_REPAIR_WINDOW: {
struct tcp_repair_window opt;
if (get_user(len, optlen))
return -EFAULT;
if (len != sizeof(opt))
return -EINVAL;
if (!tp->repair)
return -EPERM;
opt.snd_wl1 = tp->snd_wl1;
opt.snd_wnd = tp->snd_wnd;
opt.max_window = tp->max_window;
opt.rcv_wnd = tp->rcv_wnd;
opt.rcv_wup = tp->rcv_wup;
if (copy_to_user(optval, &opt, len))
return -EFAULT;
return 0;
}
|
静默关闭连接
close()将静默关闭,不会发送FIN
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
|
void tcp_close(struct sock *sk, long timeout)
{
if (unlikely(tcp_sk(sk)->repair)) {
sk->sk_prot->disconnect(sk, 0);
} else if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
sk->sk_prot->disconnect(sk, 0);
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
} else if (tcp_close_state(sk)) {
tcp_send_fin(sk);
}
...
}
int tcp_disconnect(struct sock *sk, int flags)
{
struct inet_sock *inet = inet_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
int err = 0;
int old_state = sk->sk_state;
if (old_state != TCP_CLOSE)
tcp_set_state(sk, TCP_CLOSE);
/* ABORT function of RFC793 */
if (old_state == TCP_LISTEN) {
inet_csk_listen_stop(sk);
} else if (unlikely(tp->repair)) {
sk->sk_err = ECONNABORTED;
}
...
}
|
还原连接
connect()直接进入ESTABLISH状态, 然后setsockopt各个状态选项,再用send()两个缓存到对应内核队列中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
int tcp_connect(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *buff;
int err;
tcp_connect_init(sk);//初始化tcp配置,选项,滑动窗口等
if (unlikely(tp->repair)) {
tcp_finish_connect(sk, NULL); //repair模式直接进入TCP_ESTABLISHED状态
return 0;
}
...
//发送握手包
}
|
原文来自:http://www.cnhalo.net/2016/09/13/linux-tcp-repair/
资料
TCP connection repair
CRIU
linux tcp repair及tcp热迁移相关推荐
- TCP套接口热迁移REPAIR模式
要实现TCP套接口的热迁移,必须能够实现在迁移之前保存套接口的当前状态,迁移之后还原套接口的状态.Linux内核中为支持TCP套接口热迁移实现了REPAIR模式以及相关的操作.迁移流程如下,首先启用R ...
- 数据连接linux网络编程之TCP/IP基础(四):TCP连接的建立和断开、滑动窗口
在写这篇文章之前,xxx已经写过了几篇关于改数据连接主题的文章,想要了解的朋友可以去翻一下之前的文章 一.TCP段格式: TCP的段格式如下图所示 源端口号与目标端口号 源端口号和目标端口号,加上IP ...
- Linux内核中影响tcp三次握手的一些协议配置
在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ...
- 【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系
00. 目录 文章目录 00. 目录 01. TCP服务端和客户端流程 02. connect函数 03. listen函数 04. 三次握手 05. accept函数 06. 附录 01. TCP服 ...
- 【Linux网络编程】TCP编程
00. 目录 文章目录 00. 目录 01. TCP概述 02. TCP特点 03. TCP中CS架构 04. TCP相关函数 05. TCP服务端示例 06. TCP客户端示例 07. 附录 01. ...
- 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...
- 优化Linux下的内核TCP参数来提高服务器负载能力
提高服务器的负载能力,是一个永恒的话题.在一台服务器CPU和内存资源额定有限的情况下,最大的压榨服务器的性能,是最终的目的.要提高Linux系统下的负载能力,可以先启用Apache的Worker模式, ...
- Linux集群和自动化维1.4.2 优化Linux下的内核TCP参数以提高系统性能
1.4.2 优化Linux下的内核TCP参数以提高系统性能 内核的优化跟服务器的优化一样,应本着稳定安全的原则.下面以Squid服务器为例来说明,待客户端与服务器端建立TCP/IP连接后就会关闭Soc ...
- android linux网络连接,Android和Linux服务器之间的TCP连接
我正在编写一个代码,需要每秒从Android移动设备向台式计算机(linux服务器)发送数据.由于数据经常发送,通过Http命中无法实现(因为会消耗时间),所以Tcp通信似乎是更好的选择,因为andr ...
最新文章
- 你的组织为自动化测试做好准备了吗?
- 使用QT制作桌面小工具(一)
- [云炬ThinkPython阅读笔记]2.1 赋值语句
- Python的matplotlib(2)
- Effective Java之抛出与抽象相应的异常(六十一)
- UE4学习-创建基于C++的场景
- linux命令 dstat,关于linux:每天学一个-Linux-命令103dstat
- 把编译时间加入到目标文件
- [转]iis7.5+win2008 出现 HTTP Error 503. The service is unavailable.
- 小白学深度之LSTM长短期记忆神经网络——深度AI科普团队
- c语言文本格式自动对齐,c语言文件读取原始数据是1、2列是按相同的一起排列命名为Yi- 爱问知识人...
- 谷歌地图 街景 api_Google使街景在地图中更加突出
- 超50万人推荐的神奇兼实用App,个个精品,打死也不能错过
- 《云计算架构技术与实践》连载(2):1.2 云计算的发展趋势
- 产品经理经常面临的系统须知大拷问
- 计算机音乐说散就散,说散就散(精彩音乐汇)
- 区块链源代码分析(1)
- 根据QQ号获取昵称和头像
- 正交单位矩阵 matlab,05-06线性代数试卷及答案
- 2022年汽车零部件行业前景