[转帖]使用 BPF 统计网络流量

使用,bpf,统计,网络流量 · 浏览次数 : 0

小编点评

**文章摘要:** 本文介绍使用 BPF 统计网络流量的步骤,并展示了一个使用 libbpf 编写的示例程序。 **主要内容:** 1. **BPF 的设计和功能:** - BPF 是一个可以处理高数据速率的网络包处理技术。 - 它使用 libpcap 在内核态捕获网络流量并将其存储在内核缓冲区中。 2. **使用 libpcap 实现 BPF:** - 库提供了函数来查找网络接口、获取 IP 地址和端口号等信息。 - 用户可以编写 BPF 程序,使用 libpcap 捕获网络流量并将其存储在内核缓冲区中。 3. **优化效率:** - BPF 程序使用一些优化技巧,例如编译器优化和过滤表达式优化。 - 这有助于减少流量复制的成本和提高效率。 4. **示例程序:** - 该程序展示了使用 libbpf 编写的 BPF 程序的步骤。 - 它通过判断源地址是否与目标地址相等来检测网络流量。 5. **结果和讨论:** - 程序可以正确统计网络流量,并将其打印到终端上。 - 讨论如何将该程序集成到内核态中。 **结论:** 本文展示了使用 BPF 统计网络流量的步骤和方法,并提供了一个使用 libbpf 编写的示例程序。该方法可以非常有效地统计网络流量,而无需使用外部工具,在内核态中实现。

正文

https://cloud.tencent.com/developer/article/1804036?areaSource=103001.17&traceId=rX8kmZPurwFtXqEtY-bY-

 

本文介绍使用 BPF 统计网络流量。网络流量是云产品的重要计费指标,服务器每秒可以处理上百万的数据包,这也要求有高效的方法来统计流量,而 BPF 最初作为网络包处理的技术,被设计和构造成可以支持这个速率的流量处理。

使用 libpcap

BPF 之前的时代,我们可以使用 libpcap 实现与 tcpdump 类似的方式,捕获网络流量并拷贝到用户程序中进行统计。

下面是一个简单的示例:

#define ETHERNET_HEADER_LEN 14
#define MIN_IP_HEADER_LEN 20
#define IP_HL(ip) (((ip)->ihl) & 0x0f)

const char* dev = "br-7e20abc6df31";
const char* filter_expr = "src net 172.20.0.0/16";

int64_t traffic = 0;

// Reference:
//      https://www.tcpdump.org/manpages/
//      https://tools.ietf.org/html/rfc791
void traffic_stat() {
    // find the IPv4 network number and netmask for a device
    bpf_u_int32 net = 0;
    bpf_u_int32 mask = 0;
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
    int ret = pcap_lookupnet(dev, &net, &mask, errbuf);
    if (ret == PCAP_ERROR) {
        fprintf(stderr, "pcap_lookupnet failed: %s\n", errbuf);
        exit(EXIT_FAILURE);
    }

    // open a device for capturing
    int promisc = 1;
    int timeout = 1000;   // in milliseconds
    const int SNAP_LEN = 64;
    auto handle = pcap_open_live(dev, SNAP_LEN, promisc, timeout, errbuf);
    if (!handle) {
        fprintf(stderr, "pcap_open_live failed: %s\n", errbuf);
        exit(EXIT_FAILURE);
    }

    // compile a filter expression
    struct bpf_program fp;
    int optimize = 1;
    ret = pcap_compile(handle, &fp, filter_expr, optimize, net);
    if (ret == PCAP_ERROR) {
        fprintf(stderr, "pcap_compile failed: %s\n", pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }

    // set the filter
    ret = pcap_setfilter(handle, &fp);
    if (ret == PCAP_ERROR) {
        fprintf(stderr, "pcap_setfilter failed: %s\n", pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }

    // process packets from a live capture
    int packet_count = -1;  // -1 means infinity
    pcap_loop(handle, packet_count, [](u_char* args, const struct pcap_pkthdr* header, const u_char* bytes) {

        auto ip_header = reinterpret_cast<iphdr*>(const_cast<u_char*>(bytes) + ETHERNET_HEADER_LEN);
        const int ip_header_len = IP_HL(ip_header) * 4;
        if (ip_header_len < MIN_IP_HEADER_LEN) {
            return;
        }

        auto len = ntohs(ip_header->tot_len);
        if (len <= 0) {
            return;
        }

        auto traffic = reinterpret_cast<int64_t*>(args);
        *traffic += len;

    }, reinterpret_cast<u_char*>(&traffic));

    // free a BPF program
    pcap_freecode(&fp);
    // close the capture device
    pcap_close(handle);
}

br-7e20abc6df31 是使用 Docker 创建的网桥,IP 是 172.20.0.1,使用 src net 172.20.0.0/16 表达式过滤出站流量。

这个程序可以做正确的事情,即统计流量。问题在于,它需要把所有流经网卡的流量都拷贝到用户程序,然后在进行统计,而这些拷贝随后就被丢弃,浪费了大量系统资源。

大杀器 BPF

BPF 显然是这个问题的完美解决方案。我们需要的只是累计出站流量,如果能放到内核态执行,意味着我们不需要拷贝网络数据包。

下面是一个使用 libbpf 编写的程序,它的作用与上述 libpcap 的作用一致:

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

int ifindex = 0;
__u64 traffic = 0;

SEC("tp_btf/netif_receive_skb")
int BPF_PROG(netif_receive_skb, struct sk_buff *skb)
{
    if (skb->dev->ifindex == ifindex) {
        traffic += skb->data_len;
    }
    return 0;
}

char LICENSE[] SEC("license") = "GPL";

这里,ifindex 即上述网络设备 br-7e20abc6df31 的接口索引。

在这个程序中,我们把它挂载在了 netif_receive_skb 这个 tracepoint 上,网卡接收到数据包后,我们判断是否是目的设备,若是,则累加流量。没有数据拷贝,没有上下文切换,简单高效。

实战

使用 dd 创建一个 512M 大小的文件:

$ dd if=/dev/zero of=/tmp/testfile bs=4096 count=131072

创建一个新的网络设备:

$ sudo docker network create my-tc-net

使用上面创建的网络运行一个 nginx 服务器,提供文件下载:

$ sudo docker run -d --rm \
        -p 10086:80 \
        -v /tmp/testfile:/home/data/testfile \
        -v $(PWD)/default.conf:/etc/nginx/conf.d/default.conf \
        --name my-nginx \
        --network my-tc-net \
        nginx:alpine

这是我们的服务器配置:

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location /downloads/ {
        alias               /home/data/;
    }
}

运行上述的流量统计程序:

$ sudo ./trafficstat

下载文件:

$ curl http://localhost:10086/downloads/testfile --output testfile

查看流量统计输出:

$ sudo ./trafficstat
...
traffic in bytes: 536878816
...

符合预期,大功告成。本文源代码可在这里找到。

结论

本文通过实例演示了使用 libbpf 编写 BPF 程序,实现在内核态高效统计网络流量的方案。

与[转帖]使用 BPF 统计网络流量相似的内容:

[转帖]使用 BPF 统计网络流量

https://cloud.tencent.com/developer/article/1804036?areaSource=103001.17&traceId=rX8kmZPurwFtXqEtY-bY- 本文介绍使用 BPF 统计网络流量。网络流量是云产品的重要计费指标,服务器每秒可以处理上百万的

[转帖]【译文】使用BPF控制内核的ops结构体

https://zhuanlan.zhihu.com/p/105814639 Linux内核5.6版本的众多令人惊喜的功能之一是:TCP拥塞控制算法(congestion control algorithm)可作为用户空间的BPF(Berkeley Packet Filter)程序进行加载和执行。

[转帖]使用bcc开发BPF程序的一点思路

https://zhuanlan.zhihu.com/p/488498453 之前的文章介绍了使用cilium工具开发BPF程序的例子。对于较新的系统内核来说,用这样较新的工具很不错,但是对于稍微旧一点的系统,如果不想直接写原生BPF程序的话,我们貌似只有一个选择,使用bcc。 一些常见的发行版的源

[转帖][译] 使用 bcc/BPF 分析 Go 程序

https://toutiao.io/posts/089ydx/preview BCC 是基于 BPF 的 Linux IO 分析、监控、网络工具集合。BPF Compiler Collection (BCC) 是创建高效内核追踪和处理程序的工具包,包含几个有用的工具和用例。BCC 扩展了 BPF

[转帖]7 个使用 bcc/BPF 的性能分析神器

https://linux.cn/article-9139-1.html 使用伯克利包过滤器Berkeley Packet Filter(BPF)编译器集合Compiler Collection(BCC)工具深度探查你的 Linux 代码。 在 Linux 中出现的一种新技术能够为系统管理员和开发者

[转帖]7 个使用 bcc/BPF 的性能分析神器

https://t.cj.sina.com.cn/articles/view/1772191555/69a17f430190029mf 在 Linux 中出现的一种新技术能够为系统管理员和开发者提供大量用于性能分析和故障排除的新工具和仪表盘。它被称为增强的伯克利数据包过滤器(eBPF,或 BPF),

[转帖]BPF 进阶笔记(五):几种 TCP 相关的 BPF(sockops、struct_ops、header options)

http://arthurchiao.art/blog/bpf-advanced-notes-5-zh/ 整理一些 TCP 相关的 BPF 内容,主要来自 Facebook 和 Google 的分享。 关于 “BPF 进阶笔记” 系列 平时学习和使用 BPF 时所整理。由于是笔记而非教程,因此内容不

[转帖]BPF CO-RE 示例代码解析

https://www.cnblogs.com/charlieroro/p/14357802.html 在BPF的可移植性和CO-RE一文的末尾提到了一个名为runqslower的工具,该工具用于展示在CPU run队列中停留的时间大于某一值的任务。现在以该工具来展示如何使用BPF CO-RE。 目

【转帖】Seccomp、BPF与容器安全

语音阅读2022-06-30 20:26 本文详细介绍了关于seccomp的相关概念,包括seccomp的发展历史、Seccomp BPF的实现原理已经与seccomp相关的一些工具等。此外,通过实例验证了如何使用seccomp bpf 来保护Docker的安全。 简介 seccomp(全称secu

[转帖]BCC-Tool 工具使用

1. 概述 BCC是一个用于跟踪内核和操作程序的工具集,其软件包中包含了一些有用的工具和例子,它扩展了BPF(Berkeley Packet Filters),通常被称为eBPF , 在Linux3.15中首次引入,但大多数BCC的功能需要Libux4.1及以上版本。 BCC使用C语言进行内核检测,