Local network for local virtual machines: Working bash skript

In a past article i described how to enable KVM virtualization on your Linux. Present article is about virtual network you can span with several commands or simple script.

Motivation

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

Network Details

To meet all these needs we need some decoupling from local and external network configuration. So decision is to have internal local network. To be flexible in plug an unplug new network nodes we need something like virtual switch. That virtual switch is a key-feature of local-network (localhost private network), you host system and every vms are members of it. But additionally your local host network stack acts as router, firewall and NAT to external networks. All the network setting for your external network remain the same. Your internal network configuration should not carry about external world.

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

If i look on my 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 states i'm connected the network via WLAN. I just have to start my script which will discover this and make that interface to the default gateway... also the gateway configuration will be done properly. I don't have to worry how i'm currently connected.

Working Script

Install Brigde-util package

sudo aptitude install bridge-utils  

Now you have a powerfull brctl tool installed. Store the following as a script e.g. start_brvmnat0.sh and use it to start your network.

In this example the name of the bridge device is brvmnat0, and network address is 192.168.33.1/24.

#!/bin/bash

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

#function that returnes default Gatewy 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 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 is.


How to use

Start your network with

sudo ./script_name.sh  

this will enable new a new brigde device

~$ ifconfig
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.
And of course it changes the way a start the vm. I have to connect the virtual device to a virtual brigde...

# 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 VM's on different network location, giving them Internet access trough 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 ideas and help with the script.