qemu虚拟机与外部网络的通信

来源:互联网 发布:mysql定时执行sql语句 编辑:程序博客网 时间:2024/05/17 03:49

如果想用virtio来实现一个虚拟设备大概流程是这样的:

1、自己实现scan设备的功能,然后调用register_virtio_device 函数来注册发现的虚拟设备。参考drivers/lguest/lguest_device.c里面的scan_devices 函数。一个典型的实现是,虚拟机主机提供的共享内存配置,里面提供了虚拟设备的列表一样的配置,然后自己根据配置注册device。

      注:pci总线等应该也是要扫描各个插槽,然后发现新的pci设备,然后创建device结构,再调用到驱动的probe函数里面的,应该各个总线都有发现新设备的机制,device一般是scan的时候创建。

2、为virtio_device 设备准备好virtqueue 队列。参考drivers/s390/kvm/kvm_virtio.c 里面的kvm_find_vq 函数。特别的是要提供自己的virtqueue 结构的notify函数。notify函数用于通知host主机队列里面已经有消息存在了,一般是hypercall ,就是通过vmcall指令或者traps(中断?)来通知主机。例如这个kvm代码。



3、应该/drivers/net/virtio_net.c 等代码已经实现了前端的网卡,磁盘等驱动了。所以只要你register_virtio_device注册了之后,客户机应该是可以看到相应的虚拟网卡的了。

这些驱动使用virtio的框架方法大概是这样的,

virtqueue_get_buf    这个函数用来获取virtqueue队列里面收到的消息。

virtqueue_add_buf 把发送到消息缓存写到virtqueue队列,然后 调用virtqueue_kick函数来通知host主机队列里面有消息需要它接受。virtqueue_kick将调用到virtqueue结构的notify函数,notify函数通过中断或者高级的虚拟机指令mcall通知到host主机。 host主机,根据实现可能是共享内存的hypercall的,就可以直接复制相应的内存过去就可以了。可以去看相应的虚拟机的hypercall的实现代码。

virtio的作用就是提供一种居于virtqueue的通用的虚拟io框架。


一、基本概念

为了使虚拟机能够与外界通信,Qemu需要为虚拟机提供网络设备。Qemu支持的常用网卡包括NE2000、rtl8139、pcnet32等。命令行上用-net nic为虚拟机创建虚拟机网卡。例如,qemu的命令行选项
                            -net nic,model=pcnet
表示为虚拟机添加一块pcnet型的以太网卡。如果省略model参数则qemu会默认选择一种网卡类型,目前采用的是Intel 82540EM(手册里说明的是e1000),可以在虚拟机启动后执行lspci命令查看。有了虚拟网络设备,下面的问题是如何用这些设备来联网。

首先,虚拟机的网络设备连接在qemu虚拟的VLAN中。每个qemu的运行实例是宿主机中的一个进程,而每个这样的进程中可以虚拟一些VLAN,虚拟机网络设备接入这些VLAN中。当某个VLAN上连接的网络设备发送数据帧,与它在同一个VLAN中的其它网路设备都能接收到数据帧。上面的例子中对虚拟机的pcnet网卡没有指定其连接的VLAN号,那么qemu默认会将该网卡连入vlan0。下面这个例子更具一般性:
      -net nic,model=pcnet -net nic,model=rtl8139,vlan=1, -net nic,model=ne2k_pci,vlan=1
该命令为虚拟机创建了三块网卡,其中第一块网卡类型是pcnet,连入vlan0;第二块网卡类型是 rtl8139,第三块网卡类型是ne2k_pci,这两块都连入vlan1,所以第二块网卡与第三块网卡可以互相通信,但它们与第一块网卡不能直接通信。

接下来,各个VLAN再通过qemu提供的4种通信方式与外界联网。

  1. User mode stack:这种方式在qemu进程中实现一个协议栈,负责在虚拟机VLAN和外部网络之间转发数据。可以将该协议栈视为虚拟机与外部网络之间的一个NAT服务器,外部网络不能主动与虚拟机通信。虚拟机VLAN中的各个网络接口只能置于10.0.2.0子网中,所以这种方式只能与外部网络进行有限的通信。此外,可以用-redir选项为宿主机和虚拟机的两个TCP或UDP端口建立映射,实现宿主机和虚拟机在特殊要求下的通信(例如X-server或ssh)。User mode stack通信方式由-net user选项启用,如果不显式指定通信方式,则这种方式是qemu默认的通信方式。
  2. socket:这种方式又分为TCP和UDP两种类型。
    (1)TCP:
    为一个VLAN创建一个套接字,让该套接字在指定的TCP端口上监听,而其他VLAN连接到该套接字上,从而将多个VLAN连接起来。缺点在于如果监听套接字所在qemu进程崩溃,整个连接就无法工作。监听套接字所在VLAN通过-net socket,listen选项启用,其他VLAN通过-net socket,connect选项启用。
    (2)UDP:所有VLAN连接到一个多播套接字上,从而使多个VLAN通过一个总线通信。所有VLAN都通过-net socket,mcast选项启用。
  3. TAP:这种方式首先需要在宿主机中创建并配置一个TAP设备,qemu进程将该TAP设备连接到虚拟机VLAN中。其次,为了实现虚拟机与外部网络的通信,在宿主机中通常还要创建并配置一个网桥,并将宿主机的网络接口(通常是eth0)作为该网桥的一个接口。最后,只要将TAP设备作为网桥的另一个接口,虚拟机VLAN通过TAP设备就可以与外部网络完全通信了。这是因为,宿主机的eth0接口作为网桥的接口,与外部网络连接;TAP设备作为网桥的另一个接口,与虚拟机VLAN连接,这样两个网络就连通了。此时,网桥在这两个网络之间转发数据帧。
    这里有两个问题需要注意:
    (1)网桥的转发工作需要得到内核的支持,所以在编译宿主机内核时需要选择与桥接相关的配置选项。
    (2)当宿主机eth0接口作为网桥接口时,不能为其配置IP地址,而要位将IP地址配置给网桥。
    TAP方式由-net tap选项启用。
  4. VDE:这种方式首先要启动一个VDE进程,该进程打开一个TAP设备,然后各个虚拟机VLAN与VDE进程连接,这样各个VLAN就可以通过TAP设备连接起来。VDE进程通过执行vde_switch命令启动,各个VLAN所在qemu进程通过执行veqe命令启动,这些VLAN就可以与VDE进程连接了。

以上四种通信方式中,socket方式和VDE方式用于虚拟机VLAN之间的连接,而user mode stack方式与外部网路的通信比较有限,所以下面主要讨论TAP方式的配置。

在没有做配置之前,首先需要对TAP设备有所认识。TUN/TAP是内核支持的网络虚拟设备,这种网络设备完全由的软件实现。与网络硬件设备不同,TUN/TAP负责在内核协议栈与用户进程之间传送协议数据单元。TUN与TAP的区别在于,TUN工作在网络层,而TAP则工作在数据链路层。具体在运行TCP/IP的以太网中,TUN与应用程序交换IP包,而TAP与应用程序交换以太帧。所以TUN通常涉及路由,而TAP则常用于网络桥接。TUN/TAP的典型应用包括:OpenVPN、OpenSSH 以及虚拟机网络。


二、在Ubuntu 10.04中配置qemu虚拟网络

  1. 内核支持需要对TUN/TAP设备和虚拟网桥提供支持:
    (1)Device Drivers
                --> Network device support
                            --> Universal TUN/TAP device driver support
    (2)Networking support
                --> Networking options
                            --> 802.1d Ethernet Bridging
  2. 安装两个配置网络所需软件包:
    apt-get install bridge-utils        # 虚拟网桥工具
    apt-get install uml-utilities       # UML(User-mode linux)工具
  3. 配置虚拟网桥的操作(假设系统启动后eth0已经启动,并且从DHCP获得IP地址)。
    ifconfig eth0 down                  # 先关闭eth0接口
    brctl addbr br0                     # 增加一个虚拟网桥br0
    brctl addif br0 eth0                # 在br0中添加一个接口eth0
    brctl stp br0 off                   # 只有一个网桥,所以关闭生成树协议
    brctl setfd br0 1                   # 设置br0的转发延迟
    brctl sethello br0 1                # 设置br0的hello时间
    ifconfig br0 0.0.0.0 promisc up     # 打开br0接口
    ifconfig eth0 0.0.0.0 promisc up    # 打开eth0接口
    dhclient br0                        # 从dhcp服务器获得br0的IP地址
    brctl show br0                      # 查看虚拟网桥列表
    brctl showstp br0                   # 查看br0的各接口信息
    在没有dhcp服务器的网络中也可以用ifconfig命令为br0接口配置一个静态IP地址:
    ifconfig br0 192.168.0.22 netmask 255.255.255.0
    route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.0.254
  4. 配置TAP设备的操作:
    tunctl -t tap0 -u root              # 创建一个tap0接口,只允许root用户访问
    brctl addif br0 tap0                # 在虚拟网桥中增加一个tap0接口
    ifconfig tap0 0.0.0.0 promisc up    # 打开tap0接口
    brctl showstp br0                   # 显示br0的各个接口
  5. 假设内核的顶层目录在/usr/src/linux,且内核已经编译完毕。启动qemu的操作:
    cd /usr/src/linux
    qemu -kernel arch/x86/boot/bzImage -net nic -net tap,ifname=tap0,script=no,downscript=no 
    如果省略script和downscript参数,qemu在启动时会以第一个不存在的tap接口名(通常是tap0)为参数去调用/etc/qemu-ifup脚本,而在退出时调用/etc/qemu-ifdown脚本。这两个脚本需要用户自行编写,其主要作用通常是:在启动时创建和打开指定的TAP接口,并将该接口添加到虚拟网桥中;退出时将该接口从虚拟网桥中移除,然后关闭该接口。由于配置TAP设备的操作前面已经做过了,所以启动qemu时显式地告诉qemu不要执行这两个脚本。这里需要严重注意:-net tap的各参数之间不要有空格!为了这个问题花了半个小时 :-(。


vhost_net.h/vhost_net.c

每个虚拟网卡都对应一个vhost_net,每个vhost_net都包含一个vhost_dev
代码很简单,基本是封装了vhost_dev的相关操作,并加入一些对tap的控制,对feature的控制等。

0 0