长期以来对tun和tap这对兄弟分不太清,今天下定决心研究了一下代码,总算是搞明白了。
首先它们都是从/dev/net/tun里ioctl出来的虚拟设备,一个是通过IFF_TUN,另一个是 IFF_TAP。最好的例子莫过于vpnc里面的代码了。
-
int tun_open(char *dev, enum if_mode_enum
mode) -
{
-
struct ifreq
ifr; -
int fd,
err; -
-
if ((fd = open("/dev/net/tun",
O_RDWR)) <0) { -
error(0,
errno, -
"can't
open /dev/net/tun, check that it is either device char 10 200 or (with DevFS) a symlink to ../misc/net/tun (not misc/net/tun)"); -
return -1;
-
}
-
-
memset(&ifr, 0, sizeof(ifr));
-
ifr.ifr_flags = ((mode == IF_MODE_TUN) ?
IFF_TUN : IFF_TAP) |
IFF_NO_PI; -
if (*dev)
-
strncpy(ifr.ifr_name,
dev, IFNAMSIZ); -
-
if ((err = ioctl(fd,
TUNSETIFF, (void *)&ifr)) <0) { -
close(fd);
-
return err;
-
}
-
strcpy(dev,
ifr.ifr_name); -
return fd;
-
}
用的ioctl的命令都是同一个TUNSETIFF。
虽然是出自一个娘,但它们仍然有大的不同。tun是点对点的设备,而tap是一个普通的以太网卡设备。也就是说,tun设备其实完全不需要有物理地址的!它收到和发出的包不需要arp,也不需要有数据链路层的头!而tap设备则是有完整的物理地址和完整的以太网帧。
用一个实际的例子来验证一下:
tap0 Link encap:Ethernet HWaddr 0E:78:39:78:E7:A7
inet addr:192.168.1.109 Bcast:192.168.1.255 Mask:255.255.255.0
inet6 addr: fe80::c78:39ff:fe78:e7a7/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:21 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:X.X.X.X P-t-P:X.X.X.X Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1412 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:690 (690.0 b) TX bytes:402 (402.0 b)
% ethtool -i tun0
driver: tun
version: 1.6
firmware-version: N/A
bus-info: tun
% ethtool -i tap0
driver: tun
version: 1.6
firmware-version: N/A
bus-info: tap
继续回来看代码。还是vpnc的代码 tunip.c,看它发送的时候做了什么处理: