xdp测试例子

2023-11-01

牛刀小试

 linux内核协议栈实现了一个虚拟机,允许用户程序向内核注入二进制字节码。注入的程序,就可以做一些有趣的事情,比如:负载均衡,数据包检测,加速容器网络转发,做个网损仪。
 本篇,运行一个测试程序,丢弃网卡收到的icmp包。
 xdp-drop.c,源码url:https://gitlab.com/mwiget/xdp-drop-test/-/tree/master

//https://gitlab.com/mwiget/xdp-drop-test/-/tree/master
//https://stackoverflow.com/questions/64861121/ebpf-program-load-fails-without-verifier-log
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#define SEC(NAME) __attribute__((section(NAME), used))
/*
struct bpf_map_def {
	unsigned int type;
	unsigned int key_size;
	unsigned int value_size;
	unsigned int max_entries;
	unsigned int map_flags;
	unsigned int id;
	unsigned int pinning;
	unsigned int inner_id;
	unsigned int inner_idx;
};
struct bpf_map_def SEC("maps") dummy_map = {
    .type        = BPF_MAP_TYPE_ARRAY,
    .key_size    = sizeof(int),
    .value_size  = sizeof(int),
    .max_entries = 1,
};*/
SEC("drop_icmp")
int drop_icmp_func(struct xdp_md *ctx) {
  //int key = 0;
  //int *v = (int*)bpf_map_lookup_elem(&dummy_map, &key);
  int ipsize = 0;

  void *data = (void *)(long)ctx->data;
  void *data_end = (void *)(long)ctx->data_end;

  struct ethhdr *eth = data;

  ipsize = sizeof(*eth);

  struct iphdr *ip = data + ipsize;
  ipsize += sizeof(struct iphdr);
  if (data + ipsize > data_end) {
    // not an ip packet, too short. Pass it on
    return XDP_PASS;
  }

  // technically, we should also check if it is an IP packet by
  // checking the ethernet header proto field ...
  if (ip->protocol == IPPROTO_ICMP) {
    return XDP_DROP;
  }

  return XDP_PASS;
}
char __license[] SEC("license") = "GPL";

 编译程序之前,也许需要升级下clang,参考博客[5]安装clang-13。
 编译

 clang -g -c -O2 -target bpf -c xdp-drop.c -o xdp-drop.o

 加载程序到网卡中:

sudo ip link set dev you-nic-name xdp obj xdp-drop.o sec drop_icmp

 查看网卡状态,我的usb网卡名字(you-nic-name)wlxe0e1a997ab49:

ip link show dev   you-nic-name

在这里插入图片描述
 测试,就是ping不通主机。
 卸载之后,恢复。

sudo ip link set dev you-nic-name xdp off

 昨天,因为clang的安装卡了三四个个小时。还是怀念在学校的日子,起码可以找个小弟,搞点测试,验证可行性。工作了,就像坐监,出卖时间换钱,没啥自己的时间。

进阶篇

 本节在xdp-drop.c代码中增加丢包个数统计的功能。
 xdp-drop.c,源码:

//https://gitlab.com/mwiget/xdp-drop-test/-/tree/master
//https://stackoverflow.com/questions/64861121/ebpf-program-load-fails-without-verifier-log
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <stddef.h>
#include <memory.h>
#include <sys/types.h> 
#define SEC(NAME) __attribute__((section(NAME), used))


#define __uint(name, val) int(*(name))[val]
#define __type(name, val) typeof(val) *(name)
#define __array(name, val) typeof(val) *(name)[]

/* struct bpf_map_def {
     unsigned int type;
     unsigned int key_size;
     unsigned int value_size;
     unsigned int max_entries;
 };
 
struct bpf_map_def  SEC("maps") drop_map = {
    .type        = BPF_MAP_TYPE_ARRAY,
    .key_size    = sizeof(int),
    .value_size  = sizeof(int),
    .max_entries = 1,
};*/


struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(key_size, sizeof(int));
	__uint(value_size, sizeof(int));
    __uint(max_entries, 1);
}drop_map SEC(".maps");


static void *(*bpf_map_lookup_elem)(void *map, void *key) =
	(void *) BPF_FUNC_map_lookup_elem;
SEC("drop_icmp")
int drop_icmp_func(struct xdp_md *ctx) {
  int key = 0;
  int *pkts_count=bpf_map_lookup_elem(&drop_map, &key);
  int ipsize = 0;

  void *data = (void *)(long)ctx->data;
  void *data_end = (void *)(long)ctx->data_end;

  struct ethhdr *eth = data;

  ipsize = sizeof(*eth);

  struct iphdr *ip = data + ipsize;
  ipsize += sizeof(struct iphdr);
  if (data + ipsize > data_end) {
    // not an ip packet, too short. Pass it on
    return XDP_PASS;
  }

  // technically, we should also check if it is an IP packet by
  // checking the ethernet header proto field ...
  if (ip->protocol == IPPROTO_ICMP) {
    if(pkts_count){
        (*pkts_count)++;
    }
    return XDP_DROP;
  }

  return XDP_PASS;
}

char __license[] SEC("license") = "GPL";
/*  reference
https://www.tigera.io/learn/guides/ebpf/ebpf-xdp/
https://cloud.tencent.com/developer/article/1770282
https://feisky.xyz/posts/2021-01-29-ebpf-program/
*/

/*
fatal error: sys/cdefs.h: No such file or directory
sudo apt-get --reinstall install libc6 libc6-dev
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/include/x86_64-linux-gnu/ 

clang -g -c -O2 -target bpf -c xdp-drop.c -o xdp-drop.o
llvm-objdump  -h  xdp-drop.o
objdump -h  xdp-drop.o


sudo ip link set dev wlxe0e1a997ab49 xdp obj xdp-drop.o sec drop_icmp
ip link show dev wlxe0e1a997ab49
sudo ip link set dev wlxe0e1a997ab49 xdp off
*/

 我原来安装的是ubuntu 18.04。当加载到内核的程序中有自定义map,并且使用了bpf_map_lookup_elem函数时候,总是报“R1 type=inv expected=map_ptr”的错误。后来我就重装了ubuntu 20.04,遇见系统安装问题,卡了很久[6]。
 用户程序drop-pkt-reader.c,读取丢包个数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h> //sleep
#include <libgen.h>// basename
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <linux/if_link.h>//XDP_FLAGS_UPDATE_IF_NOEXIST

#include <net/if.h>
int print_if_nameindex(){
    struct if_nameindex *if_ni, *i;
    if_ni = if_nameindex();
    if(if_ni){
        for (i = if_ni; !(i->if_index == 0 && i->if_name == NULL); i++){
            printf("%s,%d\n",i->if_name,i->if_index);
        }
        if_freenameindex(if_ni);
    }
    return 0;
}
/*
https://www.cnblogs.com/charlieroro/p/13403672.html
Native XDP(XDP_FLAGS_DRV_MODE)
Offloaded XDP(XDP_FLAGS_HW_MODE)
Generic XDP(XDP_FLAGS_SKB_MODE)
samples/bpf/xdp_sample_pkts_user.c
*/
static void usage(const char *prog)
{
	fprintf(stderr,
		"%s: %s  -n <ifname>  -o xxx.o\n\n"
		"OPTS:\n"
		"    -F    force loading prog\n"
		"    -S    use skb-mode\n",
		__func__, prog);
}
static int do_attach(int fd,uint32_t xdp_flags,int if_index,const char *if_name){
    struct bpf_prog_info info = {};
    uint32_t info_len = sizeof(info);
    int prog_id=-1,err = 0;
    err = bpf_xdp_attach(if_index, fd, xdp_flags, NULL);
    if (err < 0) {
        printf("ERROR: failed to attach program to %s\n", if_name);
        return prog_id;
    }
    err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
    if (err) {
        printf("can't get prog info - %s\n", strerror(errno));
        return prog_id;
    }
    prog_id = info.id;
    return prog_id;
}

static int do_detach(int prog_id,uint32_t xdp_flags,int if_index, const char *if_name)
{
	int curr_prog_id = 0;
	int err = 0;
	err = bpf_xdp_query_id(if_index, xdp_flags, &curr_prog_id);
	if (err) {
		printf("bpf_xdp_query_id failed\n");
		return err;
	}
	if (prog_id == curr_prog_id) {
		err = bpf_xdp_detach(if_index, xdp_flags, NULL);
		if (err < 0)
			printf("ERROR: failed to detach prog from %s\n", if_name);
	} else if (!curr_prog_id) {
		printf("couldn't find a prog id on a %s\n", if_name);
	} else {
		printf("program on interface changed, not removing\n");
	}
	return err;
}
void map_get_value_array(int fd, int key, int *value){
    if ((bpf_map_lookup_elem(fd, &key, value)) != 0) {
        fprintf(stderr,"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
        *value=-1;
	}
}
static volatile bool g_running=true;
static void sig_handler(int signo)
{
    g_running=false;
}
//  ./drop-pkt-reader -n wlxe0e1a997ab49 -o xdp-drop.o -S
int main(int argc, char **argv){
    char if_name[32]={0};
    char bpf_name[32]={0};
    const char *optstr = "n:o:FS";
	int prog_fd, map_fd=-1, opt;
    int prog_id=-1;
	struct bpf_program *prog;
	struct bpf_object *obj;
	struct bpf_map *map;
    uint32_t xdp_flags=XDP_FLAGS_UPDATE_IF_NOEXIST;
    const char *target_map="drop_map";
	if (signal(SIGINT, sig_handler) ||
	    signal(SIGHUP, sig_handler) ||
	    signal(SIGTERM, sig_handler)||
        signal(SIGTSTP,sig_handler)){
		perror("signal");
		return 1;
	}
    while ((opt = getopt(argc, argv, optstr)) != -1) {
        switch (opt) {
            case 'n':{
                memcpy(if_name,optarg,strlen(optarg));
                break;
            }
            case 'o':{
                memcpy(bpf_name,optarg,strlen(optarg));
                break;
            }
            case 'F':{
                xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
                break;
            }
            case 'S':{
                xdp_flags |= XDP_FLAGS_SKB_MODE;
                break;
            }
            default:{
                usage(basename(argv[0]));
                return 1;            
            }
        }
    }
    uint32_t if_index=if_nametoindex(if_name);
    printf("%s,%d,%d\n",if_name,if_index,optind);
    if(if_index){
        obj = bpf_object__open_file(bpf_name, NULL);
        if (libbpf_get_error(obj)){
            return 1;
        }
        //may try: bpf_object__find_program_by_title
        prog = bpf_object__next_program(obj, NULL);
        bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
        if(prog){
            const char *sec_name = bpf_program__section_name(prog);
            printf("sec:%s\n",sec_name);
        }
        int err = bpf_object__load(obj);
        if (err){
            bpf_object__close(obj);
            return 1;
        }

        prog_fd = bpf_program__fd(prog);
        map =bpf_object__find_map_by_name(obj,target_map);
        if(map){
            printf("finding %s \n",target_map);
        }else{            
            printf("finding %s failed\n",target_map);
            bpf_object__close(obj);
            return 1;
        }
        map_fd = bpf_map__fd(map);
        prog_id=do_attach(prog_fd,xdp_flags,if_index,if_name);
    }else{
        printf("nic %s does not exist\n",if_name);
    }
    int value=0;
    while(g_running){
        sleep(5);
        map_get_value_array(map_fd,0,&value);
        if(value>0){
            printf("drop pkts: %d\n",value);
        }
    }
    do_detach(prog_id,xdp_flags,if_index,if_name);
    if(obj){
        bpf_object__close(obj);
    }
    return 0;
}

 附带的CmakeLists.txt:

PROJECT(project)
cmake_minimum_required(VERSION 2.6)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall  -O2")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -O2")
SET(CMAKE_CXX_FLAGS "-fPIC")

#for libbpf
set(LIBBPF_DIR /build/root/usr)
include_directories(${LIBBPF_DIR}/include)
LINK_DIRECTORIES(${LIBBPF_DIR}/lib64)


set(EXECUTABLE_NAME "drop-pkt-reader")
add_executable(${EXECUTABLE_NAME} ${CMAKE_SOURCE_DIR}/drop-pkt-reader.c)
target_link_libraries(${EXECUTABLE_NAME} bpf)
#cmake .. -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++

 drop-pkt-reader.c依赖libbpf,需要源码编译安装[7]:

$ cd src
$ PKG_CONFIG_PATH=/build/root/lib64/pkgconfig DESTDIR=/build/root make install

 安装路径为/build/root,CmakeLists.txt有对应的路径配置:set(LIBBPF_DIR /build/root/usr)。
 把CmakeLists.txt和drop-pkt-reader.c复制到xxx文件夹中。编译程序:

cd xxx
mkdir build && cd build
cmake .. -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++
make
clang -g -c -O2 -target bpf -c xdp-drop.c -o xdp-drop.o

 测试:

 ./drop-pkt-reader -n nic-name -o xdp-drop.o -S

[1]简要概述XDP
[2]Linux网络新技术基石 |​eBPF and XDP
[3]eBPF 技术实践:加速容器网络转发
[4]一文看懂eBPF|eBPF实现原理
[5]ubuntu 18.04 install clang 11
[6]ubuntu grub-install failed
[7]github libbpf

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

xdp测试例子 的相关文章

随机推荐

  • Mybatis配置解析-03

    1 核心配置文件 JDBC中连接数据库的URL转义 jdbc mysql localhost 3306 mybatis useSSL true amp useUnicode true amp characterEncoding utf8 m
  • 传统@ServerEndpoint方式开发WebSocket应用和SpringBoot构建WebSocket应用程序

    小Hub领读 通过websocket的两种使用方式 让你更加深入理解用法 很多人不懂websocket怎么辨别是谁发送过来的 文中说到实现WebSocketConfigurer接口 定义拦截器可以绑定用户信息 还有其他很多 细细品 对比看比
  • IDEA maven报错To see the full stack trace of the errors, re-run Maven with the -e switch

    这里写自定义目录标题 报错内容 ERROR ERROR Some problems were encountered while processing the POMs FATAL Non resolvable parent POM for
  • managed unmanaged

    Enable function level control for compiling functions as managed or unmanaged pragma managed pragma unmanaged pragma man
  • 生日快乐,Bill

    12月15日 是Bill的生日 于是我用Easyx为他做了个生日礼物比较潦草 当然中间的 initgraph 680 420 EW NOCLOSE EW DBLCLKS EW NOMINIMIZE setbkmode TRANSPARENT
  • Springboot的拦截器功能实现:

    1 写拦截器类 代码如下 package com demo config import org springframework web servlet HandlerInterceptor import javax servlet http
  • CSDN 重新开放付费资源的上传了,但要求如下

    csdn上半年关闭了付费资源的上传功能 经过几个月的优化处理 现在又重新 开放了 优化后的上传功能 必须达到一定的级别才能实现 所以并不是向以前一样 都可以上传 虽然网站上已经公布了具体的要求说明 在这里我整理了一下给大家 也就是说 想上传
  • 设置elementUi无限滚动加载时的一些注意点

    1 overflow属性是一定要有的 可以加到父节点或者自身上 否则会报错 2 容器一定要被撑开并触底 这样才会触发v infinite scroll上绑定的方法 3 设置height calc 100vh 72px 72是header的高
  • [开发

    Java中比较两个对象的指定属性的值是否相等 可以使用Apache Commons Lang库中的EqualsBuilder类 EqualsBuilder提供了一种便捷的方法来比较两个对象的属性值是否相等 具体步骤如下 通过构造器创建一个E
  • 存储过程之用返回多条数据一

    要求 拼接数据 作为多条数据返回 1 创建类型 create or replace type bb ptyxztqk Type as object d index number d name varchar2 100 d this numb
  • 【全志A33】解决文件系统错误

    这个平板第一次开机就给我了一个惊喜 文件系统不可写 WTF 这还玩啥 但是查了一下内核日志 发现这事不简单 内核日志 1 690765 EXT4 fs nandd barriers disabled 1 698331 EXT4 fs nan
  • IMU+摄像头实现无标记运动捕捉

    惯性传感和计算机视觉的进步为在临床和自然环境中获得精准数据带来了新可能 然而在临床应用时需要仔细地将传感器与身体对齐 这减慢了数据收集过程 随着无标记运动捕捉的发展 研究者们提出了一个新的深度学习模型 利用来自视觉 惯性传感器及其噪声数据来
  • Java中printIn如果不能用是怎么回事?

    在Java中 使用 System out printIn 会报错 例 Try java public class Try public static void main String args System out printIn sd 结
  • C语言第十四课-------结构体的认识和使用-------重要一笔

    作者前言 作者介绍 作者id 老秦包你会 简单介绍 喜欢学习C语言和python等编程语言 是一位爱分享的博主 有兴趣的小可爱可以来互讨 个人主页 小小页面 gitee页面 秦大大
  • openwrt 常用命令

    1 进入目录 cd etc twrt 2 打开文件 vi etc twrt config 3 退出文件 ESC gt shift gt wq 3 杀死进程 kill all twrt 转载于 https www cnblogs com pp
  • Docker(一):Docker入门教程

    如今Docker的使用已经非常普遍 特别在一线互联网公司 使用Docker技术可以帮助企业快速水平扩展服务 从而到达弹性部署业务的能力 在云服务概念兴起之后 Docker的使用场景和范围进一步发展 如今在微服务架构越来越流行的情况下 微服务
  • 人工神经网络概念及组成,人工神经网络发展史

    BP神经网络的发展历史 人工神经网络早期的研究工作应追溯至上世纪40年代 下面以时间顺序 以著名的人物或某一方面突出的研究成果为线索 简要介绍人工神经网络的发展历史 1943年 心理学家W Mcculloch和数理逻辑学家W Pitts在分
  • 安装 TensorFlow GPU版(2023年)

    1 安装CUDA与cuDNN 1 1 确定所需的CUDA与cuDNN版本 查看所需的CUDA与cuDNN的版本网址 右上角语言那选English 中文的内容不全 Build from source on Windows TensorFlow
  • 基于单片机的语音风扇的设计与实现

    写在前面 因为偶尔会有人问 所以对之前做的这个小玩意进行一个小小的总结 把资料也放在这里来吧 作品展示 https www bilibili com video BV1iV411C722 spm id from 333 999 0 0 vd
  • xdp测试例子

    牛刀小试 linux内核协议栈实现了一个虚拟机 允许用户程序向内核注入二进制字节码 注入的程序 就可以做一些有趣的事情 比如 负载均衡 数据包检测 加速容器网络转发 做个网损仪 本篇 运行一个测试程序 丢弃网卡收到的icmp包 xdp dr