使用ebpf对Linux进行跟踪6:使用uprobes跟踪
2023-3-23 08:1:11 Author: 奶牛安全(查看原文) 阅读量:6 收藏

这篇文章是监控QEMU的网络流量分布。

QEMU网络使用的技术是virtio,在QEMU端是virtio-net, 在内核端是vhost-net。由于这里是使用uprobes来跟踪,所以,是对virtio-net来挂钩。

在这里对virtio-net的接收队列挂uprobesuretprobes,对发送队列挂uretprobes。同时对结果建立直方图。

QEMU对发送和接收的主要处理函数如下:

// RX
static ssize_t virtio_net_receive_rcu(NetClientState *nc,
                                      const uint8_t *buf,
                                      size_t size)
 
{
    ...
}
// TX
static int32_t virtio_net_flush_tx(VirtIONetQueue *q) {
    ...
}

为了检测virtio_net_receive_rcu,将启用uprobeuretprobe并创建大小为size_t的单独直方图。为了检测virtio_net_flush_tx,需启用一个uretprobe

在虚拟机里使用virtio-net, 需要QEMU以下列参数启动

-device virtio-net-pci,netdev=tapnet
-netdev tap,id=tapnet,vhost=off,ifname=tap0,script=/etc/qemu-ifup,downscript=no

由于使用直方图,就不需要使用BPF_PERF_OUTPUT

#!/usr/bin/python

from bcc import BPF
from time import sleep

bpf_txt = """
#include <uapi/linux/ptrace.h>

BPF_HISTOGRAM(rx_entry_hist);
BPF_HISTOGRAM(rx_retval_hist);
BPF_HISTOGRAM(tx_retval_hist);

int start_vnet_rec_rcu(struct pt_regs *ctx) {
    u64 size;
    size = PT_REGS_PARM3(ctx);
    rx_entry_hist.increment(bpf_log2l(size));
    return 0;
}

int end_vnet_rec_rcu(struct pt_regs *ctx) {
    int retval;
    retval = PT_REGS_RC(ctx);
    rx_retval_hist.increment(bpf_log2l(retval));
    return 0;
}

int end_vnet_tx(struct pt_regs *ctx) {
    int retval;
    retval = PT_REGS_RC(ctx);
    tx_retval_hist.increment(bpf_log2l(retval));
    return 0;
}
"""

# Load BPF program
dev_qemu_path = \
    "/mnt/repos/oracle/qemu/build/x86_64-softmmu/qemu-system-x86_64"

bpf_ctx = BPF(text=bpf_txt)
bpf_ctx.attach_uprobe(name=dev_qemu_path,
                      sym="virtio_net_receive_rcu",
                      fn_name="start_vnet_rec_rcu")
bpf_ctx.attach_uretprobe(name=dev_qemu_path,
                         sym="virtio_net_receive_rcu",
                         fn_name="end_vnet_rec_rcu")
bpf_ctx.attach_uretprobe(name=dev_qemu_path,
                         sym="virtio_net_flush_tx",
                         fn_name="end_vnet_tx")

# Print header
print("Aggregating data from virtio-net RX & TX virtqueues... "
      "Hit Ctrl-C to end.")

# Process arguments
while 1:
    try:
        sleep(1)
    except KeyboardInterrupt:
        print("\n")
        break

# Output
print("Virtio-net RX (entry) log2 packet size histogram")
print("---------------------------------------------")
bpf_ctx["rx_entry_hist"].print_log2_hist("size")
print("\n")
print("Virtio-net RX (return) log2 packet size histogram")
print("----------------------------------------------")
bpf_ctx["rx_retval_hist"].print_log2_hist("retSize")
print("\n")
print("virtio-net TX (return) log2 number of packets transmitted")
bpf_ctx["tx_retval_hist"].print_log2_hist("num packets")
print("\n")

/mnt/repos/oracle/qemu/build/x86_64-softmmu/qemu-system-x86_64请改成自己的qemu的二进制文件

打开另一个窗口来运行这个脚本,然后在虚拟机产生一些网络流量。在这个例子和下一个例子中,只是简单地 wget 一个文件来生成网络流量。片刻之后,按 Ctrl-C 脚本,您应该会看到如下内容:

Aggregating data from virtio-net RX & TX virtqueues... Hit Ctrl-C to end.
^C

Virtio-net RX (entry) log2 packet size histogram
---------------------------------------------
     size                : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 0        |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 0        |                                        |
        16 -> 31         : 0        |                                        |
        32 -> 63         : 7        |                                        |
        64 -> 127        : 17       |                                        |
       128 -> 255        : 1        |                                        |
       256 -> 511        : 1        |                                        |
       512 -> 1023       : 5        |                                        |
      1024 -> 2047       : 47906    |****                                    |
      2048 -> 4095       : 30912    |***                                     |
      4096 -> 8191       : 100188   |*********                               |
      8192 -> 16383      : 411462   |****************************************|
     16384 -> 32767      : 278      |                                        |

Virtio-net RX (return) log2 packet size histogram
----------------------------------------------
     retSize             : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 0        |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 0        |                                        |
        16 -> 31         : 0        |                                        |
        32 -> 63         : 7        |                                        |
        64 -> 127        : 17       |                                        |
       128 -> 255        : 1        |                                        |
       256 -> 511        : 1        |                                        |
       512 -> 1023       : 5        |                                        |
      1024 -> 2047       : 47906    |****                                    |
      2048 -> 4095       : 30912    |***                                     |
      4096 -> 8191       : 100188   |*********                               |
      8192 -> 16383      : 411462   |****************************************|
     16384 -> 32767      : 278      |                                        |

virtio-net TX (return) log2 number of packets transmitted
     num packets         : count     distribution
         0 -> 1          : 782827   |****************************************|
         2 -> 3          : 78190    |***                                     |
         4 -> 7          : 740      |                                        |
         8 -> 15         : 24       |                                        |
        16 -> 31         : 1        |                                        |

如果脚本运行没有任何结果,请确保虚拟机启动参数

查看此脚本的输出,发现接收队列的参数size和返回直方图与预期的一样。发送数据包的大部分操作通常一次只发送 1 个数据包。

BPF_HISTOGRAM(name):创建一个直方图表,原型BPF_HISTOGRAM(name, key_type=int, size=64)

PT_REGS_PARMn(ctx):获得函数第n个参数

hist.increment(bpf_log2l(val)):增加2为底对数的值

attach_uprobe(name='...', sym='...', fn_name='...'):对名称里name的二进制文件的符号sym进行挂uprobes,挂的钩子在fn_name指定

attach_uretprobe(name='...', sym='...', fn_name='...'):对名称里name的二进制文件的符号sym进行挂uretprobes,挂的钩子在fn_name指定

bpf_ctx["hist_name"].print_log2_hist("label"):将名为hist_nameBPF直方图表打印为ASCII格式的直方图, 第一列的名称由label指定

暗号:de340


文章来源: http://mp.weixin.qq.com/s?__biz=MzU4NjY0NTExNA==&mid=2247488568&idx=1&sn=b6c6e344f2aa84292f14da5ae573fa8e&chksm=fdf97f2dca8ef63bafa24f338858b865dd9bf6724afcda3a70abdddca4a67fd4e4e543b56ef2#rd
如有侵权请联系:admin#unsafe.sh