专栏首页Serverless+使用 BPF 统计网络流量
原创

使用 BPF 统计网络流量

本文介绍使用 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 程序,实现在内核态高效统计网络流量的方案。

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • 基于/proc统计网络流量的脚本

    Ethname=`cat /proc/net/dev|grep $EthXname|awk -F"[: ]+" '{ printf("%s", $2); }'`

    一见
  • Linux进程网络流量统计的实现过程

    linux都有相应开源工具实时采集网络连接、进程等信息其中网络连接一般包括最基本的五元组信息(源地址、目标地址、源端口、目标端口、协议号)再加上所属进程信息pi...

    砸漏
  • 使用 BPF 自定义安全组

    上一篇文章阅读量比较多,看起来网络的主题比较受欢迎。这一篇文章我们继续探索 BPF 在网络领域的应用:使用 BPF 来实现安全组。

    ritchiechen
  • [译]使用iptables控制网络流量

    iptables是一个允许用户配置特定规则的应用程序,这些规则由将由内核netfilter框架强制执行。它充当数据包过滤器和防火墙,可根据端口,协议和其他标准检...

    杜逸先
  • 使用Cilium增强Istio|通过Socket感知BPF程序

    8月1日凌晨,Istio 1.0发布,已生产就绪! Cilium社区感谢所有Istio贡献者为此付出的巨大努力。我们很幸运能够参与社区活动,为Istio做出贡献...

    CNCF
  • 使用 BPF 记录 TCP 重传和丢包记录

    在云函数的日常运营中,经常有用户提出要求协助排查网络问题。一般的手段就是使用 tcpdump 抓包,但是部署抓包往往是在问题发生之后,而且抓包后复现的时机也不确...

    ritchiechen
  • 使用hive查询把访问网络流量会话化

    《Hive编程指南》最后一章的Outbrain案例中,有一个把访问网络流量会话化的简单实现,但按照它的查询出来的结果是错的,于是自己重写了一个。 一、问题提...

    用户1148526
  • 使用 BPF 改变运行中的程序的函数参数

    注意到我们使用 //go:noinline 修饰了 main.greet 函数,防止被编译器内联,方便进行测试验证。

    ritchiechen
  • 使用Chameleon蜜罐监控网络流量和用户凭证

    Chameleon是一个支持自定义开发的蜜罐系统,可以帮助广大研究人员监控网络流量、Bots活动和用户密码凭证,支持的协议和组件有DNS、HTP Proxy、H...

    FB客服
  • 在Ubuntu 16.04

    BCC(BPF编译器集合 )是用于创建足智多谋内核跟踪和操作程序一套功能强大的适当的工具和示例文件。 它采用扩展BPF( Berkeley包过滤器 ),最初被称...

    233333
  • iftop实时网络流量监控工具的安装使用

    一、什么是iftop? iftop是Linux系统一个免费的网卡实时流量监控工具,类似于top命令。iftop可以监控指定网卡的实时流量、端口连接信息、反向解析...

    行 者
  • 如何使用BPF将SSH会话转换为结构化事件

    Teleport 4.2引入了一个名叫增强型会话记录(Enhanced Session Recording)的新功能,该功能可以接收一个非结构化的SSH会话,并...

    FB客服
  • 使用 BPF 进行 HTTPS 抓包 —— 以 Golang 和 cURL 为例

    分析某些业务进程的 HTTPS 请求时,类似 tcpdump 和 Fiddler 等工具无法获得请求明文,不方便进行分析。本文探讨使用 BPF 解决 HTTPS...

    啦啦啦
  • 使用 BPF 进行 HTTPS 抓包 —— 以 Golang 和 cURL 为例

    分析某些业务进程的 HTTPS 请求时,类似 tcpdump 和 Fiddler 等工具无法获得请求明文,不方便进行分析。本文探讨使用 BPF 解决 HTTPS...

    ritchiechen
  • linux下使用tc控制和模拟网络流量

    netem 与 tc: netem 是 Linux 2.6 及以上内核版本提供的一个网络模拟功能模块。该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网...

    sunsky
  • 一站式查看网络流量的完整解决方案

    互联网服务提供商(ISP)面临着用户增长带来的极端带宽使用的问题。虽然这对商业来说是件好事,但挑战在于确保带宽的有效利用。很多时候,客户会抱怨“网络很慢”,实际...

    虹科网络可视化与安全
  • 使用Caronte在CTF比赛中完成网络流量分析

    Caronte是一款功能强大的网络流量分析工具,可以帮助广大研究人员在CTF比赛或其他网络攻击/防御活动中对捕捉到的网络流量进行分析。该工具能够重新组装pcap...

    FB客服
  • eBPF文章翻译(1)—eBPF介绍

    Brendan Gregg,他在2017年的linux.conf.au大会上的演讲提到「内核虚拟机eBPF」,表示,”超能力终于来到了Linux操作系统“。让e...

    nevermosby

扫码关注腾讯云开发者

领取腾讯云代金券