在测试和使用openvswitch的时候, 一部分人会使用KVM(kernel virtual machine)创建虚拟机.但KVM需要有一定的硬盘的支持, 还传说需要CPU支持虚拟化. 而也有一部分人使用LXC. LXC是一个轻量级的虚拟化技术, 占用的空间更小, 门槛也更低. 有兴趣的同学可以看一下
http://lxc.sourceforge.net/(需翻墙)
https://help.ubuntu.com/12.04/serverguide/lxc.html

    在Mininet中, 开发者也用了类似的方法生成虚拟环境. 在对mininet的代码研究的时候, 我觉得我可以把一些基础的功能捡出来, 然后用C实现. 这一时兴起才有了我编的小程序和这篇总结的文章.

    LXC可以生成多种虚拟环境. 实现这么多种虚拟环境依靠的其实就是unshare的方法(shell方法, C方法). 由于ovs只需要网络的虚拟环境, 所以我也只实现了网络虚拟化. 非常庆幸的是LXC的官网上开放了网络虚拟化的代码(需翻墙). 运行这段代码即可为我们的程序造出一个虚拟的网络环境.

下面这行代码用了unshare方法造出一个虚拟网络环境

unshare(CLONE_NEWNET);

而下面这行代码用exel族函数在虚拟网络环境中运行我们的程序.

execve(argv[0], argv, __environ);

    现在, 我们有了一个网络虚拟环境. 将其编译后就可以放着了.
    但是我做到这时仍有了一个疑问, 何为”我们的程序”? 显然, “我们的程序”是控制和使用虚拟网络环境的程序. 所以我的答案, 也是最简单的答案是: bash.

    接下来是往该bash里写命令来控制虚拟环境. bash会运行在一个进程里, 而往一个进程里写数据当然是用管道最简单. 所以我写了另外一个程序并用管道的方法打开虚拟环境的那个文件, 让其运行起bash. 建立管道的话, Popen()是个好方法, 但不幸的是, C语言中, Popen()建立的管道只能是单向的, 要么只能读数据, 要么只能写数据. 所以, 我只好自己写一个函数建立双向管道. 我参考的方法是:Linux 上实现双向进程间通信管道. 用该方法打开虚拟环境, 然后就可以读写数据来控制该环境了!

下面是我改好后的双向管道函数.

struct ProcessInfo{
    FILE stream; ///< Pointer to the duplex pipe stream
    pid_t pid; ///< Process ID of the command
};
struct ProcessInfo dpopen(char *const command[])
{
        int fd[2];
        pid_t pid;
        struct ProcessInfo pi;
        FILE *stream;
        // 创建管道
        socketpair(AF_UNIX, SOCKET_STREAM, 0, fd);
        if ( (pid = fork()) == 0) { // 子进程
            // 关闭管道的父进程端
            close(fd[0]);
            // 复制管道的子进程端到标准输出
            dup2(fd[1], STDOUT_FILENO);
            // 复制管道的子进程端到标准输入
            dup2(fd[1], STDIN_FILENO);
            // 关闭已复制的读管道
            close(fd[1]);
            /* 使用exec执行命令 */
            execvp(command[0],command);
        } else {    // 父进程
            // 关闭管道的子进程端
            close(fd[1]);
            stream = fdopen(fd[0], "r+");
            if (stream == NULL) {
                close(fd[0]);
                return;
            }
            /* Successfully return here */
            pi.pid = pid;
            pi.stream = stream;
            return pi;
        }
}

但是, 创建的虚拟环境只有回环口lo, 所以目前该环境即没有对外的网口, 更不可能和外界通信. 解决这个问题的方法是用ip命令.

ip link add name <interface1> type veth peer name <interface2>
ip link set <interface> netns <pid>

上面的命令可以用exel族函数在主进程里执行. 其功能是创建一对相连的虚拟网卡, 并把一个放入到虚拟环境里一个留在主机里. 这样就实现了虚拟环境与外界的通信. 如果再把留在主机里的网卡加到ovs的网桥上, 那么虚拟环境就可以连上网桥了.

总结一下过程
1. 将该代码(需翻墙)编译成一个文件, 例如: vm.o
2. 用自己写的双向管道函数打开vm.o, 并把/bin/bash作为参数传入vm.o中
3. 用两个ip命令建立两个相连的网卡并把一个网卡放入虚拟环境中, 使虚拟环境能和外界相连.
4. 将留在主机里的网卡加入到ovs的网桥中, 使虚拟环境连到网桥上

剩下的功能就可以即兴发挥了. 比如可以创建任意数量的虚拟环境, 任意数量的网口等.. Have a good time~

One Comment

Leave a Reply

Your email address will not be published.