用 Python 操作虚拟网卡

在我的 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/tapXX 表示网卡序号)字符设备文件,分别对应于 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 下的实现代码如下:

 
comments powered by Disqus