libbpf-bootstrap开发指南:网络包监测-tc

news2024/11/28 8:32:08

目录

前置知识

代码分析

BPF部分

功能说明

struct __sk_buff 说明

bpf_htons & bpf_ntohs

为什么有l2 + 1、l3+1

data 数据的排布

用户部分

功能说明

DECLARE_LIBBPF_OPTS

执行效果


前置知识

IP数据包的总长度指的是整个IP数据包的长度,包括IP头部和数据部分的总和,以字节为单位。IP数据包在传输过程中可能会被分片,每个分片都会有自己的IP头部和数据部分,但是所有分片的IP头部的总长度应该是相同的。

生存时间(TTL)是一个8位字段,它表示IP数据包在网络中可以被路由器转发的最大次数。当一个路由器接收到一个IP数据包时,它会将TTL减1,然后将数据包转发到下一个路由器。当TTL的值减少到0时,路由器会丢弃该数据包,并向源主机发送ICMP“生存时间超时”消息。TTL的主要作用是防止IP数据包在网络中不断循环,因为如果数据包被无限转发,它可能会占用网络资源并导致网络拥塞。通过限制数据包的生存时间,TTL可以确保数据包在网络中得到及时处理并在合理的时间内达到目的地。

代码分析

BPF部分

// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2022 Hengqi Chen */
#include <vmlinux.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#define TC_ACT_OK 0
#define ETH_P_IP  0x0800 /* Internet Protocol packet	*/
#define IPPROTO_TCP 6

SEC("tc")
int tc_ingress(struct __sk_buff *ctx)
{
	void *data_end = (void *)(__u64)ctx->data_end;
	void *data = (void *)(__u64)ctx->data;
	struct ethhdr *l2;
	struct iphdr *l3;
	struct tcphdr *l4;

	if (ctx->protocol != bpf_htons(ETH_P_IP))
		return TC_ACT_OK;

	l2 = data;
	if ((void *)(l2 + 1) > data_end)
		return TC_ACT_OK;

	l3 = (struct iphdr *)(l2 + 1);
	if ((void *)(l3 + 1) > data_end || l3->protocol != IPPROTO_TCP)
		return TC_ACT_OK;
        
	l4 = (struct tcphdr *)(l3 + 1);
	if ((void *)(l4 + 1) > data_end)
		return TC_ACT_OK;

	bpf_printk("Got IP packet: src_ip: %u, dst_ip: %u, src_port: %u, dst_port: %u, tot_len: %d, ttl: %d", 
               bpf_ntohl(l3->saddr), bpf_ntohl(l3->daddr), bpf_ntohs(l4->source), bpf_ntohs(l4->dest), bpf_ntohs(l3->tot_len), l3->ttl);

	return TC_ACT_OK;
}

char __license[] SEC("license") = "GPL";
功能说明

这是一个 Traffic Control (TC) 类型的 BPF 程序,用于在数据包进入或离开网络设备时进行处理。

这里只需要TCP的包,并且会读取tcp包的目标IP、本地IP、目标端口和本地端口,最后会打印这个包的长度以及存活时间(路由器跳转最大上限)

struct __sk_buff 说明

struct __sk_buff 是 Linux 内核网络开发中非常重要的数据结构,它代表了一个网络数据包的缓冲区。这个结构体定义在文件 include/uapi/linux/bpf.h 中,是 eBPF 开发中常用的数据类型之一。

__sk_buff 结构体包含了一个网络数据包的所有信息,包括数据包的协议头部、数据部分、网络接口、传输控制块等等。在 eBPF 开发中,通过操作这个结构体,可以实现对网络数据包的过滤、转发、修改等操作。

以下是 struct __sk_buff 结构体的一些重要成员:

  • data:指向数据包的数据部分的指针
  • data_end:指向数据部分的结尾的指针
  • protocol:数据包的协议类型,例如 ETH_P_IP 表示 IPv4 协议,ETH_P_IPV6 表示 IPv6 协议等
  • len:数据包的总长度,包括协议头部和数据部分
  • ifindex:接收数据包的网络接口的索引
  • cb[]:一个大小为 48 字节的预留空间,可以用来在 eBPF 程序中保存一些自定义的信息。

在x86 的体系中 该结构的详细信息如下:

bpf_htons & bpf_ntohs

bpf_htons 是一个 BPF 函数,用于将一个 16 位的整数从主机字节序(即当前机器的字节序)转换为网络字节序(大端字节序)。在网络通信中,各个计算机使用的字节序可能不同,因此需要统一使用网络字节序以确保数据的正确传输。

为什么有l2 + 1、l3+1

l2 + 1不是在对l2的值加一,而是在移动指针。由于l2是一个struct ethhdr类型的指针,通过l2 + 1我们实际上获取了紧随以太网头部之后的位置,也就是下一个协议头部的起始位置。

在C语言中,当你对一个指针加一个整数时,增加的实际字节数等于这个整数乘以这个指针指向的数据类型的大小。因此,l2 + 1实际上指向了l2后面的内存,这个位置恰好是以太网头部之后的位置。

现在,(void *)(l2 + 1) > data_end这个检查确保我们没有超出数据包的尾部。如果没有这个检查,我们可能会读取超出数据包尾部的内存,这可能会导致内存访问错误。

其他同理

data 数据的排布

data指针在struct __sk_buff中指向网络数据包的数据部分。这些数据的具体排布取决于数据包的协议类型。对于一个典型的以太网TCP的数据包,数据的排布可能如下:

  1. 以太网头部 (struct ethhdr)
  2. IP头部 (struct iphdr 或 struct ipv6hdr)
  3. 传输层头部(如 struct tcphdr 或 struct udphdr)
  4. 数据载荷

用户部分

功能说明
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2022 Hengqi Chen */
#include <signal.h>
#include <unistd.h>
#include "tc.skel.h"

#define LO_IFINDEX 1

static volatile sig_atomic_t exiting = 0;

static void sig_int(int signo)
{
	exiting = 1;
}

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
	return vfprintf(stderr, format, args);
}

int main(int argc, char **argv)
{
	DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = LO_IFINDEX,
			    .attach_point = BPF_TC_INGRESS);
	DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
	bool hook_created = false;
	struct tc_bpf *skel;
	int err;

	libbpf_set_print(libbpf_print_fn);

	skel = tc_bpf__open_and_load();
	if (!skel) {
		fprintf(stderr, "Failed to open BPF skeleton\n");
		return 1;
	}

	/* The hook (i.e. qdisc) may already exists because:
	 *   1. it is created by other processes or users
	 *   2. or since we are attaching to the TC ingress ONLY,
	 *      bpf_tc_hook_destroy does NOT really remove the qdisc,
	 *      there may be an egress filter on the qdisc
	 */
	err = bpf_tc_hook_create(&tc_hook);
	if (!err)
		hook_created = true;
	if (err && err != -EEXIST) {
		fprintf(stderr, "Failed to create TC hook: %d\n", err);
		goto cleanup;
	}

	tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_ingress);
	err = bpf_tc_attach(&tc_hook, &tc_opts);
	if (err) {
		fprintf(stderr, "Failed to attach TC: %d\n", err);
		goto cleanup;
	}

	if (signal(SIGINT, sig_int) == SIG_ERR) {
		err = errno;
		fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno));
		goto cleanup;
	}

	printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
	       "to see output of the BPF program.\n");

	while (!exiting) {
		fprintf(stderr, ".");
		sleep(1);
	}

	tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0;
	err = bpf_tc_detach(&tc_hook, &tc_opts);
	if (err) {
		fprintf(stderr, "Failed to detach TC: %d\n", err);
		goto cleanup;
	}

cleanup:
	if (hook_created)
		bpf_tc_hook_destroy(&tc_hook);
	tc_bpf__destroy(skel);
	return -err;
}
DECLARE_LIBBPF_OPTS
  1. DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = LO_IFINDEX, .attach_point = BPF_TC_INGRESS);这个声明创建了一个bpf_tc_hook结构体的实例tc_hook。bpf_tc_hook是一个libbpf提供的结构体,用于描述一个TC(Traffic Control)挂钩点。在这个结构体中,.ifindex字段是网络接口的索引号,这里被设置为LO_IFINDEX,代表本地环回设备(loopback device)的接口索引号,通常为1。.attach_point字段是挂钩点的类型,这里被设置为BPF_TC_INGRESS,代表这个挂钩点是一个入口点。
  2. DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);这个声明创建了一个bpf_tc_opts结构体的实例tc_opts。bpf_tc_opts是一个libbpf提供的结构体,用于描述一个TC挂钩点的选项。在这个结构体中,.handle字段是这个挂钩点的句柄,这里被设置为1;.priority字段是这个挂钩点的优先级,这里也被设置为1。

执行效果

 Chrome_ChildIOT-6868    [019] d.s3. 93109.843628: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52762, dst_port: 37351, tot_len: 463, ttl: 64
     clash-linux-8128    [018] d.s3. 93109.843773: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 52762, tot_len: 368, ttl: 64
     clash-linux-8128    [018] d.s3. 93109.843780: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52762, dst_port: 37351, tot_len: 52, ttl: 64
     clash-linux-8128    [018] d.s3. 93110.380165: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 57856, tot_len: 72, ttl: 64
     clash-linux-8128    [018] d.s3. 93110.380178: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 57856, dst_port: 37351, tot_len: 52, ttl: 64
     clash-linux-31621   [017] d.s3. 93111.374526: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 57856, tot_len: 72, ttl: 64
     clash-linux-31621   [017] d.s3. 93111.374539: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 57856, dst_port: 37351, tot_len: 52, ttl: 64
     clash-linux-8128    [018] d.s3. 93112.368961: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 57856, tot_len: 72, ttl: 64
     clash-linux-8128    [018] d.s3. 93112.368974: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 57856, dst_port: 37351, tot_len: 52, ttl: 64
 Chrome_ChildIOT-6868    [019] d.s3. 93112.826725: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52762, dst_port: 37351, tot_len: 463, ttl: 64
     clash-linux-31621   [017] d.s3. 93112.826876: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 52762, tot_len: 368, ttl: 64
     clash-linux-31621   [017] d.s3. 93112.826883: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52762, dst_port: 37351, tot_len: 52, ttl: 64
 Chrome_ChildIOT-6868    [019] d.s3. 93113.079085: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52762, dst_port: 37351, tot_len: 463, ttl: 64
 Chrome_ChildIOT-6868    [019] d.s3. 93113.079128: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 56738, dst_port: 37351, tot_len: 463, ttl: 64
 Chrome_ChildIOT-6868    [019] d.s3. 93113.079161: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52778, dst_port: 37351, tot_len: 473, ttl: 64
     clash-linux-6910    [012] d.s3. 93113.079181: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 52762, tot_len: 368, ttl: 64
     clash-linux-6910    [012] d.s3. 93113.079191: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52762, dst_port: 37351, tot_len: 52, ttl: 64
     clash-linux-31621   [017] d.s3. 93113.079587: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 56738, tot_len: 4148, ttl: 64
     clash-linux-31621   [017] d.s3. 93113.079593: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 56738, dst_port: 37351, tot_len: 52, ttl: 64
     clash-linux-31621   [017] d.s3. 93113.079600: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 56738, tot_len: 9728, ttl: 64
     clash-linux-31621   [017] d.s3. 93113.079605: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 56738, tot_len: 59, ttl: 64
     clash-linux-6910    [012] d.s3. 93113.079844: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 52778, tot_len: 4148, ttl: 64
     clash-linux-6910    [012] d.s3. 93113.079851: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52778, dst_port: 37351, tot_len: 52, ttl: 64
     clash-linux-6910    [012] d.s3. 93113.079858: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 52778, tot_len: 32248, ttl: 64
     clash-linux-6910    [012] d.s3. 93113.079862: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 37351, dst_port: 52778, tot_len: 59, ttl: 64
 Chrome_ChildIOT-6868    [019] d.s3. 93113.081719: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 56738, dst_port: 37351, tot_len: 52, ttl: 64
 Chrome_ChildIOT-6868    [019] d.s3. 93113.081748: bpf_trace_printk: Got IP packet: src_ip: 2130706433, dst_ip: 2130706433, src_port: 52778, dst_port: 37351, tot_len: 52, ttl: 64

 

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

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

相关文章

React(2)

题外话&#xff1a;vscode有个插件可以很方便的快速写代码 输入rcc回车 1.组件嵌套 import React, { Component } from reactclass Navbar extends Component{render(){return <div>Navbar</div>} }const Swiper()>{return <div>Swiper</div> }cons…

学习babylon.js --- [2] 项目工程搭建

本文讲述如何搭建babylonjs的项目工程。 一 准备 首先创建一个目录叫MyProject&#xff0c;然后在这个目录里再创建三个目录&#xff1a;dist&#xff0c;public和src&#xff0c;如下&#xff0c; 接着在src目录里添加一个文件叫app.ts&#xff0c;本文使用typescript&#…

论文笔记--PTR: Prompt Tuning with Rules for Text Classification

论文笔记--PTR: Prompt Tuning with Rules for Text Classification 1. 文章简介2. 文章概括3 文章重点技术3.1 Pre-training & Fine-tuning & Prompt-based Fine Tuning3.2 PTR(Prompt Tuning with Rules)3.3 task decomposition3.4 Sub-prompts composition3.5 多个l…

平衡二叉搜索树--AVL详解剖析

目录 一、什么是AVL树 二、AVL树的作用 三、树节点的定义 四、节点的插入 五、旋转 1.左单旋 2.右单旋 左右双旋代码 &#xff1a; 4.右左双旋 一、什么是AVL树 AVL树就是二叉搜索树的进一步的优化&#xff0c;二叉搜索树虽可以缩短查找的效率&#xff0c;但是当数据有…

SDN系统方法 | 1. 概述

随着互联网和数据中心流量的爆炸式增长&#xff0c;SDN已经逐步取代静态路由交换设备成为构建网络的主流方式&#xff0c;本系列是免费电子书《Software-Defined Networks: A Systems Approach》的中文版&#xff0c;完整介绍了SDN的概念、原理、架构和实现方式。原文: Softwar…

SpringFactoriesLoader解析

一、SpringFactoriesLoader 介绍 1.1 SpringFactoriesLoader 简介 SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个约定俗成的加载方式&#xff0c;与 java spi 类似&#xff0c;只需要在模块的 META-INF/spring.factories 文件中&#xff0c;以 Properties 类型…

DOT slam论文翻译

DOT:视觉SLAM的动态目标跟踪 摘要 - 在本文中&#xff0c;我们提出了DOT(动态目标跟踪)&#xff0c;这是一个添加到现有SLAM系统中的前端&#xff0c;可以显着提高其在高动态环境中的鲁棒性和准确性。DOT结合实例分割和多视图几何来生成动态对象的掩模&#xff0c;以允许基于刚…

实现 Rollup 插件alias 并使用单元测试提高开发效率

本篇文章是对 实现 Rollup 插件 alias | 使用 TypeScript 实现库的基本流程 | 使用单元测试提高开发效率 的总结。其中涉及到开发一个组件库的诸多知识点。 实现一个经常用的 rollup 插件 alias 首先执行npm init命令初始化一个package.json文件&#xff0c;因为插件使用了ty…

DevOps系列文章之Argo CD 使用

一、什么是 argo cd Argo CD 是用于 Kubernetes 的声明性 GitOps 连续交付工具。 二、为什么使用 argo cd Argo CD 可在指定的目标环境中自动部署所需的应用程序状态&#xff0c;应用程序部署可以在 Git 提交时跟踪对分支&#xff0c;标签的更新&#xff0c;或固定到清单的特…

测试开发之路 ---- 可读性,可维护性,可扩展性

目录 前言 测试框架与测试脚本的目标&#xff08;部分&#xff09; 分层 使用类似 xml 这种可扩展性强的语义存储数据 代码复用&#xff1a;抽象一切可抽象的&#xff0c;减少一切可能的代码相似与重复 活用 java 注解和反射&#xff08;python 中应该也有相关的机制&…

如何从视频中提取音频?分享三个免费的方法给大家!

在数字时代&#xff0c;视频和音频的使用越来越广泛。有时&#xff0c;您可能希望从视频中提取音频&#xff0c;以便单独使用或与他人分享。无需购买昂贵的软件或具备专业技能&#xff0c;下面将介绍三种免费的方法&#xff0c;帮助您从视频中提取音频。这些方法简单易行&#…

Unity学习笔记--siki学院保卫萝卜

生命周期&#xff1a; 在同一个脚本中的执行先后顺序&#xff1a;先左后右 Inspector 赋值 > 外部调用 > Awake > OnEnable > Start 脚本对象的失活与激活不作用于Awake方法&#xff0c;当方法中只有Awake方法时&#xff0c;控制脚本激活失活的对勾会消失掉 当…

vue3 中ref的函数用法

简介 这里说的ref不是响应式ref,是用在组件身上的ref标识&#xff0c;一般都是ref“某一个字符串”&#xff0c;本文介绍第二种用法&#xff0c;ref“()>{}”,对没错&#xff0c;ref可以等于一个回调函数 ref可以是一个回调 <el-input:ref"(vc: any) > (inputAr…

lwip-2.1.3自带的httpd网页服务器使用教程(三)使用CGI获取URL参数(GET类型表单)

上一篇&#xff1a;lwip-2.1.3自带的httpd网页服务器使用教程&#xff08;二&#xff09;使用SSI动态生成网页部分内容 认识URL参数 在上网的时候&#xff0c;我们经常会见到在网址后面带有?AB&CD这样的语法格式。例如&#xff1a;https://blog.csdn.net/ZLK1214/articl…

OpenCV的HSV颜色空间在无人车中颜色识别的应用

RGB属于三基色空间&#xff0c;是大家最为熟悉的&#xff0c;看到的任何一种颜色都可以由三基色进行混合而成。然而一般对颜色空间的图像进行有效处理都是在HSV空间进行的&#xff0c;HSV(色调Hue,饱和度Saturation,亮度Value)是根据颜色的直观特性创建的一种颜色空间, 也称六角…

如何撤销git上一次的commit(或已push)

如何撤销git上一次的commit&#xff08;或已push&#xff09; 当多人开发时&#xff0c;我们本地commit后&#xff0c;刚要push&#xff0c;发现忘记pull最新代码&#xff0c;此时会有冲突push失败&#xff0c; 我们想要撤销最近的一次commit 我们先简单介绍一下git git有三大…

GreenPlum数据库日常维护

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

使用 YOLOv8 和 Streamlit 构建实时对象检测和跟踪应用程序:第3部分:添加跟踪算法

介绍 对象跟踪是随着时间的推移识别一系列帧中的特定对象或多个对象的过程。它涉及定位对象在每个帧中的位置并跟踪其跨帧的移动。对象跟踪在各个领域都有广泛的应用,包括监控、机器人、自动驾驶、运动分析等。 跟踪算法使用各种技术(例如颜色直方图、运动分析、深度学习等)…

【多线程】(二)线程安全问题与线程同步

文章目录 一、多线程带来的风险1.1 观察线程不安全1.2 线程安全概念1.3 线程不安全的原因1.4 线程安全的解决方法 二、synchronized关键字2.1 synchronized 的特性2.2 synchronized 使用示例2.3 Java 标准库中的线程安全类 三、volatile关键字3.1 保证内存可见性3.2 禁止指令重…

Java反射的应用:动态代理

代理设计模式的原理 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。 对于静态代理&#xff0c;特征是代理类和目标对象的类都是在编译期间确定下来&#xff0c;不利于程…