XDP入门--通过用户态程序自动加载与卸载eBPF程序字节码到网卡

news2024/11/29 0:48:38

本文目录

  • 1、测试环境
  • 1、eBPF字节码的源代码实现
  • 3、用户态应用程度实现简介
  • 4、编译与运行
  • 5、运行状态验证

通过文章XDP入门–之hello world 我们知道,可以通过iproute2的ip工具向网卡去加载和卸载eBPF程序的字节码。但这个使用起来不太方便。而且在需要网卡恢复正常工作时,还需要输入相应的命令去手动卸载eBPF程序的字节码。
更为通用和可行的做法是创建一个常规的用户态程序做为让用户运行使用的管理控制程序。

  • 当用户态程序启动时,自动去加载eBPF程序的字节码到网卡
  • 当用户态程序退出时,自动去卸载eBPF程序的字节码,让相应网卡恢复正常工作模式
  • 当用户态程序运行时,用于对eBPF程序的字节码的运行做出配置与控制

所以本文章就介绍如上所述的自动加载和卸载的代码实现。

1、测试环境

硬件:基于树莓派Zero w + 带二个以太网卡的扩展底板----图中的RPi
网络:如下图所示

                                                     +- RPi -------+          +- old pc1----+
                                                     |         Eth0+----------+ Eth0        |    
                 +- Router ----+                     |  DHCP server|          | 10.0.0.10   |
                 | Firewall    |                     |   10.0.0.1  |          |             |
(Internet)---WAN-+ DHCP server +-WLAN AP-+-)))   (((-+ WLAN        |          +-------------+
                 | 192.168.3.1 |                     |             |          
                 +-------------+                     |             |          +- old pc2----+
                                                     |         Eth1+----------+ Eth0        |   
                                                     |             |          | 10.0.0.4    |                                                       
                                                     +-------------+          |             |
                                                                              +-------------+

在这里插入图片描述
在这里插入图片描述

1、eBPF字节码的源代码实现

这个字节码实现的功能:每进来一个报文就检测是不是IPV4的报文,如果是,则打印本报文的源IP地址和目标IP地址

#include <stdio.h>
#include <linux/bpf.h>
#include <net/ethernet.h>
#include <linux/if_vlan.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <bpf/bpf_helpers.h>

#ifndef __section
# define __section(NAME)                  \
   __attribute__((section(NAME), used))
#endif

__section("prog")
int xdp_ip_filter(struct xdp_md *ctx)
{
    void *end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    int ip_src;
    int ip_dst;
    long int offset;
    short int eth_type;

    char info_fmt1[] = "Dst Addr: %pi4";
    char info_fmt2[] = "Src Addr: %pi4";
    char info_fmt3[] ="-----------------";
   
    struct ethhdr *eth = data;
    offset = sizeof(*eth);

    if (data + offset > end) {
    return XDP_ABORTED;
    }
    eth_type = eth->h_proto;

    /* 只处理 IPv4 地址*/
    if (eth_type == ntohs(ETH_P_IPV6)) {
        return XDP_PASS;
    }

    struct iphdr *iph = data + offset;
    offset += sizeof(struct iphdr);
    /*  在读取之前,确保你要读取的子节在数据包的长度范围内  */
    if (iph + 1 > end) {
        return XDP_ABORTED;
    }
    ip_src = iph->saddr;
    ip_dst = iph->daddr;

    bpf_trace_printk(info_fmt3, sizeof(info_fmt3));
    bpf_trace_printk(info_fmt2, sizeof(info_fmt2), &ip_src);
    bpf_trace_printk(info_fmt1, sizeof(info_fmt1), &ip_dst);

    return XDP_PASS;
}

char __license[] __section("license") = "GPL";

3、用户态应用程度实现简介

注意,

  1. 因为实验测试用的树梅派只有二个以太网卡,所以代码里就写死二个网卡了。
  2. 这个用户态程序不会自动退出,只能以ctrl+c的方式强制退出
  3. 编程运行环境为32位linux,所以如果你的环境是64位的,char, short, int, long, long long的长度需要进行调整

#include <stdio.h>
#include <signal.h>
#include <sys/socket.h>
#include <net/if.h>
#include <bpf/bpf.h>
#include <linux/bpf.h>
#include <linux/rtnetlink.h>
#include "/usr/src/linux-6.1/tools/testing/selftests/bpf/bpf_util.h"

int flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
static int *ifindex_list;

// 退出时自动卸载eBPF字节码的函数
static void uninstall_exit(int sig)
{
        int i = 0;
        for (i = 0; i < 2; i++) {
                bpf_set_link_xdp_fd(ifindex_list[i], -1, 0);
        }
        exit(0);
}
// 以下是用户态程序入口
int main(int argc, char *argv[])
{
        int i;
        char filename[64];
        struct bpf_object *obj;
        struct bpf_prog_load_attr prog_load_attr = {
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
        int prog_fd;

        // 以下bridge.o依赖于你编译出来的.o文件名,做修改,.o要和当前代码编译出来的可执行程序放在同一个目录下
        snprintf(filename, sizeof(filename), "bridge.o");
        prog_load_attr.file = filename;

        // 从文件中载入eBPF字节码
        if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) {
                return 1;
        }

        ifindex_list = (int *)calloc(2, sizeof(int *));

        //注意,运行时,需要输入二个网卡的ifname, 就是eth0 eth1这种,依赖于系统不一样,可能名字会不同,下面的代码会把ifname转换在ifindex。
        ifindex_list[0] = if_nametoindex(argv[1]);
        ifindex_list[1] = if_nametoindex(argv[2]);

        for (i = 0; i < 2; i++) {
                // 将eBPF字节码安装到指定的网卡
                if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) {
                        printf("install xdp fd failed\n");
                        return 1;
                }
        }
        //设置程序退出时,自动卸载eBPF字节码的函数
        signal(SIGINT, uninstall_exit);

        // 进入运行循环,什么都不做,只打印一个working...
        while(1){
            i++;
            sleep(1);
            printf("working...%d\r\n", i);
        }

}


4、编译与运行

将用户态程序存成文件main.c, 将eBPF程序存成bridge.c, 分别用以下命令进行编译

gcc main.c -lbpf
sudo clang -O2 -Wall -target bpf -c bridge.c -o bridge.o

会在当前目录下生成a.out和bridge.o二个文件

然后用以下命令运行:

sudo ./a.out eth0 eth1

5、运行状态验证

  1. 用户态程序已经正常运行(这里有个告警打印,不影响功能,忽略则可)

meihualing@raspberrypi:~/userloadprint $ sudo ./a.out eth0 eth1
libbpf: elf: skipping unrecognized data section(4) .rodata.str1.1
working...3
working...4
working...5
working...6
working...7
working...8
working...9
working...10
working...11
working...12
working...13
working...14
working...15
working...16
working...17
working...18
working...19
working...20
working...21
working...22
working...23
working...24
working...25
working...26
working...27

2,然后eBPF字节码运行的输出如下(如需要详细的原理,可参见XDP入门–BPF程序如何打印log, printf log,打印日志)

因为eth0,eth1接着的电脑后台程序在和互联网交互,所以不需要任何东西就可以看到字节码的打印输出,你的linux特别干净,没有后台运行的东西,则可以从10.0.0.4 ping 10.0.0.10后,得到类似下方的输出。

sudo cat /sys/kernel/debug/tracing/trace_pipe

  kworker/u3:0-60      [000] d.s..  7613.615253: bpf_trace_printk: -----------------
    kworker/u3:0-60      [000] d.s..  7613.615287: bpf_trace_printk: Src Addr: 010.000.000.004
    kworker/u3:0-60      [000] d.s..  7613.615299: bpf_trace_printk: Dst Addr: 115.223.009.115
    kworker/u3:0-60      [000] d.s..  7613.615756: bpf_trace_printk: -----------------
    kworker/u3:0-60      [000] d.s..  7613.615784: bpf_trace_printk: Src Addr: 010.000.000.004
    kworker/u3:0-60      [000] d.s..  7613.615795: bpf_trace_printk: Dst Addr: 115.223.009.115
    kworker/u3:0-60      [000] d.s..  7613.617634: bpf_trace_printk: -----------------
    kworker/u3:0-60      [000] d.s..  7613.617668: bpf_trace_printk: Src Addr: 010.000.000.004
    kworker/u3:0-60      [000] d.s..  7613.617679: bpf_trace_printk: Dst Addr: 115.223.009.115
            sshd-1291    [000] d.s..  7613.620971: bpf_trace_printk: -----------------
            sshd-1291    [000] d.s..  7613.621009: bpf_trace_printk: Src Addr: 010.000.000.004
            sshd-1291    [000] d.s..  7613.621022: bpf_trace_printk: Dst Addr: 192.168.003.190
          <idle>-0       [000] d.s..  7613.628747: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s..  7613.628785: bpf_trace_printk: Src Addr: 010.000.000.004
          <idle>-0       [000] d.s..  7613.628797: bpf_trace_printk: Dst Addr: 192.168.003.190
          <idle>-0       [000] d.s..  7613.839358: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s..  7613.839397: bpf_trace_printk: Src Addr: 010.000.000.004
          <idle>-0       [000] d.s..  7613.839409: bpf_trace_printk: Dst Addr: 192.168.003.190
          <idle>-0       [000] d.s..  7613.849433: bpf_trace_printk: -----------------
          <idle>-0       [000] d.s..  7613.849473: bpf_trace_printk: Src Addr: 010.000.000.004
          <idle>-0       [000] d.s..  7613.849484: bpf_trace_printk: Dst Addr: 192.168.003.190

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

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

相关文章

浅析Java 多线程中的锁

前言 随着互联网技术的快速发展&#xff0c;多线程编程已经成为了现今编程领域中必不可少的知识点之一。Java 是一种广泛使用的编程语言&#xff0c;也是一些底层应用程序和高并发应用程序的首选语言。而 Java 提供的多线程编程机制和相关的锁机制&#xff0c;则成为了 Java 开…

SpringBoot 上传图片-指定目录按照日期存储

SpringBoot 上传图片-指定目录按照日期存储 1. 在配置文件中指定文件保存根目录 我用的yaml,用properties也行 file-save-path: D:/upload/2. 文件上传接口 package com.admin.controller.wechat;import cn.hutool.core.lang.UUID; import com.redic.base.Result; import com…

SpringMVC 程序开发

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 什么是 Spring MVCMVC 定义 怎么学 Spring MVCSpring MVC 创建和连接创建 Spring MVC 项目RequestMapping 注解介绍PostMappi…

二叉搜索树的实现(C语言)

目录 前言&#xff1a; 一&#xff1a;准备工作 (1)需要的头文件 (2)树节点结构体描述 (3)初始化 二&#xff1a;指针 三&#xff1a;插入新节点(建树) (1)生成一个新节点 (2)找插入位置 四&#xff1a;查找和遍历 (1)查找 (2)遍历 五&#xff1a;删除节点 六&…

mysql 主从同步

① 修改 master 配置文件② 新建同步账号③ 创建数据库④ 修改 slave 配置文件⑤ 配置主从关系⑥ 检验主从结果 角色ipmaster192.168.233.100slave1192.168.233.101slave2192.168.233.102 禁用 selinux 跟 firewal l情况下&#xff1a; ① 修改 master 配置文件 vim /etc/my…

NEFU linux实验二

在linux中&#xff0c;家目录又称“home目录”、“主目录”&#xff0c;是用户的宿主目录&#xff0c;通常用来保存用户的文件&#xff0c;可以使用“~”来表示。一个用户登录系统&#xff0c;进入后所处的位置就是“/home”&#xff0c;即家目录&#xff1b;root用户的家目录为…

路径规划算法:基于鸡群优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于鸡群优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于鸡群优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法鸡群…

输电线路故障诊断(利用随机森林方法实现二分类和多分类)

1.simunlike仿真系统的建立&#xff08; 运行效果 &#xff1a;输电线路故障诊断_哔哩哔哩_bilibili&#xff09; 2.在仿真系统的基础上获取数据集 分别获取单相接地故障、两相接地故障、两相间短路故障、三相接地故障、三相间短路故障和正常状态下的电流&#xff08;Ia,Ib,I…

一文带你了解MySQL之optimizer trace神器的功效

前言&#xff1a; 对于MySQL 5.6以及之前的版本来说&#xff0c;查询优化器就像是一个黑盒子一样&#xff0c;你只能通过EXPLAIN语句查看到最后优化器决定使用的执行计划&#xff0c;却无法知道它为什么做这个决策。这对于一部分喜欢刨根问底的小伙伴来说简直是灾难&#xff1…

2023年5月14日蓝桥杯c++省赛中级组

选择题讲解 1.)C++中,bool类型的变量占用字节数为 ( )。 A.1B.2 C.3 D.4 答案:A 解析:(C++ 中 bool 类型与 char 类型一样,都需要1 byte。一些其他类型的占用字节数:short:2 byte。int:4 byte。long long:8 byte。double:8 byte。) 2.)以下关于C++结构体的说…

代码随想录二刷 day04 | 链表之 24两两交换链表中的节点 19删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II

24. 两两交换链表中的节点 题目链接 解题思路&#xff1a; 先将一些可能会改变的节点保存一下&#xff0c;然后再按照三个步骤就行修改 注意 要使用改变以后节点的指针&#xff08;这个地方一刷的时候没注意到&#xff0c;稀里糊涂的过去了&#xff09; 代码如下&#xff1a;…

【八股】计算机网络-HTTP和HTTPS的区别、HTTPS加密传输原理

计算机网络-HTTP和HTTPS的区别、HTTPS加密传输原理 一、HTTP和HTTPS的基本概念二、HTTP与HTTPS的区别三、HTTPS加密传输原理1. 什么是HTTPS1.1 https诞生的原因1.2 https加密方式1.3.http和https的区别 2. https的工作流程3. 数字证书3.1 什么是数字证书3.2 如何申请数字证书3.…

亚马逊六页纸沟通法,拒绝PPT

亚马逊六页纸沟通管理法&#xff0c;拒绝PPT 使用一种简洁的「结构化备忘录」 内部管理会议沟通&#xff0c;每次不超过六页 趣讲大白话&#xff1a;让沟通更有效 【趣讲信息科技178期】 **************************** 那么“6页备忘录”到底是什么呢&#xff1f; 1. What we d…

供应链 | 在线平台的研究与思考(一):销售渠道与模式选择

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/4968391/ 编者按 当前&#xff0c;电商平台主要采用两种销售模式&#xff1a;代理和分销。商家根据自身情况选择线上或线下渠道&#xff0c;而电商平台会根据不同的线上商家选择适当的分销模式。本期编者精选的两…

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 目录 Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 六、关键脚本 一、简单介绍 Unity 工具类&#xff0c;自己整…

从事黑客工作十余年,究竟如何成为一名高级的安全工程师?

目录 1. 前言 2. 经验 3. 要考虑的问题 4. 学习路线详解 第一步&#xff1a;计算机基础 第二步&#xff1a;编程能力 第三步&#xff1a;安全初体验 第四步&#xff1a;分方向 尾言 参考书籍列表 1. 前言 说实话&#xff0c;一直到现在&#xff0c;我都认为绝大多数…

STP协议

目录 STP的基本概念&#xff1a; 桥ID&#xff08;Bridge ID&#xff09;&#xff1a; 根桥&#xff1a; 开销&#xff08;Cost&#xff09;&#xff1a; RPC&#xff08;根路径开销&#xff09;&#xff1a; Port ID&#xff1a; BPDU&#xff1a;&#xff08;网桥协议…

ROS:ROS是什么

目录 一、ROS简介二、ROS可以做些什么三、ROS特征四、ROS特点4.1点对点设计4.2不依赖编程语言4.3精简与集成4.4便于测试4.5开源4.6强大的库与社区 五、ROS的发展六、ROS架构6.1OS层6.2中间层6.3应用层 七、通信机制八、计算图8.1节点&#xff08;Node&#xff09;8.2节点管理器…

当ChatGPT参加中国高考,把全国A卷B卷喂给它后,竟严重偏科

作者 |Python ChatGPT作为一个智能人机对话应用&#xff0c;在推出后迅速风靡全球。仅仅一个月的时间&#xff0c;其用户数量已经突破了一亿大关。人们也用ChatGPT测试了很多考试项目&#xff0c;例如SAT、AP、GRE等。然而&#xff0c;如果让ChatGPT来参加我们中国的高考&…

Jetson Orin Nano 快速安装 ROS2 Foxy详解

大家好&#xff0c;我是虎哥&#xff0c;入手一块Jeston Orin nano 8G模块&#xff0c;这个模块因为是英伟达未来5年左右主推的模块&#xff0c;所以我逐步会将之前所有的应用都在这个模块环境上做适配&#xff0c;本章内容&#xff0c;我将主要围绕安装ROS2 Foxy版本为主展开。…