#!/bin/bash # Generate interface files for a dumbbell topology (as well as some special cases) # # Read the comments in this script or the help for details. # For more info see: http://sandilands.info/sgordon/virtnet # # $Revision$ # $Author$ # $Date$ # $URL$ # Default values lefthosts=1 righthosts=0 routers=0 topology="100" outdir="./" createnodelist=0 drawfig=0 drawdot=0 function usage { echo "Usage: vn-generatedumbbell [OPTIONS]..." echo "Generate interface files for virtnet for dumbbell-like topology" echo " " echo "OPTIONS" echo "Options may take either a long form (--long) or short form (-l). Options" echo "with parameters have a space between the option and parameter. The" echo "options available are [default values in square brackets]:" echo " -t, --topology NUM number to be assigned to the created topology [100]" echo " -l, --lefthosts NUM number of hosts on left of dumbbell [1]" echo " -r, --righthosts NUM number of hosts on right of dumbbell [0]" echo " -R, --routers NUM number of routers in middle of dumbbell [0]" echo " -n, --nodelist if set, create a node list file [not set]" echo " -d, --drawdot if set, create a dot figure of topology [not set]" echo " -o, --out DIR save all output files in directory DIR [./]" echo " " echo "EXAMPLES" echo "1. LAN of 5 hosts, no routers (not really dumbbell)" echo " vn-generatedumbbell -l 5" echo " " echo "2. Chain topology with 3 routers and hosts on either end; dot file output" echo " vn-generatedumbbell -l 1 -R 3 -r 1 -d" echo " " echo "3. Dumbbell (uneven); node list file also created" echo " vn-generatedumbbell -l 3 -R 4 -r 2 -n -o ../../data/interfaces" echo " " echo "4. Multiple clients, single server; topology 101" echo " vn-generatedumbbell -l 10 -R 1 -r 1 -t 101" echo " " echo "Some special cases/rules:" echo " - if 0 routers are specified, then all right hosts will be transferred" echo " to become left hosts, e.g. -l 3 -R 0 -r 2 will produce -l 5" echo " - topologies may have 0 left and/or right hosts if at least 1 router" echo " " echo "OUTPUT" echo "The primary output is a set of interface files, one per node in the" echo "topology, the can then be used with vn-createtopology to create a virtnet" echo "network. An explanation of the interface files and their format can be" echo "found in ../../data/topologies/README.txt" echo " " echo "If the --nodelist option is used, a text file containing the list of nodes," echo "their names and IP addresses, is also created. This can be used by other" echo "virtnet scripts, e.g. vn-applycmd to apply commands to only the hosts." echo " " echo "If the --drawdot option is used, then two .dot files will be created, " echo "which then can be used by 'dot' (part of GraphViz) to create an image" echo "showing the topology. The files are named:" echo " virtnet-topology-NUM-named.dot" echo " virtnet-topology-NUM-nodes.dot" echo "where the named file includes node names such as left1, router2, sw1" echo "and the nodes file includes node numbers such as 1, 2, 3. If 'dot' is" echo "installed these files can be used to produce images such as PNG by:" echo " dot -Tpng virtnet-topology-NUM-named.dot > virtnet-topology-NUM-named.png" echo " " echo "TODO" echo "Add option for ring topology, connecting first router with last." } # Read input parameters while [ "$1" != "" ]; do case $1 in -l | --lefthosts ) shift; lefthosts=$1; ;; -r | --righthosts ) shift; righthosts=$1; ;; -R | --routers ) shift; routers=$1; ;; -n | --nodelist ) createnodelist=1; ;; -f | --drawfig ) drawfig=1; ;; -d | --drawdot ) drawdot=1; ;; -t | --topology ) shift; topology=$1; ;; -o | --out ) shift; outdir=$1; ;; * ) usage exit 1 esac shift done # If no routers, then must be a LAN. Join the left and right hosts. if [ "${routers}" = "0" ]; then lefthosts=$(( ${lefthosts} + ${righthosts} )); righthosts=0 fi # If only routers (no left or right hosts) then create a ring if [ "${lefthosts}" = "0" -a "${righthosts}" = "0" ]; then if [ "${routers}" = "0" ]; then echo "You cannot have 0 hosts and 0 routers." echo "Exiting." exit 1 else specialcase="ring" fi fi # Topology parameters numnetworks=$(( ${routers} + 1)); totalnodes=$(( ${lefthosts} + ${routers} + ${righthosts} )); firstrouter=$(( ${lefthosts} + 1 )); lastrouter=$(( ${lefthosts} + ${routers} )); netnumber=1 # Address structure # Ip addresses are split into parts: # commonnetwork - subnet - hostbase - hostspecific # where # commonnetwork is the same for all networks, e.g. 192.168. commonnetwork="192.168." # subnet is an integer, incremented for each subnet subnet=1 # hostbase is 1 for left hosts lefthostbase=1 # and 2 or higher for right hosts righthostbase=2 # hostspecific is an integer, increment for each host in the subnet hostspecific=1 # # The result is address such as: # lefthosts: 192.168.1.11, 192.168.1.12, 192.168.1.13, ... , 192.168.1.20, ... # righthosts: 192.168.x.21, 192.168.x.22, 192.168.x.23, ..., 192.168.x.30, ... # router1: 192.168.1.1, 192.168.2.1 # router2: 192.168.2.2, 192.168.3.2 # router3: 192.168.3.3, 192.168.4.3 # routerN: 192.168.N.N, 192.168.(N+1).N # Other addresses used: netmask="255.255.255.0" defaultnetwork="${commonnetwork}0.0" defaultnetmask="255.255.0.0" # Check the limits # Currently there is a limit on the number of networks/routers supported # since the router addressing scheme will overlap with host addressing # scheme in the right network. This is limited by righthostbase. # To lift this limit, increase righthostbase parameter above. limitrouters=$(( ${righthostbase} * 10 - 1 )); if [ ${routers} -gt ${limitrouters} ]; then echo "vn: The number of routers you selected (${routers}) creates" echo "vn: too many networks such that there will be a conflict" echo "vn: in addresses assigned to hosts and routers. The" echo "vn: current limit is ${limitrouters}. Either reduce the" echo "vn: number of routers, or modify this script and increase" echo "vn: righthostbase." echo "vn: Error. Address conflict due to too many routers. Exiting." exit 1 fi # Currently there is a limit on the number of networks/routers supported # since networks are named a, b, c, d, ... . # To lift this limit, need to modify the naming scheme, e.g. a, b, ... z, aa, ab, ... limitnetworks=26 if [ ${numnetworks} -gt ${limitnetworks} ]; then echo "vn: The number of routers you selected (${routers}) creates" echo "vn: more than the supported number of networks (${limitnetworks})." echo "vn: Please try again with less routers (or change this script" echo "vn: to support more networks)". echo "vn: Error. Too many routers. Exiting." exit 1 fi # DOT figure if [ "${drawdot}" = "1" ]; then dotfilenamed="${outdir}/virtnet-topology-${topology}-named.dot" dotfilenodes="${outdir}/virtnet-topology-${topology}-nodes.dot" echo "graph topology${topology} {" | tee ${dotfilenamed} ${dotfilenodes} > /dev/null echo "rankdir=LR" | tee -a ${dotfilenamed} ${dotfilenodes} > /dev/null echo "node [shape=rectangle,width=0.5,height=0.5]" | tee -a ${dotfilenamed} ${dotfilenodes} > /dev/null echo "s1 [shape=rectangle,width=0.3,height=0.15,label=\"sw\"]" >> ${dotfilenamed} echo "s2 [shape=rectangle,width=0.3,height=0.15,label=\"sw\"]" >> ${dotfilenamed} echo "s1 [shape=rectangle,width=0.3,height=0.15,label=\"\"]" >> ${dotfilenodes} echo "s2 [shape=rectangle,width=0.3,height=0.15,label=\"\"]" >> ${dotfilenodes} fi # Create the interfaces for each node for (( node=1; node<=${totalnodes}; node++ )); do # Node number in text format noden=${node} if [ ${noden} -lt 10 ]; then noden="0${noden}"; fi # Name of interface file nodeiffile="${outdir}/interfaces-topology-${topology}-node-${noden}" # IP addresses # Left hosts if [ ${node} -lt ${firstrouter} ]; then nodetype="host" hostaddress=$(( ${lefthostbase} * 10 + ${node} )); ipaddress="${commonnetwork}${subnet}.${hostaddress}"; netaddress="${commonnetwork}${subnet}.0" bcaddress="${commonnetwork}${subnet}.255" defaultrouter="${commonnetwork}${subnet}.1" nodename="left${node}" # DOT figure if [ "${drawdot}" = "1" ]; then echo "${nodename} -- s1" >> ${dotfilenamed} echo "${node} -- s1" >> ${dotfilenodes} dotlastnode="s1" dotlastnum="s1" fi # Right hosts elif [ ${node} -gt ${lastrouter} ]; then nodetype="host" hostaddress=$(( ${righthostbase} * 10 + ${node} - ${lefthosts} - ${routers} )); ipaddress="${commonnetwork}${subnet}.${hostaddress}"; routerhostportion=$(( ${subnet} - 1 )); defaultrouter="${commonnetwork}${subnet}.${routerhostportion}" nodename="right$(( ${node} - ${lefthosts} - ${routers} ))" # DOT figure if [ "${drawdot}" = "1" ]; then if [ ${node} -eq $(( ${lastrouter} + 1 )) ]; then echo "${dotlastnode} -- s2" >> ${dotfilenamed} echo "${dotlastnum} -- s2" >> ${dotfilenodes} fi echo "s2 -- ${nodename}" >> ${dotfilenamed} echo "s2 -- ${node}" >> ${dotfilenodes} fi # Routers else nodetype="router" routernum=$(( ${node} - ${lefthosts} )); ipaddress="${commonnetwork}${subnet}.${routernum}" subnet2=$(( ${subnet} + 1 )); ipaddress2="${commonnetwork}${subnet2}.${routernum}" netaddress2="${commonnetwork}${subnet2}.0" bcaddress2="${commonnetwork}${subnet2}.255" nodename="router$(( ${node} - ${lefthosts} ))" # DOT figure if [ "${drawdot}" = "1" ]; then echo "${dotlastnode} -- ${nodename}" >> ${dotfilenamed} echo "${dotlastnum} -- ${node}" >> ${dotfilenodes} dotlastnode=${nodename} dotlastnum=${node} fi fi netaddress="${commonnetwork}${subnet}.0" bcaddress="${commonnetwork}${subnet}.255" # Output node list if [ "${createnodelist}" = "1" ]; then nodelistfile="${outdir}/nodelist-topology-${topology}" echo "${node},${nodename},${ipaddress}" >> ${nodelistfile} fi # Convert subnet number into name, e.g. "1" to "neta" subnetnum=$(( ${subnet} + 96 )); subnetletter=`printf "\x$(printf %x $subnetnum)"`; vboxnet="net${subnetletter}" if [ "${nodetype}" = "router" ]; then subnet2num=$(( ${subnet2} + 96 )); subnet2letter=`printf "\x$(printf %x $subnet2num)"`; vboxnet2="net${subnet2letter}" fi echo "# Interface file auto-generated by vn-generateinterfaces" > ${nodeiffile} echo "auto lo" >> ${nodeiffile} echo "iface lo inet loopback" >> ${nodeiffile} echo " " >> ${nodeiffile} echo "auto eth0" >> ${nodeiffile} echo "iface eth0 inet dhcp" >> ${nodeiffile} echo " " >> ${nodeiffile} echo "# VBoxNetwork: ${vboxnet}" >> ${nodeiffile} echo "auto eth1" >> ${nodeiffile} echo "iface eth1 inet static" >> ${nodeiffile} echo -e "\taddress ${ipaddress}" >> ${nodeiffile} echo -e "\tnetmask ${netmask}" >> ${nodeiffile} echo -e "\tnetwork ${netaddress}" >> ${nodeiffile} echo -e "\tbroadcast ${bcaddress}" >> ${nodeiffile} # Host node if [ "${nodetype}" = "host" ]; then echo -e "\tpost-up route add -net ${defaultnetwork} netmask ${defaultnetmask} gw ${defaultrouter} dev eth1" >> ${nodeiffile} echo -e "\tpre-down route del -net ${defaultnetwork} netmask ${defaultnetmask} gw ${defaultrouter} dev eth1" >> ${nodeiffile} # Router node else # In a chain such as: # H1 - R1 - R2 - R3 - R4 - R5 - H2 # we will treat R1, R2 and R3 as left side routers, and R4 and R5 as right side # Left side routers will have: # - default route to rest of network on eth2 (right side of router) # - specific routes to subnets on left of it on eth1 # Note that we don't need to add specific routes for directly attached networks # as they are automatically created when the interface is brought up. # For example R2 will have: # - default route to 192.168.0.0 to gw R3 on eth2 # - specific router to 192.168.1.0 (H1-R1) to gw R1 on eth1 # Similar applies for right side routers, but in the opposite direction: # - default route to rest of network on eth1 (left side of router) # - specific routes to subnets on right of it on eth2 firstrouternum=1 midrouternum=$(( (${routers} + 1) / 2 )); lastrouternum=${routers} # Left side routers if [ ${routernum} -le ${midrouternum} ]; then # Add 'numsr' specific routes on 1st interface numsr=$(( ${routernum} - ${firstrouternum} )); for (( sr=1; sr<=${numsr}; sr++ )); do destnetwork="${commonnetwork}${sr}.0" destmask=${netmask} destgw="${commonnetwork}${subnet}.$(( ${subnet} - 1 ))" destif="eth1" echo -e "\tpost-up route add -net ${destnetwork} netmask ${destmask} gw ${destgw} dev ${destif}" >> ${nodeiffile} echo -e "\tpre-down route add -net ${destnetwork} netmask ${destmask} gw ${destgw} dev ${destif}" >> ${nodeiffile} done # Create 2nd interface on router echo " " >> ${nodeiffile} echo "# VBoxNetwork: ${vboxnet2}" >> ${nodeiffile} echo "auto eth2" >> ${nodeiffile} echo "iface eth2 inet static" >> ${nodeiffile} echo -e "\taddress ${ipaddress2}" >> ${nodeiffile} echo -e "\tnetmask ${netmask}" >> ${nodeiffile} echo -e "\tnetwork ${netaddress2}" >> ${nodeiffile} echo -e "\tbroadcast ${bcaddress2}" >> ${nodeiffile} # Add default route on 2nd interface if [ ${routers} -gt 1 ]; then defaultgw="${commonnetwork}$(( ${subnet} + 1 )).$(( ${subnet} + 1 ))"; defaultif="eth2" echo -e "\tpost-up route add -net ${defaultnetwork} netmask ${defaultnetmask} gw ${defaultgw} dev ${defaultif}" >> ${nodeiffile} echo -e "\tpre-down route add -net ${defaultnetwork} netmask ${defaultnetmask} gw ${defaultgw} dev ${defaultif}" >> ${nodeiffile} fi # Right side routers else # Add default route on 1st interface defaultgw="${commonnetwork}${subnet}.$(( ${subnet} - 1 ))"; defaultif="eth1" echo -e "\tpost-up route add -net ${defaultnetwork} netmask ${defaultnetmask} gw ${defaultgw} dev ${defaultif}" >> ${nodeiffile} echo -e "\tpre-down route add -net ${defaultnetwork} netmask ${defaultnetmask} gw ${defaultgw} dev ${defaultif}" >> ${nodeiffile} # Create 2nd interface on router echo " " >> ${nodeiffile} echo "# VBoxNetwork: ${vboxnet2}" >> ${nodeiffile} echo "auto eth2" >> ${nodeiffile} echo "iface eth2 inet static" >> ${nodeiffile} echo -e "\taddress ${ipaddress2}" >> ${nodeiffile} echo -e "\tnetmask ${netmask}" >> ${nodeiffile} echo -e "\tnetwork ${netaddress2}" >> ${nodeiffile} echo -e "\tbroadcast ${bcaddress2}" >> ${nodeiffile} # Add 'numsr' specific routes on 2nd interface numsr=$(( ${lastrouternum} - ${routernum} )); for (( sr=${numsr}; sr>=1; sr-- )); do destnetwork="${commonnetwork}$(( ${subnet} + 1 + ${sr} )).0" destmask=${netmask} destgw="${commonnetwork}$(( ${subnet} + 1 )).$(( ${subnet} + 1 ))" destif="eth2" echo -e "\tpost-up route add -net ${destnetwork} netmask ${destmask} gw ${destgw} dev ${destif}" >> ${nodeiffile} echo -e "\tpre-down route add -net ${destnetwork} netmask ${destmask} gw ${destgw} dev ${destif}" >> ${nodeiffile} done fi fi # Increment subnet if necessary if [ "${nodetype}" = "router" ]; then subnet=$(( ${subnet} + 1 )); fi done if [ "${specialcase}" = "ring" ]; then echo "TODO: create ring by connecting router1 to routerN" fi if [ "${drawdot}" = "1" ]; then echo "}" | tee -a ${dotfilenamed} ${dotfilenodes} > /dev/null fi