Linux内核4.4版本带来的网络新特性

简介:

本文题目有点大,但其实我只想描述一些我个人一直比较关注的特性,并且不会太详细,跟往常一样,主要是帮忙理清思路的,不会分析源码。这主要是为了哪一天突然忘了的时候,一目十行扫一眼就能记忆当时的理解,不然写的太细节了,自己都看不懂了。

Lockless TCP listener

先 从TCP的syncookie说起,如果都能使用syncookie机制该有多好,但是不能,因为它会丢失很多选项协商信息,这些信息对TCP的性能至关 重要。TCP的syncookie主要是为了防止半连接的syn flood攻击,超级多的节点发送大量的syn包,然后就不管了,而被攻击的协议栈收到一个syn就会建立一个request,绑定在syn针对的 Listener的request队列上。这会消耗很大的内存。
       但是仔细想想,抛开选项协商不说,仅仅针对TCP的syn,synack而言,事实上TCP在3次握手过程,只需要查找一下Listener即可,只要它 存在,就可以直接根据syn包构造synack包了,根本就不用Listener了,要记住2次握手包的信息,有两个办法,第一个办法就是 syncookie机制给encode并echo回去,等第3次握手ack来了之后,TCP会decode这个ack的序列号信息,构造子socket, 插入Listener的accept队列,还有一种办法就是在本地分配内存,记录这个连接客户端的信息,等第3次握手包ack到来之后,找到这个 request,构造子socket,插入Listener的accept队列。
       在4.4之前,一个request是属于一个Listener的,也就是说一个Listener有一个request队列,每构造一个request,都 要操作这个Listner本身,但是4.4内核给出了突破性的方法,就是基于这个request构造一个新的socket!插入到全局的socket哈希 表中,这个socket仅仅记录一个它的Listener的轻引用即可。等到第3个握手包ack到来后,查询socket哈希表,找到的将不再是 Listnener本身,而是syn包到来时构造的那个新socket了,这样传统的下面的逻辑就可以将Listener解放出了:
传统的TCP协议栈接收

sk = lookup(skb);
lock_sk(sk);
if (sk is Listener); then
    process_handshake(sk, skb);
else
    process_data(skb);
endif
unlock_sk(sk);

可以看出,sk的lock期间,将是一个瓶颈,所有的握手逻辑将全部在lock期间处理。4.4内核改变了这一切,下面是新的逻辑:

sk = lookup_form_global(skb);
if (sk is Listener); then
    rv = process_syn(skb);
    new_sk = build_synack_sk(skb, rv);
    new_sk.listener = sk;
    new_sk.state = SYNRECV;
    insert_sk_into_global(sk);
    send_synack(skb);
    goto done;
else if (sk.state == SYNRECV); then
    listener = sk.lister;
    child_sk = build_child_sk(skb, sk);
    remove_sk_from_global(sk);
    add_sk_into_acceptq(listener, child_sk);
fi
lock_sk(sk);
process_data(skb);
unlock_sk(sk);
done:


这个逻辑中,只需要细粒度lock具体的队列就可以了,不需要lock整个socket了。对于syncookie逻辑更简单,根本连SYNRECV socket都不用构造,只要保证有Listener即可!
       这是周四早上蹲厕所的时候猛然看到的4.4新特性,当时就震惊了,这正是我在2014年偶然想到的,但是后来由于没有环境就没有跟进,如今已经并在 mainline了,不得不说这是一件好事。当时我的想法是依照一个syn包完全可以无视Listner而构造synack,需要协商的信息可以保存在别 的地方而不必非要和Listner绑定,这样可以解放Listener的职责。但是我没有想到再构造一个socket,与所有socket平行插入到同一 个socket哈希表中。
       我觉得,4.4之前的逻辑是简单明了的,不管是握手包和数据包,处理逻辑完全一致,但是4.4将代码复杂化了,分离了那么多的if-else...但是这 是不可避免的。事实上,syn构造的request本身就应该与Listener进行绑定,只是如果想到优化,代码会变得复杂,但是如果在代码本身下一番 功夫,代码也会很好看,只是,我没有那个能力,我代码写的不好。
       这个Lockless的思想跟nf_conntrack的思想类似,但是我觉得conntrack对于related conn逻辑也可以这么玩。

TCP listener的CPU亲和力与REUSEPORT

紧随着Lockless TCP Listener而来的accept队列的优化!众所周知,一个Listener只有一个accept队列,在多核环境下这个单一的队列绝对是个瓶颈,一个高性能服务器怎么可以忍受这样!
       其实这个问题早就被REUSEPORT解决了。REUSEPORT允许多个独立的socket同时侦听同一个IP/Port对,这对于当今的多队列网卡, 多CPU环境绝对是个福音。但是,虽然路宽了,车道多了,没有规则的话,性能反而下降,拥挤程度反而降级!
       4.4内核为socket引入了一个SO_INCOMING_CPU选项,如果一个socket的该选项设置为n,意味着只有在n号cpu上处理协议栈逻 辑的执行流才可以将数据包插入这个socket。体现在代码上,就是在compute_score上给与加分,也就是说,除了目标IP,目标端口,源 IP,源端口之外,cpu也成了一个匹配项目。
       正如patch说明说的,此特性与REUSEPORT,多队列网卡相结合,一定是一道美味佳肴!

新的基于流的多路径路由选路

以 前的时候,有路由cache,一个路由cache项就是一个带有源信息的n元组信息,每一个数据包在匹配到FIB条目后都会建立一条cache项,后续的 查找首先去查找cache,因此都是基于流的。然而在路由cache下课后,多路径选路变成了基于包的,这对于TCP这种协议而言肯定会造成乱序问题。为 此4.4内核在多路径选路的时候,hash计算中引入了源信息,避免了这个问题。只要计算方法不变,永远一个流的数据hash到一个dst。

携带version number的socket路由缓存

这 个不是4.4内核携带的特性,是我自己的一些想法。early_demux已经被引入了内核,旨在消除本机入流量的路由查找,毕竟路由查找后还要再 socket查找,为何不直接socket查找呢?查找到的结果缓存路由信息。对于本机提供服务的设备而言,开启这个选项吧。
       但是对于出流量,还是会有很大的开销浪费在路由查找上。虽然IP是无连接的,但是TCP socket或者一个connected UDP socket却是可以明确标示一个5元组的,如果把路由信息存储在socket中,是不是更好的。好吧!很多人会问,怎么解决同步问题,路由表改了怎么 办,要notify socket吗?如果你被此引导而去设计一个“高效的同步协议”,你就输了!办法很简单,就是引入两个计数器-缓存计数器和全局计数器,socket的路 由缓存如下:

sk_rt_cache {
    atomic_t version;
    dst_entry *dst;
};

全局计数器如下:

atomic_t gversion;

每当socket设置路由缓存的时候,读取全局gversion的值,设置进缓存version,每当路由发生任何改变的时候,全局gversion计数器递增。如果cache计数器的值与全局计数器值一致,就可用,否则不可用,当然,dst本身也要由引用计数保护。



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1735581

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
8天前
|
安全 Linux 虚拟化
网络名称空间在Linux虚拟化技术中的位置
网络名称空间(Network Namespaces)是Linux内核特性之一,提供了隔离网络环境的能力,使得每个网络名称空间都拥有独立的网络设备、IP地址、路由表、端口号范围以及iptables规则等。这一特性在Linux虚拟化技术中占据了核心位置🌟,它不仅为构建轻量级虚拟化解决方案(如容器📦)提供了基础支持,也在传统的虚拟机技术中发挥作用,实现资源隔离和网络虚拟化。
网络名称空间在Linux虚拟化技术中的位置
|
9天前
|
网络协议 安全 Linux
Linux网络名称空间之独立网络资源管理
Linux网络名称空间是一种强大的虚拟化技术🛠️,它允许用户创建隔离的网络环境🌐,每个环境拥有独立的网络资源和配置。这项技术对于云计算☁️、容器化应用📦和网络安全🔒等领域至关重要。本文将详细介绍在Linux网络名称空间中可以拥有的独立网络资源,并指出应用开发人员在使用时应注意的重点。
|
9天前
|
安全 网络协议 Linux
Linux网络名称空间概述
Linux网络名称空间是操作系统级别的一种虚拟化技术🔄,它允许创建隔离的网络环境🌐,使得每个环境拥有自己独立的网络资源,如IP地址📍、路由表🗺️、防火墙规则🔥等。这种技术是Linux内核功能的一部分,为不同的用户空间进程提供了一种创建和使用独立网络协议栈的方式。本文旨在全方面、多维度解释Linux网络名称空间的概念、必要性和作用。
Linux网络名称空间概述
|
16天前
|
Linux
Linux中centos桌面消失网络图标
Linux中centos桌面消失网络图标
13 0
|
27天前
|
运维 网络协议 安全
【Shell 命令集合 网络通讯 】Linux 网络抓包工具 tcpdump命令 使用指南
【Shell 命令集合 网络通讯 】Linux 网络抓包工具 tcpdump命令 使用指南
44 0
|
27天前
|
监控 Linux Shell
【Shell 命令集合 网络通讯 】Linux 配置和管理网络流量的形状 shapecfg命令 使用指南
【Shell 命令集合 网络通讯 】Linux 配置和管理网络流量的形状 shapecfg命令 使用指南
36 0
|
27天前
|
网络协议 Shell Linux
【Shell 命令集合 网络通讯 】Linux 设置和配置PPP pppsetup命令 使用教程
【Shell 命令集合 网络通讯 】Linux 设置和配置PPP pppsetup命令 使用教程
34 0
|
27天前
|
Shell Linux C语言
【Shell 命令集合 网络通讯 】Linux 查看系统中的UUCP日志文件 uulog命令 使用指南
【Shell 命令集合 网络通讯 】Linux 查看系统中的UUCP日志文件 uulog命令 使用指南
29 0
|
7天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
20 6
|
27天前
|
监控 Shell Linux
【Shell 命令集合 网络通讯 】Linux 共享文件和打印机 Samba 命令 使用指南
【Shell 命令集合 网络通讯 】Linux 共享文件和打印机 Samba 命令 使用指南
39 0