参考:xdp-tutorial

basic01

主要介绍了如何编写简单的xdp程序以及通过libbpf库或者iproute2将xdp程序加载到内核并将内核代码挂载到指定的网络接口上。

Simple XDP code:

SEC("xdp")
int  xdp_prog_simple(struct xdp_md *ctx)
{return XDP_PASS;
}

section本身的作用就是来自汇编中的声明,汇编每一段开头都有不同的声明,表示接下来这一段的内容是什么。如下图所示,BPF字节码存储在ELF文件中。为了将BPF字节码加载到内核中,用户空间需要ELF loader来读取ELF文件并将其以正确的格式加载到内核,libbpf库即提供了这样的ELF loader以及XDP辅助函数。

Loading and the XDP hook

第一种是使用iproute2工具来加载BPF程序到内核,标准的iproute2工具包含BPF ELF loader,但是因为这个加载器并不是基于libbpf,当开始使用BPF映射时将会出现不兼容的情况。

第二种是基于libbpf API。

xdp_pass_user.c这个文件干的事情就是将.o文件(bpf经过llvm+clang编译生成的字节码加载到内核并attach到内核对用设备上)

两个关键函数:

bpf_prog_load 返回加载到内核的文件句柄

bpf_set_link_xdp_fd将内核文件挂载到hook点

basic02

主要介绍了对于包含多个section的BPF字节码文件(obj文件),在libbpf中如何将obj文件加载到内核并根据section name选择将对应的section内核代码挂载到hook点

任务一

可以使用下列命令查看指定网络接口上的xdp程序:

ip link list dev veth-basic02
bpftool net list dev veth-basic02

使用命令sudo ./xdp_loader --dev veth-basic02 --unload --progsec xdp_drop即可在网络接口上卸载指定的xdp程序。

任务二

比较简单,可参考原github链接

整个流程:

basic03

创建BPF映射

创建BPF映射是通过定义如下全局结构变量struct bpf_map_def实现的,

struct bpf_map_def SEC("maps") xdp_stats_map = {.type        = BPF_MAP_TYPE_ARRAY,.key_size    = sizeof(__u32),.value_size  = sizeof(struct datarec),.max_entries = XDP_ACTION_MAX,
};

需要注意的是,一切都通过bpf系统调用进行,这就意味着用户空间程序需要通过调用不同的bpf系统调用分别创建BPF映射和BPF程序。那么一个BPF程序如何引用BPF映射呢——首先加载所有的BPF映射并存储其对应的文件描述符,然后通过ELF重定位表来确定BPF程序对给定的BPF映射的引用,每个引用都会被重写,因此BPF字节码指令能够使用正确的映射文件描述符。所有这些需要在BPF程序加载到内核之前完成。

注释:

Helper Functions(BPF辅助函数):辅助函数对应着一系列内核定义的函数调用,使得BPF程序可以从内核检索数据或将数据推送到内核。不同类型的BPF程序可用的辅助函数有所区别,例如,挂载到sockets上的BPF程序所能调用的辅助函数是挂载到tc层的BPF程序的一个子集(举个栗子,用于构建轻量级隧道的封装和解封装的辅助函数只对较低tc层(lower tc layers)的BPF程序适用,而用于将通知推送到用户空间的辅助函数的事件输出辅助函数可用于tc和XDP的BPF程序)。BPF辅助函数定义在bpf.c文件中,bpf_attr联合中包含不同的匿名结构体用于向系统调用传递不同的参数信息,比如可指定创建映射所需要的信息、对映射进行操作所需的参数信息或者加载BPF程序到内核所需的参数信息等等。以bpf_map_update_elem为例,其调用过程如下:

第一个参数__NR_bpf不同对应不同的架构指令集,cmd是枚举变量,指明进行的操作,后面两个参数传递必要的参数信息。

定位bpf映射的文件描述符

即通过libbpf库中实现的bpf_object__find_map_by_name来获取struct bpf_map类型结构体,并通过bpf_map_fd从中获取映射的文件描述符。

检查映射信息(如datarec是否是期望大小)

关键函数调用

err = bpf_obj_get_info_by_fd(map_fd, info, &info_len); 该函数是bpf辅助函数,通过映射的文件描述符获取映射信息并存到info结构体。

用户空间程序读取映射内容

任务一运行结果:

basic04

在这之前的第三节,用户空间程序xdp_load_and_stats.c既进行了BPF程序的加载又从BPF映射中读取数据包的统计数据,通常只有加载BPF程序的同一个程序才能访问BPF map。在这一节将了解到从“外部”程序来访问BPF映射,这是通过BPF虚拟文件系统实现的。

这部分的用户空间程序将分为两个:

  • xdp_loader.c: 将BPF/XDP程序加载到内核
  • xdp_stats.c:从内核BPF映射获取并打印数据

接下来阅读相关代码并做笔记以供之后实践参考:

xdp_prog_kern.c文件的修改

  1. 映射类型更改为BPF_MAP_TYPE_PERCPU_ARRAY,即在每个CPU上都维护一个BPF映射存放数据包统计信息,最后在用户空间程序打印数据包统计信息时需要对所有CPU上的统计信息手动汇总。

  2. 实现xdp_stats_record_action(强制内联)进行存储在BPF映射中不同类型数据包统计信息的更新。

  3. 新增xdp_drop,xdp_abort的BPF program。当指定其中的一个SEC(如xdp_drop)挂载到特定hook点时,数据包到达该hook点由于XDP程序返回XDP_DROP,数据包被丢弃。

loader.c文件的实现

  1. load_bpf_and_xdp_attach函数内部新增:

接下来看load_bpf_object_file_reuse_maps函数的实现:

  1. loader.c文件新增将BPF程序和BPF映射保存到虚拟文件系统。

接下来查看pin_maps_in_bpf_object的具体实现,其传入的第一个参数是struct bpf_object类型结构体,第二个参数是挂载bpf程序的网络接口名(如veth-basic02)。在/sys/fs/bpf/cfg.ifname路径下保存两种类型的对象:BPF映射和完整的BPF程序。

进入此函数,首先检查/sys/fs/bpf/map_filename对应的映射持久化文件是否存在,如果存在说明之前的XDP程序没有被清理,调用libbpf中的bpf_object__unpin_maps(bpf_obj, pin_dir)来清理该目录/sys/fs/bpf/cfg.ifname下面的所有map映射持久化文件。然后通过bpf_object__pin_maps(bpf_obj, pin_dir);将该BPF对象中的所有映射保存到文件系统。

packet01

+-----------------------------+                          +-----------------------------+
| Root namespace              |                          |Testenv namespace 'package'  |
|                             |      From 'test01'       |                             |
|                    +--------+ TX->                RX-> +--------+                    |
|                    |package +--------------------------+  veth0 |                    |
|                    +--------+ <-RX                <-TX +--------+                    |
|                             |       From 'veth0'       |                             |
+-----------------------------+                          +-----------------------------+
key(src ip) value (1)
192.168.11.168 1
192.168.11.169 1
192.168.11.190 1
1

packet 02

实现效果:

packet 03-对数据包进行重定向

XDP_TX:转发数据包,这可能在数据包被修改前或修改后发生,转发数据包意味着将接收到的数据包发送回数据包到达的同一网卡。

任务一实验效果:

任务二:

除了能够将数据包从相同的网络接口发送回去,XDP还能将数据包直接发送到其他网络接口的出口(egress ports of other interfaces),这可以通过bpf_redirect或者bpf_redirect_map辅助函数(返回XDP_REDIRECT)来实现,实验效果如下:

任务三:

bpf_redirect_map:将数据包重定向其他端口,是任务二使用的bpf_redirect一般化版本,更为常用且高效。

这个时候在right网络接口的rx端和left网络接口的rx端都附加上xdp_redirect_map对应的BPF程序,这个时候,在/sys/fs/bpf/left和/sys/fs/bpf/right虚拟文件夹下面都会产生对应的持久化映射文件,如下图所示:

  • 映射tx_port存储的是虚拟端口到重定向网络接口索引的映射关系,映射类型为BPF_MAP_TYPE_DEVMAP, 在BPF程序中通过语句action = bpf_redirect_map(&tx_port, 0, 0);将数据包转发到另一个接口的egress端,其第一个参数是Map,第二个参数是key,第三个参数是flag;
  • 映射redirect_params存储的是源mac地址到目的mac地址的映射关系,也就是说,当数据包从子命名空间的veth0接口发出来到达left网络接口,其上面的BPF程序就会修改以太帧的头部字段,将报文中实际的目的mac地址(left网络接口mac地址)替换成right子命名空间下的veth0网络接口的mac地址,最后执行bpf_redirect_map完成数据包的转发;

总结起来就是干了两件事情,一是修改链路层数据包头,二是转发。xdp_prog_user.c的作用就是分别设置left和right网络接口对应的映射文件所存储的参数信息。

为方便起见,提供了t redirect right left来简化操作步骤,bpf_redirect_map做的事情就是将数据包从left网口(网络接口索引为18)的入口直接转发到right网口的出口(网络接口索引为20),修改mac地址是将目的mac地址修改为right子空间veth0网口的mac地址,这样就不会被veth0网口丢弃

实验效果:发现是可以ping通的。

任务四

之前都是通过硬编码的方式来实现数据包重定向实现内部接口之间的数据包转发。bpf_fib_lookup辅助函数能够帮助XDP和TC程序访问内核中的路由表,并返回要将数据包转发到的接口的网络索引值。效果如下:

XDP/BPF Github教程相关推荐

  1. 一篇文章了解Github和Git教程-AndroidStudio上传Github教程

    前言 为了方便保存自己的代码,下班后可以回家继续进行,自己的码农工作,介绍一下Github. 什么是Github呢? 作为一个编程人员,我觉得得了解一下Github吧! 当然,如果你放弃了码农或者技术 ...

  2. git 使用writer_「github 教程」写给大家看的教程:从零开始在GitHub上部署个人博客 - seo实验室...

    github 教程 本文适合的读者:希望搭建个人博客的朋友,无需了解Git.Ruby等技术. GitHub是一个利用Git进行版本控制.专门用于存放软件代码与内容的共享虚拟主机服务(来自维基百科),我 ...

  3. Github教程】史上最全github使用方法:github入门到精通

    [Github教程]史上最全github使用方法:github入门到精通 [初识Github] 首先让我们大家一起喊一句"Hello Github".YEAH!就是这样. Git是 ...

  4. [转载]专为设计师写的github教程

    题目是专为设计师,看了之后对于像我这样刚开始接触的人来说也是一篇不错的教程~ 原文如下: 在互联网行业工作的想必都多多少少听说过GitHub的大名,除了是最大的开源项目托管平台,许多企业也都是用Git ...

  5. 为了你能愉快地积累Python项目,我做了一份手把手的GitHub教程

    2019 年 11月 13-14 日,GitHub 举办了年度开发者大会 GitHub Universe 2019.今年的 GitHub 大会最大的亮点莫过于--GitHub Archive Prog ...

  6. ubuntu上传代码到github教程--token

    ubuntu上传代码到github教程 注意的点: 1.上传代码的命令行在github新建项目的时候会给出来的(不要自动生成readme文件的话) 2.要上传什么代码就在该文件夹下进行git init ...

  7. [github 教程]手把手教你最简单的开源项目托管GitHub入门教程_github 教程

    [github 教程]手把手教你最简单的开源项目托管GitHub入门教程--简介 自从google code关闭了下载服务了之后,GitHub作为了目前最好用的免费 开源 项目托管站点,众多开源项目都 ...

  8. Facebook 流量路由最佳实践:从公网入口到内网业务的全路径 XDP/BPF 基础设施

    本文翻译自 Facebook 在 LPC 2021 大会上的一篇分享:<From XDP to Socket: Routing of packets beyond XDP with BPF[1] ...

  9. 「Github」Linux/Ubuntu下终端Github教程与手册

    1 前期准备 1.1 环境说明 系统:Ubuntu 18.04 1.2 前期准备 打开终端,确保git命令可使用:如果不能使用请自行搜索如何安装git 建议新建一个专门存储各个仓库的文件夹(假定文件夹 ...

最新文章

  1. 每秒能捕捉万亿帧的相机
  2. 28. LAST() 函数
  3. QT显示框架嵌入Vs控制台工程
  4. Git vs SVN
  5. const在c语言中作用,在C语言中const作用全面总结.doc
  6. 纪念我2014.1.4 中国银行广东分行面试
  7. 写在WinHEC开幕之际:沿着Windows我们一路走来
  8. 传感器的定义、构成、分类
  9. 电信华为 HG526 破解 - 简易启用USB恢复
  10. java常见面试题:Java程序员面试题(四)
  11. 读取 system serial number
  12. R-CNN解读+代码梳理
  13. windows下更新pip源
  14. Python的初步了解
  15. PyTorch基础:Tensor的自动广播机制与向量化
  16. Scala 034 特质trait
  17. 高铁的速度 华为的服务
  18. 智能合约Smart Contract技术详解
  19. 互联网dns架构实现智能dns实现
  20. decimal 和 numeric 的区别

热门文章

  1. Verilog基础知识
  2. 【Unity学习历程之一】给Visual Studio中的C#脚本编辑添加自动补全
  3. 我的内存怎么不够用了?
  4. UNI-APP隐私政策问题无法上架
  5. 前端之vue3使用动画库animate.css(含动画、过渡)
  6. C++字符串转换为数值型
  7. emui系统就是鸿蒙吗,华为EMUI是不是鸿蒙系统
  8. centos7安装部署gitlab
  9. HUST-多媒体基础PPT目录
  10. Uncaught SyntaxError: Unexpected identifier问题解决