我有一个qemu
由编排脚本启动的虚拟机,该脚本创建临时tap
接口。当我检查进程的命令行参数时qemu-system-x86_64
,我可以看到该进程tap
使用文件描述符连接到已经打开的接口27
:
-netdev tap,fd=27,id=hostnet1,vhost=on,vhostfd=28
据ls -l /proc/<qemu-system-x86_64_PID>/fd/27
其指出/dev/net/tun
。
它是否以某种方式工作,将tap
接口名称(例如vnet99
)传递给/dev/net/tun
withioctl()
并且返回正确的 fd ?或者一般来说,我如何找出tap
主机中的哪个接口具有文件描述符 27?
答案1
这iff:
条目其中本来可以给 一个答案曾是内核3.14添加与提交tun: add device name(iff) field to proc fdinfo entry
,因此在内核 3.13 或更早版本上不可用,例如 Ubuntu 14.04LTS。
在这种情况下,虽然不可能要求内核提供信息,但仍然可以询问实际过程通过使用调试器跟踪来提供此信息。
自 Linux 2.6.27 起,有一个 ioctl 可用,用于询问有关已配置的信息通塔普接口:TUNGETIFF
。它的用途是用于继承FD以便能够查询到FD接收接口的名称和类型(ifr_name
和ifr_flags
)或知道 tuntap 设备尚未配置(EBADFD
)并且它应该进行配置。
所以虽然可以使用gdb
,这有点棘手,因为如果没有可用的开发环境,则必须了解或调整一些所需的参数和值(并且可能在将来或随着架构而改变)。此处需要的这些信息和调整:
- 定义
$malloc
对于 64 位系统来说,要处理返回的内存地址的正确大小:功劳在于这条评论来自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
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
典型的执行示例(可能只能以 root 身份运行,即使用户 libvirt-qemu 似乎也无法跟踪qemu 系统):
# ./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
答案2
已知文件描述符 27 后,您可以/proc
在 QEMU 的进程下进行操作:
$ ls /proc/<qemu-system-x86_64_PID>/fd/27
与该目录处于同一级别的fd
是另一个目录fdinfo
,其中包含如下详细信息:
$ cat /proc/<qemu-system-x86_64_PID>/fdinfo/27
pos: 0
flags: 0104002
mnt_id: 18
iff: tap0123acdc-66
该文件中的条目iff
是 Tap 设备。