使用NetCat实现宿主机和虚拟机间通信

QEMU模式的网络通信协议是NAT协议。为了实现宿主机和虚拟机间的通信,需要知道宿主机相对于虚拟机的IP地址,更准确地说,应该是内部虚拟网络的网关地址。

Netcat是Linux下的一个用于调试和检查网络工具包。可用于创建TCP/IP连接,处理TCP套接字。

本文首先介绍了NAT网络的基本概念,然后介绍了通过NetCat实现宿主机和虚拟机间通信的方法。

NAT的基本思想是,为每个公司分配一个IP地址,用于传输Internet流量。在每个公司内部,每台计算机有唯一的IP地址,它使用该地址来传输内部流量。然而,当一个分组离开公司的网络,发向ISP时,它需要执行一个地址的转换。为了保证这种方案的可行性,有三段IP地址范围已经被声明为私有地址。这三段保留的地址范围为

基地址 子网掩码 主机地址个数
10.0.0.0 -10.255.255.255/8 16777216
172.16.0.0 -172.31.255.255/12 1048576
192.168.0.0 -192.168.255.255/16 65536

当一个进程希望与另一个远程进程建立TCP连接时,它绑定到一个本地机器尚未使用的TCP端口上,该端口称为源端口,它告诉TCP代码,凡是属于该连接进来的分组都应该发送给这个进程。这个进程也要提供一个目标端口,已执行分组被送到远程机器上应该给谁。从01023之间的端口都是保留端口,用于一些知名的服务。例如80端口被用于Web服务器,所以,远程客户可以找到Web服务。每个向外发送的TCP消息都包含一个源端口和一个目标端口。这两个端口合起来标识出了客户端和服务器端正在使用该连接的进程。

利用source port(源端口)域,我们可以解决前面的映射问题。任何时候,当一个向外发送的分组进入到NAT盒的时候,源地址10.x.y.z被公司的真实IP地址所取代,而且,TCP的source port域被一个索引值所取代,该索引值指向NAT盒的地址转换表中65535个表项之一。该表项包含了原来的IP地址和原来的源端口。最后NAT盒重新计算IP头和TCP头的校验和,并将校验和插入到分组中。这里之所以要替换source port域,是因为10.0.0.110.0.0.2出发的连接可能碰巧使用了同一个端口,比如5000,所以,仅仅使用source port域还不足以唯一标识发送进程。

当一个分组从ISP到达NAT盒的时候,NAT盒从TCP头中提取中源端口,把他作为索引值从NAT盒的映射表中找到对应的表项,然后从该表项中提取出内部IP地址和原来的TCP source port,并将它们插入到分组中。然后重新计算IP和TCP的校验和,并插入分组中。最后将该分组传递给公司内部的路由器,它使用10.x.y.z地址进行正常的路由。

Aaron所使用的宿主机器所在的网段是192.168.1.0/24

QEMU默认给虚拟机分配的IP地址是10.0.2.15,这是虚拟的DHCP服务器可以分配的第一个IP地址。虚拟的DHCP服务器所在的地址是10.0.2.2。虚拟的DNS服务器的地址是10.0.2.3

查看虚拟机的网络信息:

aaron@ubuntu904-i386:~$ ifconfig -a
eth0      Link encap:Ethernet  HWaddr 52:54:00:12:34:56
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:345 errors:0 dropped:0 overruns:0 frame:0
          TX packets:308 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:45014 (45.0 KB)  TX bytes:46252 (46.2 KB)
          Interrupt:11 Base address:0xc100

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:7 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:760 (760.0 B)  TX bytes:760 (760.0 B)

查看虚拟机的路由表:

aaron@ubuntu904-i386:~$ netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
10.0.2.0        0.0.0.0         255.255.255.0   U         0 0          0 eth0
0.0.0.0         10.0.2.2        0.0.0.0         UG        0 0          0 eth0

从路由表得到内部网络网关IP是10.0.2.2。我们将使用这个IP与宿主机器进行通信。

我们使用的通信工具是NetCat,命令简称是nc。终端下输入man nc可以查看这个工具的手册,可以知道这是一个用于TCP/UDP链接和监听的工具。

NC(1)                     BSD General Commands Manual                    NC(1)

NAME
     nc -- arbitrary TCP and UDP connections and listens

...

DESCRIPTION
     The nc (or netcat) utility is used for just about anything under the sun involving TCP or UDP.  It can open TCP connections, send UDP packets, listen on
     arbitrary TCP and UDP ports, do port scanning, and deal with both IPv4 and IPv6.  Unlike telnet(1), nc scripts nicely, and separates error messages onto
     standard error instead of sending them to standard output, as telnet(1) does with some.

首先,让宿主机器在某个端口监听TCP连接,这里选取端口12345

$ nc -l 12345

然后,让虚拟机创建连接,目标地址为网关IP,目标端口12345:

$ nc 10.0.2.15 12345

这时就建立好了TCP连接,宿主机器可以与虚拟机器通信了。

在宿主机器终端输入的消息:

aaron@aaron-u1204:~$ nc -l 12345
hello
world

消息将实时显示在虚拟机器上。

aaron@ubuntu904-i386:~$ nc 10.0.2.2 12345
hello
world

注意通信是双向的,虚拟机发送的消息也将实时地显示在宿主机上。按ctrl + c结束通信。

这种方法可以用于宿主机器和虚拟机器之间的文件传输。

例如,假设宿主机器上有一个文件input.txt,文件内容如下:

aaron@aaron-u1204:~/Desktop$ cat input.txt
this is
a
input
file
.

下面通过nc命令将其传输到虚拟机上。

宿主机终端执行命令:

aaron@aaron-u1204:~/Desktop$ nc -l 12345 < input.txt

虚拟机上采用如下命令接收文件:

aaron@ubuntu904-i386:~/test$ nc 10.0.2.2 12345 > input.txt

在虚拟机上查看文件内容:

aaron@ubuntu904-i386:~/test$ ls
input.txt
aaron@ubuntu904-i386:~/test$ cat input.txt
this is
a
input
file
.

由此可见,文件已经成功的传输到虚拟机器上了。

参考资料

  1. 计算机网络. 潘爱民译. 清华大学出版社
  2. QEMU/Networking
写于2016年11月23日