In a past article i described how to enable KVM virtualization on your Linux. The present article is about a virtual network you can span let say on your Laptop. It needs several commands or a simple script.

Motivation

Private virtual machines, if they are created, usually needed to be connected to some networks. Of course, there could be plenty of different solutions for variable infrastructure contexts. In this post I’m focusing on simple and easy way to create network hang all the local vms into it. So I need a solution that is good for Linux-based private or small organizations. It is even the best solution (until you provide a better one in a comment :))  for your laptop. Because laptops can be moved around, I don’t want to rely on any constraints of the external network, also maybe I don’t want exposed to much information about my local network. Further, the whole has to be simple and easy too.

Network Details

To meet all these demands we need some decoupling from local and external network configuration. I think this can be achieved by having independent (virtual) local network in between. To be flexible in plug and unplug new network nodes, we need something like a virtual switch. That virtual switch is a key feature of our localhost private network. Your host system and every VMs are members of it. But additionally, your local host network stack acts as a router, firewall, and NAT to external networks too.

Having that, all the external network setting can remain the same. Your internal network configuration should not carry about the details of the connection to the external networks.

Of course, Linux provides the best networking tools nearly out of the box. So what we need is the bridge-utils package. Everything else is usually already installed in common distros.

Get Interfaces Overview

If I look at my network interfaces I’ll see

eth0      Link encap:Ethernet  HWaddr XX:yy:XX:yy:XX:9b
UP BROADCAST MULTICAST  MTU:1500  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
Interrupt:20 Memory:f3900000-f3920000
....
wlan0     Link encap:Ethernet  HWaddr 8c:XX:XX:XX:XX:XX
inet addr:192.168.0.39  Bcast:192.168.0.255  Mask:255.255.255.0
inet6 addr: YYYY::XXXX:6cb2:8ff1:d879/64 Scope:Global
inet6 addr: YYYY::XXXX:5aff:fe21:a420/64 Scope:Global
inet6 addr: YYYY::XXXX:5aff:fe21:a420/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:102741 errors:0 dropped:0 overruns:0 frame:0
TX packets:63828 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:115518394 (115.5 MB)  TX bytes:8309878 (8.3 MB)

that means I’m connected to the network via WLAN. I just have to start my script which will discover exactly this and make that interface to the default gateway… also the gateway configuration will be done properly.

Setuping script

Install Bridge-util package

sudo aptitude install bridge-utils

Now you have a powerful brctl CLI installed and this is the only dependency you might need. Store the following as a script e.g. start_brvmnat0.sh and use it to setup your network. In this example, the name of the bridge device is brvmnat0, and the network address is 192.168.33.1/24.

Script

#!/bin/bash

# Bridge interface name and address
BR_VMNAT_DEV="brvmnat0"
BR_VMNAT_ADDR="192.168.33.1/24"

#function that returns default Gateway device
get_def_gw_dev(){
	local i STR_DEV_SPEC STR_DEV
 	while read i; do
  	# Skip non default routes
 	[ "$(echo $i | cut -d " " -f 1)" != "default" ] && continue

	# Extract dev part
	STR_DEV_SPEC=$(echo $i | egrep -o "dev .*")
	STR_DEV=${STR_DEV_SPEC##dev }
	STR_DEV=$(echo $STR_DEV| cut -d " " -f 1)

    # Output device to stdout
	echo $STR_DEV
	done <<(ip route show)
}

declare -A IF_IP # declared as Array
while read i; do
	# Set if_name and if_ip
	if_name="${i%% *}"
	if_ip="${i##* }"

	# Check if_name in IF_SHADOW
	if_shadowed=""
	for i_s in $IF_SHADOW; do
		[ "$i_s" = "$if_name" ] && if_shadowed="true" && break
	done
	[ ! -z "$if_shadowed" ] && continue

	# Add interface and IP to an array
	IF_IP[$if_name]="$if_ip"
done <<(ip addr list | egrep -o 'inet ([0-9]|\.)+.*' | awk '{print($(NF), $2)}' | cut -d '/' -f 1)

# Check bridge exists or construct
[ -z "$(ip link list | egrep -o '^[0-9]+: .+:' | egrep -o1 ' .*:' | cut -d " " -f 2|cut -d ":" -f 1 | grep "$BR_VMNAT_DEV")" ] && {
	echo "Bringing up $BR_VMNAT_DEV"
	brctl addbr "$BR_VMNAT_DEV"
	ifconfig "$BR_VMNAT_DEV" "$BR_VMNAT_ADDR"
} || echo "bridge allready exist"

# Obtain default GW device and IP
DEF_GW_DEV=$(get_def_gw_dev | head -1 )

[ -z "$DEF_GW_DEV" ] && echo "No default gateway" && exit 1

DEF_GW_ADDR=$(ip addr list dev "$DEF_GW_DEV" |egrep -o "inet [0-9.]+" | cut -d " " -f 2)
[ -z "$DEF_GW_ADDR" ] && echo "Can not obtain default gateway address" && exit 1

# Play with iptables
{
	echo "Configuring NAT rules for $DEF_GW_ADDR as gateway"

	#enable forwarding
	echo 1 &gt; /proc/sys/net/ipv4/ip_forward

	# Flush NAT table
	iptables -t nat -F

	# Create SNAT
	iptables -t nat -A POSTROUTING -o "$DEF_GW_DEV" -s "$BR_VMNAT_ADDR" -m state --state=NEW -j SNAT --to-source "$DEF_GW_ADDR"
}

#echo ${IF_IP[$
exit 0

That all.


How to use

Start your network with

sudo ./script_name.sh

this will enable new a new bridge device. Check it with infconfig:

ifconfig
#returns..
brvmnat0  Link encap:Ethernet  HWaddr yy:xx:yy:xx:yy:64
inet addr:192.168.33.1  Bcast:192.168.33.255  Mask:255.255.255.0
inet6 addr: yyyy::xxxx:yyyy:xxxx:6a86/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:800 errors:0 dropped:0 overruns:0 frame:0
TX packets:884 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:219390 (219.3 KB)  TX bytes:143951 (143.9 KB)

Your VMS should have IP addresses in a network 192.168.33.1/24. At the moment I give them static range.

How to connect VMs

And of course, it changes the way how we start the VMs. We have to connect the virtual device to a virtual bridge…

# after VM is started with virtual interface name ${VIF}
ifconfig ${VIF} up
echo "Exec brctl addif $BRIDGENAME $VIF"
brctl addif ${BRIDGENAME} ${VIF}

If you extend your VM starting script like this, they will be automatically plugged into a bridged network. On the other site, the provided script detects you Gateway automatically allowing you to work with your VMs on different network locations, giving them Internet access through local NAT. Of course, the VM see each other too.

Hope this can be of interest to someone. Please feel free to bring new ideas or questions.

P.S. Thank you Boris for the ideas and help with the script.