可编程网络DataPath

大部分网络数据的最终生产者和消费者都是应用程序,在一个计算机中,网络数据包需要经过网卡 <=> 系统内核 <=> 应用程序,才能完成传输。

Linux 有严格的内核和用户空间隔离,网络数据在内核和应用程序之间的传输需要频繁的进行上下文切换,随之带来额外的CPU cycle 开销。所以为了提升网络性能,在越来越多的SDN场景都采用了kernel-bypass的技术。其中具有代表性就是DPDK,不过DPDK 在带来性能提升的同时,也有一些问题:

  • 首先,因为改变了现有操作系统的工作方式,很难与现有操作系统集成
  • 因为网络路径中没有了操作系统,相关的网络应用程序需要重新实现之前由操作系统提供的一些功能,例如路由表,4-7层网络协议
  • 一些由操作系统提供的熟悉的管理部署工具将不再可用,因为操作系统现在没有相关网络硬件的控制权
  • 因为上面的原因带来的复杂性
  • 破坏了原有操作系统内核提供了的安全性,这一点在容器场景尤其重要,因为在容器场景中,资源的抽象和隔离主要是由操作系统内核提供的
  • 需要消耗1个或者多个CPU核来专门处理网络包

相对于DPDK,XDP具有以下优点

  • 无需第三方代码库和许可
  • 同时支持轮询式和中断式网络
  • 无需分配大页
  • 无需专用的CPU
  • 无需定义新的安全网络模型

XDP(eXpress Data Path)是近年兴起的网络数据面技术,为Linux内核提供了高性能、可编程的网络数据通路。

不同于kernel-bypass技术,XDP 是在网络包在还未进入网络协议栈之前就处理,所以既没有内核-用户空间的切换开销,又没有保留了操作系统控制网络硬件的能力。

XDP 的基本架构

XDP(eXpress Data Path)提供了一个内核态、高性能、可编程 BPF 包处理框架。

XDP 的处理方式在内核的RX 路径上添加一个早期hook,让用户可以使用eBPF程序控制数据包。该hook 在中断处理之后放置在NIC驱动程序中,并且在网络堆栈本身的所有内存分配之前,因为内存分配可能是一项高成本的操作。由于这种设计,XDP 可以使用商用硬件每秒每核丢弃 2600 万个数据包。

XDP 数据包进程(Packet Processor)包含一个内核组件,该组件通过功能接口直接从驱动程序处理 RX 数据包页(packet-pages),而无需提前分配 skbuff 或软件队列(software queues)。通常,每个 RX 队列分配一个 CPU,但在此模型中,没有加锁 RX 队列,CPU 可以专用于忙轮询或中断模型。BPF 程序执行诸如数据包解析、表查找、创建/管理有状态过滤器、封装/去封装数据包等处理。

XDP 系统由4个主要部分组成:

  • XDP driver hook:这是XDP程序的接入点,当网络数据包从硬件中收到时会被执行。
  • eBPF virtual machine:执行XDP 程序的字节码,并且JIT 编译到机器码
  • BPF maps:key/value store,用来在整个XDP 系统中做数据的交互
  • eBPF verifier:在程序加载到内核之前静态的分析、检查代码,以确保代码会Crash 或者损坏运行的内核。

GRO(Generic receive offload):通用receive offload,offload 详见XDP 硬件要求小节。

RPS/RFS(Receive Package Steering / Receive Flow Steering):用以在软件层面实现报文在多个cpu之间的负载均衡以及提高报文处理的缓存命中率。

XDP 的软件要求

Linux kernel 4.8 开始支持XDP,XDP 依赖于eBPF ,所以需求较新的内核支持eBPF,可以参考eBPF 基础架构及使用。

大部分支持 XDP 的驱动都支持在不会引起流量中断(traffic interrupt)的前提下原子地替换运行中的程序。出于性能考虑,支持 XDP 的驱动只允许 attach 一个程序 ,不支持程序链(a chain of programs)。如果有必要的话,可以通过尾调用来对程序进行拆分,以达到与程序链类似的效果。

XDP 的硬件要求

使用XDP 对网卡有一些要求

  • 支持多队列的网卡
  • 一般的协议通用offload
  • TX/RX checksum offload,即校验offload,利用网卡计算校验和,而不是。
  • Receive Side Scaling,RSS 即接收端伸缩,是一种网络驱动程序技术,可在多处理器或多处理器核心之间有效分配接收到的网络数据包并处理。
  • Transport Segmentation Offload,TSO,即,是一种利用网卡替代CPU对大数据包进行分片,降低CPU负载的技术。
  • 最好支持LRO,aRFS

目前越来越多的网卡设备开始支持offload特性,以便提升网络收发和处理的性能。本文所描述的offload特性,主要是指将原本在协议栈中进行的IP分片、TCP分段、重组、checksum校验等操作,转移到网卡硬件中进行,降低系统CPU的消耗,提高处理性能。

XDP 的工作流程及使用

XDP 的工作模式

XDP 总共支持三种工作模式(operation mode):

  • xdpdrv

xdpdrv 表示 native XDP(原生 XDP), 意味着 BPF 程序直接在驱动的接收路 径上运行,理论上这是软件层最早可以处理包的位置(the earliest possible point)。这是常规/传统的 XDP 模式,需要驱动实现对 XDP 的支持,目前 Linux 内核中主流的 10G/40G 网卡都已经支持。

  • xdpgeneric

xdpgeneric 表示 generic XDP(通用 XDP),用于给那些还没有原生支持 XDP 的驱动进行试验性测试。generic XDP hook 位于内核协议栈的主接收路径(main receive path)上,接受的是 skb 格式的包,但由于 这些 hook 位于 ingress 路 径的很后面(a much later point),因此与 native XDP 相比性能有明显下降。因 此,xdpgeneric 大部分情况下只能用于试验目的,很少用于生产环境。

  • xdpoffload

最后,一些智能网卡(例如支持 Netronome’s nfp 驱动的网卡)实现了 xdpoffload 模式 ,允许将整个 BPF/XDP 程序 offload 到硬件,因此程序在网卡收到包时就直接在网卡进行处理。这提供了比native XDP 更高的性能,虽然在这种模式中某些 BPF map 类型和BPF 辅助函数是不能用的。BPF 校验器检测到这种情况时会直接报错,告诉用户哪些东西是不支持的。除了这些不支持的 BPF 特性之外,其他方面与 native XDP 都是一样的。

引入XDP 前的DataPath:

引入XDP 之后的DataPath:包含native 模式、generic 模式和offload 模式

这三种模式 iproute2 都实现了,执行 ip link set dev em1 xdp obj [...] 命令时,内核会先尝试以 native XDP 模 式加载程序,如果驱动不支持再自动回退到 generic XDP 模式。如果显式指定了 xdpdrv 而不是 xdp,那驱动不支持 native XDP 时加载就会直接失败,而不再尝试 generic XDP 模式。

XDP 的工作流程

包含XDP 的Linux 网络栈:

简单的说,XDP 就是在网卡驱动中的hook点,在该hook点处打入eBPF 程序,利用eBPF 的事件驱动来完成网络数据包处理:

  1. 高级语言程序设计,例如C 来完成。
  2. 编译成eBPF 字节码,llvm等工具已经支持了将C 语言编译成eBPF 字节码。
  3. 在加载到内核之前,会交给eBPF verifier 静态的分析代码的安全性。
  4. 加载到内核。
  5. 在收到网络包时,通过JIT(Just In Time)编译器翻译成机器指令并执行。

在XDP 程序的结束,需要对packet做出一个结论。结论有4+1 种可能:

  • XDP_DROP:直接丢包
  • XDP_ABORTED:也是丢包,不过会触发一个eBPF 程序错误,可以通过调试工具查看
  • XDP_TX:将处理后的packet 发回给相同的网卡
  • XDP_PASS:将处理后的packet 传递给内核协议栈
  • XDP_REDIRECT 稍微复杂点,它会需要一个额外的参数来表明Redirect 的目的地,这个额外的参数是在XDP 程序返回之前通过一个helper 函数设置。这种方式使得Redirect 可以非常方便的扩展,增加新的Redirect目的地只需要再增加一个参数值即可。目前Redirect的目的地包含了以下几种可能:
    • 将处理后的packet转发给一个不同的网卡,包括了转发给连接虚拟机或者容器的虚拟网卡
    • 将处理后的packet转发给一个不同的CPU做进一步处理
    • 将处理后的packet转发给一个特定的用户空间socket(AF_XDP),这种方式使得XDP也可以直接bypass网络协议栈,甚至进一步结合zero-copy技术降低包处理的overhead

Hello World

利用eBPF ,嵌入eBPF 网络数据包处理程序至XDP hook 点。

所以重点是如何编写处理网络数据包的程序。

// ping_drop.c

#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ip.h> #define SEC(NAME) __attribute__((section(NAME), used)) SEC("prog") //prog 作为一个hook ,介于自定义处理程序和XDP之间 int ping_drop(struct xdp_md *ctx)
{
void *data = (void*)(long)ctx->data; //报文数据开始处
void *end = (void*)(long)ctx->data_end; //报文数据结束点 struct ethhdr *eh; //以太头
eh = data;
if (data > end) //这个检测有点多余,一个合格驱动会保证
return XDP_PASS; //data一定是小于end的
if ((void*)(eh+1) > end) //这个检测非常重要,否则在下面读取 eh->h_proto
return XDP_PASS; //的时候,无法通过bpf verifier的验证,程序就无法加载 if (eh->h_proto != __constant_htons(ETH_P_IP)) //不是IP报文,放过
return XDP_PASS; struct iphdr *iph;
iph = (void*)(eh + 1); if ((void*)(iph+1) > end) //这里的检测也非常重要,原因同上
return XDP_PASS; if (iph->protocol == IPPROTO_ICMP) //判断如果是ping报文,丢弃
return XDP_DROP; //返回 XDP_DROP,会导致无法ping通主机,其他如ssh等不受影响 return XDP_PASS;
} char __license[] SEC("license") = "GPL";

XDP hook 点在网络驱动中,基于eBP 的事件驱动机制,当XDP 收到网络数据包时,我们的处理程序就会被执行。

传入eBPF 处理程序的ctx 其实就是XDP 元数据,没有sk_buff结构,只有一个 struct xdp_md 指针

/* user accessible metadata for XDP packet hook
* new fields must be added to the end of this structure
*/
struct xdp_md {
__u32 data; //数据包开始指针
__u32 data_end; //数据包结束指针
__u32 data_meta; //初始阶段它是一个空闲的内存地址,供XDP程序与其他层交换数据包元数据时使用。
/*
分别是接收数据包接口的索引和对应的RX 队列的索引。当访问这两个值时,BPF 代码会在内核内部重写,
以访问实际持有这些值的内核结构struct xdp_rxq_info。
*/
/* Below access go through struct xdp_rxq_info */
__u32 ingress_ifindex; /* rxq->dev->ifindex */
__u32 rx_queue_index; /* rxq->queue_index */
};

Clang 编译生成对象文件,并加载

[root@dev ~]# clang -Wall -target bpf -c ping_drop.c -o ping_drop.o

然后用iproute2 里面的 ip link 命令加载到某个NIC 上,如ens192

[root@dev ~]# ip link set dev ens192 xdp object ping_drop.o

iproute2 需要打开 HAVE_ELF 这个宏,默认CentOS 7 并没有,需要编译iproute2 并打开。

git clone git://git.kernel.org/pub/pub/scm/network/iproute2/iproute2.git

cd iproute2/
./configure --prefix=/usr
make -j8 && make install

BPF map 和程序作为内核资源只能通过文件描述符访问,其背后是内核中的匿名 inode。如 iproute2,其中的 tc 或 XDP 在准备 环境、加载程序到内核之后最终会退出。在这种情况下,从用户空间也无法访问这些 map 了,而本来这些 map 其实是很有用的,所以内核实现了一个最小内核空间 BPF 文件系统,BPF map 和 BPF 程序 都可以钉到(pin)这个文件系统内,这个过程称为 object pinning(钉住对象)。

挂载BPF FS,允许BPF 程序从虚拟文件系统固定和获取map

mount -t bpf /sys/fs/bpf /sys/fs/bpf
ip link set dev ens192 xdp object ping_drop.o # 之后,在其他节点ping 当前节点就会显示无法ping 通。
ip link set dev ens192 xdp off 之后,ping # 之后,恢复正常。



XDP 的应用

借助XDP/BPF,可以快速、高效、超低副作用的处理网络数据包,可以在以下几个方面应用:

  • 防御DDoS 攻击
  • CDN 服务调优:CDN 服务突刺分析及处理
  • QoS:控制网络传输速率,如 tc

参考

进阶参考

可编程网络DataPath 及XDP的更多相关文章

  1. [转] - Linux网络编程 -- 网络知识介绍

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  2. JAVA基础知识之网络编程——-网络基础(Java的http get和post请求,多线程下载)

    本文主要介绍java.net下为网络编程提供的一些基础包,InetAddress代表一个IP协议对象,可以用来获取IP地址,Host name之类的信息.URL和URLConnect可以用来访问web ...

  3. UNIX网络编程——网络IPC:套接字

    UNIX网络编程——网络IPC:套接字 Contents 套接字接口 套接字描述符 寻址 字节序 地址格式 地址查询 绑定地址 建立连接 数据传输 套接字选项 带外数据 UNIX域套接字 使用套接字的 ...

  4. 系统编程-网络-tcp客户端服务器编程模型(续)、连接断开、获取连接状态场景

    相关博文: 系统编程-网络-tcp客户端服务器编程模型.socket.htons.inet_ntop等各API详解.使用telnet测试基本服务器功能 接着该上篇博文,咱们继续,首先,为了内容的完整性 ...

  5. java socket编程(网络编程)

    一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...

  6. JAVA基础知识之网络编程——-网络通信模型(IO模型)

    <Unix网络编程:卷1>中介绍了5中I/O模型,JAVA作为运行在宿主机上的程序,底层也遵循这5中I/O模型规则.这5中I/O模型分别是: 阻塞式IO 非阻塞式IO I/O复用 信号驱动 ...

  7. UNIX环境高级编程——网络基础概念

    TCP协议分成两个不同的协议: 1.网络传输中差错的传输控制协议TCP 2.专门负责对不同网络进行互联的互联网协议IP 网络体系结构概念: 网络体系结构即是指网络的层次结构和每层所使用协议的集合 OS ...

  8. 网络编程—网络基础概览、socket,TCP/UDP协议

    网络基础概览 socket概览 socket模块—TCP/UDP的实现 TCP/UDP总结 网络基础概览 osi七层协议各层主要的协议 # 物理层传输电信号1010101010 # 数据链路层,以太网 ...

  9. Socket网络编程--网络爬虫(1)

    我们这个系列准备讲一下--网络爬虫.网络爬虫是搜索引擎系统中十分重要的组成部分,它负责从互联网中搜集网页,采集信息,这些网页信息用于建立索引从而为搜索引擎提供支持,它决定着整个引擎系统的内容是否丰富, ...

随机推荐

  1. VS“无法查找或打开PDB文件”解决方法

    ``#运行时报错提示 "温度柱状图.exe"(Win32): 已加载"C:\Windows\SysWOW64\rpcrt4.dll".无法查找或打开 PDB 文 ...

  2. 开箱即用的Vite-Vue3工程化模板

    开箱即用的Vite-Vue3工程化模板 前言 由于临近毕业肝毕设和论文,停更有一段时间了,不过好在终于肝完了大部分内容,只剩下校对工作 毕设采用技术栈Vue3,Vite,TypeScript,Node ...

  3. 腾讯云原生混合云-第三方集群弹EKS应对突发流量的利器

    作者 何鹏飞,腾讯云专家产品经理,曾作为容器私有云.TKEStack的产品经理兼架构师,参与腾讯云内部业务.外部客户容器化改造方案设计,目前负责云原生混合云产品方案设计工作. 胡晓亮,腾讯云专家工程师 ...

  4. linux网络编程中INADDR_ANY的含义

    INADDR_ANY选项 网络编程中常用到bind函数,需要绑定IP地址,这时可以设置INADDR_ANY INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或&q ...

  5. BUAA OS实验调试指南:从看懂到看开

    一般的调试流程其实很简单:发现问题,稳定复现,确定临界条件,定位问题,修复问题,核查结果.迭代这个过程,形成一个闭环 老实说,OS的实验代码,开箱体验极差,程序跳来跳去,进了Lab4后还要考虑内核态切 ...

  6. K8S的资源管理

    K8S的资源管理 管理K8S资源的三种基本方法: 陈述式资源管理方法-使用cli工具进行管理. 声明式资源管理方式-主要依耐资源配置清单. GUI式资源管理方法-主要依耐图形界面. 陈述式资源管理方法 ...

  7. VMware vCenter重置web console SSO登录密码

    On a Windows Platform Services Controller or vCenter Server with Embedded Platform Services Controll ...

  8. Docker Swarm(六)Label 节点标签与服务约束

    前言 多节点 Swarm 集群下,可能节点的配置不同(比如 CPU.内存等),部署着不同类型的服务(比如 Web服务.Job服务等),当这些服务以 Service 或者 Stack 的形式部署到集群, ...

  9. 056.Python前端Django模型ORM多表基本操作

    一 准备工作 1.1 新建一个项目 root@darren-virtual-machine:~# cd /root/PycharmProjects/ root@darren-virtual-machi ...

  10. 007.kubernets的headless service配置和ingress的简单配置

    前面配置了servcie的nodepoint和clusterIP附在均衡 一 headless service配置 1.1 默认下的DNS配置 [root@docker-server1 deploym ...