Rust的eBFP框架Aya(一) - Linux内核网络基础

前言

在我的Rust入门及实战系列文章中已经说明, Rust是一门内存安全的高性能编程语言,从它的这些优秀特性来看,就是一门专为系统开发而诞生的语言。至于很多使用Rust来进行web开发的行为,不能说它们不好,只能说是杀鸡焉用牛刀耳。

本系列的文章旨在为大家介绍一个新的专用于开发eBPF程序的Rust框架:Aya。Aya 是 Rust 中的一个 eBPF(Extended Berkeley Packet Filter)库,提供了用 Rust 语言编写、加载和运行 eBPF 程序的能力。eBPF 是一种强大的技术,用于在 Linux 内核中安全地运行沙盒程序,常用于网络编程、性能监控和安全增强。我们在后面的文章中将对eBPF进行更加详细和深入的介绍。

Linux内核网络基础

如果你还不了解什么是eBPF, 那么其实从它的名称中便可见一斑,从Packet Filter可以看出,这显然是用于包的处理的一门技术。它通过在Linux的内核中的不同挂载点,加入一个隔离可控的二进制程序,来达到我们想要为内核增加功能处理网络包的目的。既然是在内核的网络处理流程中动手脚,那么在开始之前,我们有必要对Linux内核原本的网络处理流程有基本的认知, 否则,如果直接开始eBPF的编写,那么我们很可能会变成狗拿刺猬,无从下手。

网络模型概览

众所周知,网络模型的的划分有不同的方法, 最流行的莫过于经典的OSI七层网络模型, TCP/IP四层网络模型,还有综合两者而成的五层网络模型。从理解内核网络工作原理的角度出发,我们选择五层网络模型进行接下来的探究:
请添加图片描述

注: 在Linux系统中,内核源码的位置位于/usr/src/目录下的对应内核命名的目录下,例如我使用的azure虚拟机上,内核代码的目录为/usr/src/linux-headers-5.15.0-1052-azure。 后文提到的内核代码相关的目录都是以此为根的相对路径。

Linux内核在收到一个网络数据包后,首先会由网卡驱动程序(相关内核代码位于drivers/net/ethernet中)先进行处理, 然后回交由内核中处理协议栈相关的代码进行处理(相关内核代码位于kernel/net/中),处理完成后的结果,再由socket提供接口,供用户空间的应用层程序访问。

网络中断处理原理

那么从网卡收到数据包,是怎么传递给内核进行处理的呢,这里就要谈到中断处理了。 从硬件的角度来看,当一个网卡收到数据包时, 它会进行以下两件事情:

  • 将数据包以DMA(Direct Memory Access)的方式, 将收到的数据帧存放到内存的环形缓冲区中(Buffer Ring);
  • 向CPU的引脚施加一个电压变化,向CPU表明现在有一个数据来了,需要处理。

上述这种通过向CPU引脚施加电压的硬件操作,被称为硬中断。那么CPU此时就会对收到的数据包进行处理,那么如何处理呢?网络包如果一直不停的到来,而对网络包的处理往往又是复杂和耗时的,如果CPU每收到一个数据包都对它进行处理完成后再干别的工作,就会导致CPU的占用率过高,而无法对其他的硬中断进行响应了,比如鼠标键盘等设备发起的硬中断请求。

软中断注册

因此,当CPU收到一个网卡发来的硬中断时,它会告诉网卡驱动程序: “你先去内存登记一下待办事项吧”,于是网卡驱动程序会在内存中标记一个变量,表示这里有一个网络包需要人手来处理了。这个在内存中设置标志的操作,就被称为软中断。

上述过程的图示如下:
请添加图片描述

软中断处理

内核驱动程序处理

在我们的Linux启动后,内核中会运行一个进程ksoftirqd, 它的职责就是检测内存中是否有软中断需要处理, 一旦检测到这是一个网络驱动注册的软中断,就会调用网卡驱动中的poll函数,从内存的环形缓冲区中将网络数据包收下来并进行处理, 这个过程的图示如下:
请添加图片描述
其中, 网卡驱动程序中的igb_poll()函数会从内存的环形缓冲区中将完整的网络数据包取出来,然后调用igb_clean_rx_irq()函数进行处理,这些处理包括:

  • 校验收到的数据格式是否是一个合法的网络包;
  • 将收到的数据包格式化成skb,解析timestamp, VLAN id, protocol等字段信息
内核协议栈处理

在驱动程序对数据包进行处理后,处理完的数据将被发送到内核的协议栈进行处理,在进入协议栈之前,内核中存在一个GRO引擎,它的作用是把一些小的网络包合成一个大的网络包,一次性发给协议栈进行处理,目的是减少传送给协议栈的包数量,这有助于减少 CPU 的使用量。
请添加图片描述
如上图所示,在数据包进入协议栈后:

  • 首先会调用netif_receive_skb()函数,其中会辨别数据包的网络层协议,根据网络层协议调用不同的函数;
  • 例如判断得到这个数据包是个IP包,则会接下来调用ip_rcv()函数,在其中又会判断它的传输层协议,根据其是TCP还是UDP而调用不同的函数;
  • 例如判断得到这是一个TCP包,那么将继续调用tcp_rcv()函数对数据进行处理;
  • 处理完成后的数据可以供用户通过socket访问;

小结

网络模块在Linux内核中及其复杂,上面的介绍以尽可能简单明了的方式描述了一个数据包从网卡收到它开始,如何被内核进行处理的整个过程。其中包含了CPU硬中断,ksoftiqrd线程,软中断处理,网卡驱动对数据包的处理,skb的创建, 网络协议栈对数据包的处理等过程。

本文没有涉及用户空间应用程序从socket取包的过程,这涉及到recvfrom系统调用,也是一个比较复杂的话题。本系列文章旨在介绍Rust开发eBPF程序,只关注内核对网络包的处理流程,因此用户空间取包不在我们的关注范围内。在了解了网络包在内核中的处理流程之后,对于后需eBPF程序的挂载点,我们应当会有更清晰的认识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/206761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2017下半年软工(桥接模式)

题目——桥接模式(抽象调用实现部分) package org.example.桥接模式;/*** 桥接模式的核心思想是将抽象部分与它的实现部分分离,使它们可以独立变化,就是说你在实现部分:WinImp、LinuxImp基础上还能加上RedHatImp&#…

03-微服务架构构建之微服务拆分

文章目录 前言一、微服务拆分的原则二、微服务拆分的时机三、微服务拆分的方法总结 前言 微服务架构是将一个单体应用程序拆分为一个个独立且保持松耦合的服务的一种架构方式,每个服务有着独立的数据库并且能独立运行部署。微服务架构的构建过程中,第一…

偷窃别人的密码

在工作中都用到virtual machine吧,同事之间还互相借用机器,或者不借用但凭借自己的域账号能登录任意机器,而且每个域账号都有sudo权限? 那你要小心了! 因为我可以轻易记录下每个ssh登录到我机器,或者我在…

uniapp 输入框输入时,会将内容顶上去的解决方案

// 设置页面最小高度 export const setPageMinHeight () > {return {position: relative,min-height: uni.getSystemInfoSync().windowHeight px} }页面使用: import {setPageMinHeight} from "/utils/uniUtil";data() {return {minHeight: setPag…

Unity 状态系统

状态系统 原理食用方法Demo 原理 #mermaid-svg-lUbxJ8eMP3KqrEhY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lUbxJ8eMP3KqrEhY .error-icon{fill:#552222;}#mermaid-svg-lUbxJ8eMP3KqrEhY .error-text{fill:#55…

官方officevisio在线安装包

在线安装包,在线就是要有网络环境,你能搜到这篇博客,就初步具备网络环境 visio在线安装包.zip官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘 在线安装包如下,双击执行安装即可,可供选择的64/32位 软件的激活与…

netty使用

支持协议 TCP/UDPHTTP/HTTPSWebSocketSPDY/HTTP2MQTT/CoAP 服务端 常用类 ServerBootstrap 服务端配置类 //设置线程组、parentGroup处理连接、childGroup处理I/O group(EventLoopGroup parentGroup, EventLoopGroup childGroup) //Channel通过何种方式获取新的连接(NioSe…

【扩散模型】ControlNet从原理到实战

ControlNet从原理到实战 ControlNet原理ControlNet应用于大型预训练扩散模型ControlNet训练过程ControlNet示例1 ControlNet与Canny Edge2. ControlNet与Depth3. ControlNet与M-LSD Lines4. ControlNet与HED Boundary ControlNet实战Canny Edge实战Open Pose 小结参考资料 Cont…

Linux系统上RabbitMQ安装教程

一、安装前环境准备 Linux:CentOS 7.9 RabbitMQ Erlang 1、系统内须有C等基本工具 yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c kernel-devel m4 ncurses-devel tk tc xz socat2、下载安装包 1)首先&a…

[linux] kaggle 数据集用linux下载

你可以通过以下步骤获取Kaggle的下载链接并在Linux中进行下载: 首先,确保你已经安装了Python和Kaggle API。如果没有安装,你可以通过以下命令安装: pip install kaggle 接着,你需要在Kaggle网站上获取API Token。登录…

时间相关类

内容 JDK7时间相关类JDK8时间相关类 第一章 Date类 1.1 Date概述 java.util.Date类 表示特定的瞬间,精确到毫秒。 继续查阅Date类的描述,发现Date拥有多个构造函数,只是部分已经过时,我们重点看以下两个构造函数 public Dat…

【PyTorch】 暂退法(dropout)

文章目录 1. 理论介绍2. 实例解析2.1. 实例描述2.2. 代码实现2.2.1. 主要代码2.2.2. 完整代码2.2.3. 输出结果 1. 理论介绍 线性模型泛化的可靠性是有代价的,因为线性模型没有考虑到特征之间的交互作用,由此模型灵活性受限。泛化性和灵活性之间的基本权…

Docker构建自定义镜像

创建一个docker-demo的文件夹,放入需要构建的文件 主要是配置Dockerfile文件 第一种配置方法 # 指定基础镜像 FROM ubuntu:16.04 # 配置环境变量,JDK的安装目录 ENV JAVA_DIR/usr/local# 拷贝jdk和java项目的包 COPY ./jdk8.tar.gz $JAVA_DIR/ COPY ./docker-demo…

Java基础50题: 21.实现一个方法printArray, 以数组为参数,循环访问数组中的每个元素,打印每个元素的值.

概述 实现一个方法printArray, 以数组为参数,循环访问数组中的每个元素,打印每个元素的值. 代码 public static void printArray(int[] array) {for (int i 0; i < array.length; i) {System.out.println(array[i] " ");}System.out.println();}public static…

【数据结构c实现】顺序表实现

文章目录 线性表线性表的顺序实现结点结构结点初始化增配空间Inc打印顺序表show_list线性表长度length尾部插入push_back头部插入push_front尾部删除pop_back头部删除pop_front按位置插入insert_pos按值查找find按位置删除delete_pos按值删除delete_val排序sort(冒泡&#xff1…

云上业务DDoS与CC攻击防护实践

案例背景&#xff1a;DDoS攻击来势汹汹&#xff0c;云上业务面临威胁 某网络科技有限公司&#xff0c;SaaS化创业公司&#xff0c;业务基于云上开展。其业务主要为各大网站提供安全验证服务&#xff0c;且市场占有率较高&#xff0c;服务客户遍布金融、直播、教育、电商等多个领…

【日常总结】mybatis-plus WHERE BINARY 中文查不出来

目录 一、场景 二、问题 三、原因 四、解决方案 五、拓展&#xff08;全表全字段修改字符集一键更改&#xff09; 准备工作&#xff1a;做好整个库备份 1. 全表一键修改 Stage 1&#xff1a;运行如下查询 Stage 2&#xff1a;复制sql语句 Stage 3&#xff1a;执行即可…

100. 相同的树(Java)

目录 解法&#xff1a; 官方解法&#xff1a; 方法一&#xff1a;深度优先搜索 复杂度分析 时间复杂度&#xff1a; 空间复杂度&#xff1a; 方法二&#xff1a;广度优先搜索 复杂度分析 时间复杂度&#xff1a; 空间复杂度&#xff1a; 给你两棵二叉树的根节点 p 和…

L1-028:判断素数

题目描述 本题的目标很简单&#xff0c;就是判断一个给定的正整数是否素数。 输入格式&#xff1a; 输入在第一行给出一个正整数N&#xff08;≤ 10&#xff09;&#xff0c;随后N行&#xff0c;每行给出一个小于231的需要判断的正整数。 输出格式&#xff1a; 对每个需要判断的…

Kotlin(十五) 高阶函数详解

高阶函数的定义 高阶函数和Lambda的关系是密不可分的。在之前的文章中&#xff0c;我们熟悉了Lambda编程的基础知识&#xff0c;并且掌握了一些与集合相关的函数式API的用法&#xff0c;如map、filter函数等。另外&#xff0c;我们也了解了Kotlin的标准函数&#xff0c;如run、…