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 > /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.