WEBKT

告别 tcpdump:用 eBPF 高效进行网络包监控和协议分析

36 0 0 0

告别 tcpdump:用 eBPF 高效进行网络包监控和协议分析

为什么选择 eBPF?

eBPF 在网络包监控中的应用场景

实战:使用 eBPF 监控 HTTP 请求

总结

扩展学习

更多 eBPF 的应用场景

编写更复杂的 eBPF 程序

调试 eBPF 程序

告别 tcpdump:用 eBPF 高效进行网络包监控和协议分析

作为一名网络工程师,你是不是经常需要抓包分析网络问题?是不是还在用着古老的 tcpdump?不得不说,tcpdump 确实经典,但面对日益复杂的网络环境,它的局限性也越来越明显,例如性能瓶颈、过滤规则不够灵活等等。今天,就来聊聊如何使用 eBPF(extended Berkeley Packet Filter)这个强大的工具,告别 tcpdump,实现更高效、更灵活的网络包监控和协议分析。

为什么选择 eBPF?

先来简单了解下 eBPF。它最初是 Linux 内核中的一个包过滤技术,后来被扩展到可以运行用户自定义的程序,而这些程序可以安全、高效地在内核中运行。这就意味着,我们可以利用 eBPF 在内核中对网络包进行过滤、修改、监控等操作,而无需将数据包复制到用户空间,大大提高了性能。

相比 tcpdump,eBPF 的优势在于:

  • 高性能: eBPF 程序直接在内核中运行,避免了用户空间和内核空间的数据拷贝,减少了上下文切换,性能更高。
  • 灵活性: 可以编写自定义的 eBPF 程序,实现各种复杂的过滤和分析逻辑,比 tcpdump 的 BPF 语法更加强大。
  • 安全性: eBPF 程序在运行前会经过内核的验证器(verifier)检查,确保程序的安全性,防止程序崩溃或恶意操作。
  • 可编程性: eBPF 提供了丰富的 API 和工具链,方便开发者编写和调试 eBPF 程序。

eBPF 在网络包监控中的应用场景

eBPF 在网络包监控领域有着广泛的应用,例如:

  • 流量监控: 统计特定协议、端口或 IP 地址的流量,用于网络性能分析和故障排除。
  • 安全监控: 检测恶意流量,例如 SYN Flood 攻击、DDoS 攻击等,用于网络安全防护。
  • 协议分析: 解析网络协议,例如 HTTP、DNS、TCP 等,用于协议分析和性能优化。
  • 性能分析: 跟踪网络延迟、丢包率等指标,用于网络性能分析和优化。

实战:使用 eBPF 监控 HTTP 请求

接下来,通过一个简单的例子,演示如何使用 eBPF 监控 HTTP 请求。这个例子会抓取包含特定 URL 的 HTTP 请求,并打印到控制台。

1. 准备工作

  • 安装 bcc 工具: bcc (BPF Compiler Collection) 是一个用于创建 eBPF 程序的工具集,提供了 Python 封装,方便我们编写和调试 eBPF 程序。

    # Debian/Ubuntu
    sudo apt-get install bpfcc-tools linux-headers-$(uname -r)
    # CentOS/Fedora
    sudo yum install bpfcc-tools kernel-devel-$(uname -r)
  • 安装 libpcap-dev: 用于解析网络包的头部。

    sudo apt-get install libpcap-dev
    

2. 编写 eBPF 程序 (http_monitor.py)

#!/usr/bin/env python
from bcc import BPF
import socket
import struct
# eBPF 程序
program = '''
#include <uapi/linux/ptrace.h>
#include <net/sock.h>
#include <net/inet_sock.h>
#include <linux/tcp.h>
#include <linux/ip.h>
// 定义 HTTP 请求的起始字符串
#define HTTP_REQUEST "GET /"
// 定义存储数据的 BPF 环形缓冲区
BPF_PERF_OUTPUT(http_events);
// 定义数据结构,用于传递数据到用户空间
struct http_data_t {
u32 pid;
u32 uid;
char comm[16];
char saddr[INET_ADDRSTRLEN];
char daddr[INET_ADDRSTRLEN];
u16 sport;
u16 dport;
char url[64];
};
// kprobe 函数,在 tcp_sendmsg 函数被调用时执行
int kprobe__tcp_sendmsg(struct pt_regs *ctx, struct sock *sk, struct msghdr *msg, size_t size)
{
// 获取 socket 信息
struct inet_sock *inet = (struct inet_sock *)sk;
u32 saddr = inet->inet_saddr;
u32 daddr = inet->inet_daddr;
u16 sport = inet->inet_sport;
u16 dport = sk->sk_dport;
// 获取进程信息
u32 pid = bpf_get_current_pid_tgid() >> 32;
u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
// 读取 HTTP 请求内容
char *data = (char *)PT_REGS_PARM2(ctx);
char buf[64];
bpf_probe_read_user(buf, sizeof(buf), data);
// 检查是否为 HTTP 请求
if (memcmp(buf, HTTP_REQUEST, sizeof(HTTP_REQUEST) - 1) == 0) {
struct http_data_t http_data = {};
// 填充数据结构
http_data.pid = pid;
http_data.uid = uid;
strcpy(http_data.comm, comm);
inet_ntop(AF_INET, &saddr, http_data.saddr, sizeof(http_data.saddr));
inet_ntop(AF_INET, &daddr, http_data.daddr, sizeof(http_data.daddr));
http_data.sport = ntohs(sport);
http_data.dport = ntohs(dport);
strcpy(http_data.url, buf);
// 将数据发送到用户空间
http_events.perf_submit(ctx, &http_data, sizeof(http_data));
}
return 0;
}
'''
# 加载 eBPF 程序
bpf = BPF(text=program)
# 定义回调函数,用于处理从内核空间传递过来的数据
def print_http_event(cpu, data, size):
event = bpf["http_events"].event(data)
print("%-6d %-12s %-16s %-16s %-5d %-5d %s" % (event.pid, event.comm, event.saddr, event.daddr, event.sport, event.dport, event.url))
# 打印头部信息
print("PID COMMAND SADDR DADDR SPORT DPORT URL")
# 绑定回调函数到环形缓冲区
bpf["http_events"].open_perf_buffer(print_http_event)
# 循环读取数据
while True:
try:
bpf.perf_buffer_poll()
except KeyboardInterrupt:
exit()

代码解析:

  • eBPF 程序:
    • #include 引入必要的头文件,例如 linux/tcp.hlinux/ip.h 等,用于访问网络协议相关的数据结构。
    • HTTP_REQUEST 定义 HTTP 请求的起始字符串,用于判断是否为 HTTP 请求。
    • BPF_PERF_OUTPUT(http_events) 定义一个 BPF 环形缓冲区,用于将数据从内核空间传递到用户空间。
    • http_data_t 定义数据结构,用于存储 HTTP 请求的相关信息,例如 PID、UID、进程名、源 IP 地址、目的 IP 地址、源端口、目的端口和 URL。
    • kprobe__tcp_sendmsg 是一个 kprobe 函数,它会在 tcp_sendmsg 函数被调用时执行。tcp_sendmsg 函数是 TCP 发送数据包的函数,我们通过 hook 这个函数来抓取 HTTP 请求。
    • kprobe__tcp_sendmsg 函数中,我们首先获取 socket 信息,包括源 IP 地址、目的 IP 地址、源端口和目的端口。然后,获取进程信息,包括 PID、UID 和进程名。接着,读取 HTTP 请求内容,并检查是否为 HTTP 请求。如果是 HTTP 请求,则将数据填充到 http_data_t 结构体中,并通过 http_events.perf_submit 函数将数据发送到用户空间。
  • Python 脚本:
    • BPF(text=program) 加载 eBPF 程序。
    • print_http_event 是一个回调函数,用于处理从内核空间传递过来的数据。在这个函数中,我们解析 http_data_t 结构体中的数据,并将 HTTP 请求的信息打印到控制台。
    • bpf["http_events"].open_perf_buffer(print_http_event) 将回调函数绑定到环形缓冲区。
    • bpf.perf_buffer_poll() 循环读取数据,并调用回调函数处理数据。

3. 运行程序

sudo python http_monitor.py

运行程序后,你就可以在控制台上看到捕获到的 HTTP 请求信息了。例如:

PID COMMAND SADDR DADDR SPORT DPORT URL
1234 chrome 192.168.1.100 172.217.160.142 54321 80 GET /index.html
5678 firefox 192.168.1.100 104.27.138.123 54322 443 GET /style.css

4. 进阶:过滤特定 URL

上面的例子会捕获所有的 HTTP 请求,如果只想捕获包含特定 URL 的 HTTP 请求,可以在 eBPF 程序中添加过滤条件。例如,只想捕获包含 "/api/" 的 URL,可以修改 eBPF 程序如下:

// 检查是否为 HTTP 请求,并包含特定 URL
if (memcmp(buf, HTTP_REQUEST, sizeof(HTTP_REQUEST) - 1) == 0 && strstr(buf, "/api/") != NULL) {
...
}

总结

通过这个简单的例子,你已经了解了如何使用 eBPF 进行网络包监控。eBPF 的强大之处在于它的灵活性和高性能,可以满足各种复杂的网络监控需求。掌握 eBPF,你就可以告别 tcpdump,成为一名更高效、更专业的网络工程师。

扩展学习

更多 eBPF 的应用场景

除了网络包监控,eBPF 还可以应用于很多其他领域,例如:

  • 性能分析: 跟踪函数调用、内存分配等,用于性能分析和优化。
  • 安全: 检测恶意行为,例如文件访问、系统调用等,用于安全防护。
  • 容器: 监控容器的资源使用情况,用于容器管理和调度。
  • 服务网格: 实现服务之间的流量控制和监控,用于服务网格管理。

编写更复杂的 eBPF 程序

上面的例子只是一个简单的入门示例,实际应用中,你可能需要编写更复杂的 eBPF 程序。以下是一些编写复杂 eBPF 程序的建议:

  • 使用 BPF Maps: BPF Maps 是一种在内核空间和用户空间之间共享数据的机制。你可以使用 BPF Maps 存储和更新数据,例如统计信息、配置信息等。
  • 使用 Tail Calls: Tail Calls 允许你从一个 eBPF 程序跳转到另一个 eBPF 程序。你可以使用 Tail Calls 将复杂的逻辑分解成多个小的 eBPF 程序,提高代码的可维护性。
  • 使用 Helpers: eBPF 提供了很多 Helpers 函数,用于执行各种操作,例如访问网络协议头部、获取进程信息等。你可以使用 Helpers 函数简化代码。
  • 使用 CO-RE (Compile Once – Run Everywhere): CO-RE 是一种技术,允许你编写一次 eBPF 程序,然后在不同的内核版本上运行。你可以使用 CO-RE 提高 eBPF 程序的兼容性。

调试 eBPF 程序

调试 eBPF 程序可能比较困难,因为 eBPF 程序在内核中运行,不能像用户空间程序那样直接使用 GDB 等调试器。以下是一些调试 eBPF 程序的技巧:

  • 使用 bpf_trace_printk bpf_trace_printk 函数允许你将调试信息打印到内核日志中。你可以使用 bpf_trace_printk 函数打印变量的值、函数调用等信息。
  • 使用 bpftool bpftool 是一个用于管理和调试 eBPF 程序的工具。你可以使用 bpftool 查看 eBPF 程序的运行状态、加载和卸载 eBPF 程序等。
  • 使用 bcc 的调试工具: bcc 提供了一些调试工具,例如 traceprofile 等,可以帮助你分析 eBPF 程序的性能和行为。

希望这篇文章能够帮助你入门 eBPF,并将其应用到你的网络工作中。eBPF 的潜力是无限的,期待你能够利用它创造出更多有趣的应用!

NetBoy eBPF网络监控协议分析

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/9623