Skip to content
forked from wkz/qeneth

String together QEMU VMs with UDP sockets using graphviz(7), mustache(1) and duct tape

License

Notifications You must be signed in to change notification settings

kernelkit/qeneth

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

60 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Qeneth

String together QEMU VMs with UDP sockets using graphviz(7), mustache(1) and duct tape.

               .--------.
    Graph ---> |        |      .---------------------.
               | qeneth | ---> | qemu-system-${ARCH} |-.
Templates ---> |        |      '---------------------' |-.
               '--------'        `---------------------' |
                                   `---------------------'

qeneth starts from an undirected graphviz(7) Graph of "record nodes". The graph is analyzed, annotated with information, and split into per-node YAML documents. Finally, these documents are filtered through one or more mustache(1) Templates to create executable scripts. Typically the generated script will launch an instance of qemu-system-${ARCH}, but it could be anything.

Some basic process monitoring is available, but the executables can also be run manually or through some other process monitor.

Installation

Clone the repository and place the absolute path to qeneth is in your $PATH. A few tools are required to run, on Debian based systems like Ubuntu or Linux Mint:

~$ sudo apt install e2fsprogs graphviz ruby-mustache squashfs-tools \
        qemu-system-x86 qemu-utils

Basic completion support for Bash is available. Copy or symlink qeneth-complete.sh to /etc/bash_completion.d to enable it.

Tutorial

This tutorial makes use of one of the bundled Infix NOS templates. We start by creating our tutorial directory where all files related to our network project will be stored. Then we download a pre-built Infix image into it:

~$ mkdir tutorial
~$ cd tutorial/
~/tutorial$ wget -q https://github.com/kernelkit/infix/releases/download/latest/infix-x86_64.tar.gz
~/tutorial$ tar xf infix-x86_64.tar.gz infix-x86_64/infix-x86_64.img
~/tutorial$ mv infix-x86_64/infix-x86_64.img .
~/tutorial$ rm -rf infix-x86_64/ infix-x86_64.tar.gz

Next, let's setup our topology, edit ~/tutorial/topology.dot.in:

graph "tutorial" {
    node [shape=record];
    qn_template="infix-x86_64";
    qn_append="quiet";
    qn_mem="512M";
    qn_oui="00:a0:85";

    server [label="server   | { <e1> e1 | <e2> e2 }", qn_basemac="02:de:ad:00:00:00"];
    client1 [label="client1 | { <eth1> eth1 }"];
    client2 [label="client2 | { <eth1> eth1 }"];

    server:eth0 -- client1:eth0 [qn_tailmac="02:de:ad:12:34:56", qn_headmac="08:00:20:c0:ff:ee"];
    server:eth1 -- client2:eth0;
}

The two clients in this example connect to the server, which has two interfaces named e1 and e2. We name all <interfaces> (called "ports" in graphviz) and put the same label on them. The name is used in all qemu script generation and the label is "only" used when generating a graph. Use your favorite graphviz(7) layout engine to visualize it. Here we've used neato -Tpng topology.dot.in -otopology.png:

Network topology

  • qn-append is what is appended to the kernel command line
  • qn_mem overrides the template's default RAM size for all nodes
  • qn_oui sets a default MAC OUI prefix for all nodes
  • qn_basemac changes the base MAC used for all connections on one node
  • qn_tailmac and qn_headmac overrides the MAC address for a specific interface on a link

Note: the names headmac and tailmac refer to the head and tail end of the edge in the graph. Even though we do not use directed edges the left side is called the tail and the right is head.

Everything we need is now in place - so we can generate the scripts and all support files:

~/tutorial$ qeneth generate
Info: Generating topology
Info: Generating node YAML
Info: Generating executables
~/tutorial$ ls
client1         client2         infix-x86_64.img  server.yaml   topology.dot.in
client1.mactab  client2.mactab  server            topology.dot
client1.yaml    client2.yaml    server.mactab

Finally, we can start our network:

~/tutorial$ qeneth start
Info: Launching server
Info: Launching client1
Info: Launching client2
~/tutorial$ qeneth status
NODE           PID  CNSOL  MONTR
server     2811526  10000  10001
client1    2811527  10010  10011
client2    2811528  10020  10021

Attach to the different nodes with qeneth console NODE. Use Ctrl-] to get to the telnet> prompt, quit with 'q' followed by enter:

~/tutorial$ qeneth console server
Trying 127.0.0.1...
Connected to localhost.

Infix by KernelKit (console)
server login: root
Note: use help, show, and setup commands to set up and diagnose the system.
root@server:~# ip -br link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
eth0             UP             02:00:00:00:02:00 <BROADCAST,MULTICAST,UP,LOWER_UP>
eth1             UP             02:00:00:00:02:01 <BROADCAST,MULTICAST,UP,LOWER_UP>
root@server:~#
telnet> Connection closed.
~/tutorial$

When we are done, we stop all the nodes:

~/tutorial$ qeneth stop
Info: Stopping server
Info: Stopping client1
Info: Stopping client2

Host Interaction

In addition to VM-to-VM communication, a Qeneth network can also be connected to the host system. This is done by connecting a VM port to a port on a node named host. Every port on host will show up as a TAP interface.

Let's look at an example topology (~/host-net/topology.dot.in):

graph "host-net" {
        node [shape=record];
        qn_template="infix-x86_64-classic";

        host   [label="host  | { <tap-infix> tap-infix }"];
        infix  [label="infix | { <eth0> eth0 | <eth1> eth1 }"];

        host:"tap-infix" -- infix:eth0;
}

After generating and starting the network, a new TAP interface (tap-infix) is created, which is connected to eth0 inside the VM infix:

~/host-net$ qeneth generate && qeneth start
Info: Generating topology
Info: Generating node YAML
gvpr: warning: Using value of uninitialized edge attribute "qn_headport" of "host--infix"
Info: Generating executables
Info: Launching infix
~/host-net$ ip -br link | grep tap-infix
tap-infix        DOWN           ce:fe:82:f8:cd:19 <BROADCAST,MULTICAST>
~/host-net$ ip addr add 10.10.10.1/24 dev tap-infix
~/host-net$ qeneth console infix
Trying 127.0.0.1...
Connected to localhost.

Infix by KernelKit (console)
infix login: root
Note: use help, show, and setup commands to set up and diagnose the system.
root@infix:~# ip addr add 10.10.10.2/24 dev eth0
root@infix:~# ping -c 3 10.10.10.1
PING 10.10.10.1 (10.10.10.1): 56 data bytes
64 bytes from 10.10.10.1: seq=0 ttl=64 time=0.241 ms
64 bytes from 10.10.10.1: seq=1 ttl=64 time=0.474 ms
64 bytes from 10.10.10.1: seq=2 ttl=64 time=0.504 ms

--- 10.10.10.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.241/0.406/0.504 ms
root@infix:~#
telnet> q
Connection closed.

About

String together QEMU VMs with UDP sockets using graphviz(7), mustache(1) and duct tape

Resources

License

Stars

Watchers

Forks

Languages

  • Shell 69.5%
  • Python 16.9%
  • Mustache 11.6%
  • Dockerfile 2.0%