我想使用 sudo 创建一个 Tap,然后将用户空间程序 (ssh) 附加到它。
我不想对接口名称进行硬编码,因为多个用户将使用它,所以我希望动态生成接口名称。
所以我像这样创建水龙头:
sudo ip tuntap add mode tap user $USER
如何让 ip 命令返回创建的接口的名称,以便我可以使用它传递给 ssh?
答案1
该tuntap
设备的 Linux 编程接口记录在此处(带有示例代码):tuntap.rst。可以询问接口名称,然后使用初始调用传递的结构来检索接口名称ioctl(fd, TUNSETIFF, ...)
。由于ip tuntap
不显示该名称,因此仅此命令无法提供信息。
有两种方法可以做到这一点。
库包装器
可以添加一个 libc 包装器ioctl()
对于这个特定目标,可以在运行时使用LD_PRELOAD
从而无需修改原始工具。这是相应用途的包装器ioctl()
,将打印到标准输出刚刚创建的接口的名称。考虑到 OP 的ip tuntap
示例仅使用了 3 次ioctl()
与轻敲接口,这是相当安全的,尽管ioctl()
其所有用途并不总是具有相同数量的参数(这里假设有 3 个参数)。
wraptuntap.c
:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#define ioctl ioctl_diverted
#include <sys/ioctl.h>
#undef ioctl
#include <stdio.h>
int ioctl(int fd, unsigned long request, void *p) {
int ret;
int (*orig_ioctl)(int, unsigned long, void *)=dlsym(RTLD_NEXT,"ioctl");
if ((ret=orig_ioctl(fd, request, p)) != -1 )
if (request == TUNSETIFF)
printf("%s\n",((struct ifreq *)p)->ifr_name);
return ret;
}
评论:
<sys/ioctl.h>
需要TUNSETIFF
正确定义,但同时我们不能ioctl()
定义,否则包装器的定义会发生冲突。它的初始定义被转移为这个SO Q/A的方法。ifr_name
保证是自内核 4.13 起以 null 终止否则……好吧,这是一个脆弱的包装纸。
例如要编译为:
gcc -shared -fPIC -o /tmp/wraptuntap.so wraptuntap.c -ldl
然后像这样使用:
# LD_PRELOAD=/tmp/wraptuntap.so ip tuntap add mode tap user 1000
tap0
# LD_PRELOAD=/tmp/wraptuntap.so ip tuntap add mode tap user 1000
tap1
# LD_PRELOAD=/tmp/wraptuntap.so ip tuntap add mode tap user 1000 name foo%d
foo0
# LD_PRELOAD=/tmp/wraptuntap.so ip tuntap add mode tap user 1000 name foo%d
foo1
或更有用的:
# mytun=$(LD_PRELOAD=/tmp/wraptuntap.so ip tuntap add mode tap user 1000 name foo%d)
# ip link show dev "$mytun"
11: foo2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether ae:99:4a:2b:1f:6b brd ff:ff:ff:ff:ff:ff
由于创建这样的界面并不是很困难,因此对于生产用途,应该优先创建一个专用的工具,或者使用其他可以显示此类界面名称的工具。
年长的tunctl
(仅限于轻敲接口):
随 UML 实用程序一起提供的较旧的(因此通常已弃用的)tunctl
工具会显示一个带有所使用的接口名称的句子,但只会创建模式轻敲(第 2 层)接口(这正是 OP 正在寻找的)而不是屯(第 3 层)接口。
例子:
# tunctl -u 1000 -t foo%d
Set 'foo3' persistent and owned by uid 1000
# tunctl -u 1000 -t foo%d | sed -E "s/^Set '([^']+)' .*\$/\1/"
foo4
# ip -d link show dev foo4 # this is a "type tap" interface
13: foo4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c2:05:de:78:16:a3 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65521
tun type tap pi off vnet_hdr off persist on user 1000 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536