在我的 XTunnel 项目中,已经用 Python 作过这种相对底层的工作了(这说明 Python 果然还是非常强大的,上下层通吃啊),不过那边目前还是只实现了 Linux 的版本。后来我又陆陆续续地把 Windows 以及 Mac 下的操作方法给搞通了,今天就来总结一下。
在 Linux 内核中,特别是在现在的发行版中,应该都已经有了 TUN/TAP
虚拟网卡的驱动程序,看一下有没有 /dev/net/tun
这个文件就可以知道了。如果没有,就执行一下 sudo modprobe tun
这个命令吧。如果还是没有,那就 Google 之吧。下面上代码:
简而言之,就是首先打开对应的设备文件,然后通过 ioctl
系统调用告诉它我们想要的网卡类型和名称,同时还可以告诉它我们想以普通用户的身份来对它进行操作。之后通过 ifconfig
命令将新建的网卡拉起来,就可以开始读写了。
当你以 root 身份运行这个脚本的时候,可以 ping
一下 192.168.7.2
这个地址试试,看看是不是能 ping
得通。
下面的代码则在 Mac 环境中实现了同样的功能(不过还没有设置用户身份的功能):
当然要运行上面的代码,你首先要到这里下载并安装 Mac 下的 TUN/TAP
设备驱动程序才行。安装之后,在系统的 /dev
目录中就会分别有 16 个 /dev/tunX
以及 /dev/tapX
( X
表示网卡序号)字符设备文件,分别对应于 16
个同名的 TUN/TAP
虚拟网卡。当然,在运行这个脚本之前,你是看不到这些网卡的。不不,用 ifconfig -a
也不行。
因为与 Linux 下的驱动的实现方法不同,这里是用的文件名来标识网卡类型与名称,所以就不需要 Linux 版本中的第一个 ioctl
调用了。
也不记得一开始是在哪边看到的一个讲 TUN/TAP
编程的文章,说要实现对 ping
报文的处理,只要简单地将读到的 IP 报文中的源地址与目的地址对换一下,再写回去就可以了。在 Linux 系统中也确实是如此,因此我也就没有深究。直到后来才发现,同样的招数在 Mac 下居然没用。于是赶紧上网翻 ICMP 的报文格式,改报文中的 type
码,并重新计算 checksum
,这才搞定。这时也才发现,用 Python 来操作原始的字节流还是没有 C 这种底层语言直观啊。
可是 Linux 下为什么不需要这么麻烦呢?于是回去抓了抓包,这才发现,相对于 Mac 下的一问( ping
命令)一答( Python 脚本),在 Linux 下居然是两问两答,一问是 ping
命令,一问是我们的那个 Python 脚本。这也不奇怪,我连 ICMP
中的 type
码都没改,发过来的是 request
,那再发出去的当然还是 request
。至于应答,大概就是 Linux 的 TUN/TAP
驱动搞的鬼了。
最后,当然也有 Windows 的实现版本啦,不过代码被我丢到公司的 Windows 工作用机上了,所以,就请您且听下回分解了。
Update 2011-04-26: Windows 下的实现代码如下: