如何找到tap接口与其文件描述符之间的联系?

Mar*_*tin 5 qemu linux-kernel tap

我有一个qemu由编排脚本启动的虚拟机,该脚本创建临时tap接口。当我检查进程的命令行参数时qemu-system-x86_64,我可以看到该进程tap使用文件描述符连接到已经打开的接口27

-netdev tap,fd=27,id=hostnet1,vhost=on,vhostfd=28
Run Code Online (Sandbox Code Playgroud)

ls -l /proc/<qemu-system-x86_64_PID>/fd/27其指出/dev/net/tun

它是否以某种方式工作,将tap接口名称(例如vnet99)传递给/dev/net/tunwithioctl()并返回正确的 fd?或者一般来说,我如何找出tap主机中的哪个接口具有文件描述符 27?

A.B*_*A.B 3

可以给出答案的iff:条目是通过commit添加到内核 3.14 中的,因此在内核 3.13 或之前版本(例如 Ubuntu 14.04LTS)上不可用。 tun: add device name(iff) field to proc fdinfo entry

在这种情况下,虽然不可能要求内核提供信息,但仍然可以通过使用调试器跟踪来要求实际进程提供此信息。

自 Linux 2.6.27 起,有一个 ioctl 可用,用于询问有关配置的tuntap 接口的信息:TUNGETIFF它的用途是让继承fd的进程能够查询fd以接收接口的名称和类型(ifr_nameifr_flags),或者知道 tuntap 设备尚未配置(EBADFD)并且它应该这样做。

因此,虽然可以使用gdb,但有点棘手,因为如果没有可用的开发环境,则必须了解或调整一些所需的参数和值(并且可能在将来或随架构而改变)。此处需要的这些信息和调整:

  • 定义$malloc64 位系统来处理返回的内存地址的正确大小:归功于SO 的此评论。
  • $malloc(64)struct ifreq在 64 位上似乎是 40 字节,为了安全起见,我们使用 64。
  • 0x800454d2== TUNGETIFF
  • 结果ifr_name位于偏移量 0 处。

下面是一个 shell 脚本,为每个发现的 tuntap fd 的调用做好准备gdb,以及 gdb 自己的脚本:

tungetiff.sh:

#!/bin/sh

[ $# -gt 0 ] || exit 1
SCRIPTGDB="$1"; shift

for pid in "$@"; do
    for procfd in /proc/$pid/fd/*; do
        if [ "$(readlink $procfd)" = "/dev/net/tun" ]; then
            fd=$(basename $procfd)
            printf 'pid=%d fd=%d ifname=' $pid $fd
            gdb -batch-silent --pid=$pid -ex 'set $fd'=$fd -x "$SCRIPTGDB"
        fi
    done
done
Run Code Online (Sandbox Code Playgroud)

tungetiff.gdb:

set $malloc=(void *(*)(long long)) malloc
p $malloc(64)
p ioctl($fd, 0x800454d2, $1)
set *((char *)($1+16))=0
set logging file /dev/stdout
set logging on
printf "%s\n",$1
set logging off
call free($1)
quit
Run Code Online (Sandbox Code Playgroud)

典型的执行示例(可能只能以 root 身份运行,即使用户 libvirt-qemu 似乎也无法 ptrace qemu -system):

# ./tungetiff.sh tungetiff.gdb $(pgrep qemu-system-)
pid=22281 fd=26 ifname=vnet1
pid=22281 fd=30 ifname=vnet2
pid=27109 fd=26 ifname=vnet0
Run Code Online (Sandbox Code Playgroud)