1#!/bin/sh 2# 3# Copyright (c) 2010, Yavuz Gokirmak 4# 5# All rights reserved. 6# 7# This source code may be used, modified, copied, distributed, and 8# sold, in both source and binary form provided that the above 9# copyright and these terms are retained, verbatim, as the first 10# lines of this file. Under no circumstances is the author 11# responsible for the proper functioning of the software nor does 12# the author assume any responsibility for damages incurred with 13# its use. 14# 15# $FreeBSD$ 16# 17# 18# This script creates and connects n router like nodes. Complex wide 19# area topologies can be created with the help of script. 20# 21# Virtual nodes are generated via jails and network connections are 22# established using ng_eiface(4) node types. 23# 24# To use this script: 25# 26# 0. Make your own copy of this example script. 27# 28# 1. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual 29# nodes. Virtual topology definition includes node names and their 30# IP address. Target top. sytax: ( name|ip<->name|ip ... ) 31# Example 1: ( n1|10.0.2.1/30<->n2|10.0.2.2/30 ...) 32# Example 2: ( n1|2001:b90::14a/125<->n1|2001:b90::14b/125 ...) 33# 34# 2. Run this script with "start" as the command line argument. 35# 36# 3. Add necessary static route commands for each virtual node. For 37# example assume you have three virtual nodes connected each other 38# llike a chain ( n1 is connected to n2, n2 is connecte to n3 ). 39# In order to estabklish connectivity among these virtual nodes, 40# you have to add default routes to node n1 and node n3. Example 41# static route command is: 42# STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 43# STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 44# After defining default routes with above format you have to set 45# the total number of static route commands as: 46# STATIC_ROUTE_CNT=2 47# 48# 4. Stop bridging by running this script with "stop" as the 49# command line argument. 50# 51# 5. This cript uses a template file in order to carry information 52# between start and stop calls. 53# In the start call, the netgraph interfaces and jails are created. 54# At the stop phase, all created objects should be removed. 55# DO NOT delete the temporary file between the start and stop phases. 56# 57# Target Topology: 58# 59# +---------------+ +---------------------------------------------+ 60# | n1 (vimage) | | n2 (vimage) | 61# | | | | 62# | +-----------+ | | +-----------+ +-----------+ +-----------+ | 63# | | ngeth0 | | | | ngeth1 | | ngeth2 | | ngeth4 | | 64# | |(ng_eiface)| | | |(ng_eiface)| |(ng_eiface)| |(ng_eiface)| | 65# | +--+-----+--+ | | +--+-----+--+ +--+-----+--+ +--+-----+--+ | 66# | |ether| | | |ether| |ether| |ether| | 67# | +-X---+ | | +--X--+ +--X--+ +--X--+ | 68# +-------X-------+ +------X--------------X---------------X-------+ 69# X X X X 70# X X X X 71# XXXXXXXXXXXXXXX X X 72# X X 73# +--------X------+ +--------X------+ 74# | -+--X--+- | | -+--X--+- | 75# | |ether| | | |ether| | 76# | +--+-----+--+ | | +--+-----+--+ | 77# | | ngeth3 | | | | ngeth5 | | 78# | |(ng_eiface)| | | |(ng_eiface)| | 79# | +-----------+ | | +-----------+ | 80# | | | | 81# | n3 (vimage) | | n4 (vimage) | 82# +---------------+ +---------------+ 83# 84# 85# 86 87# List the names of virtual nodes and their IP addresses. Use ':' 88# character to seperate node name from node IP address and netmask. 89 90TARGET_TOPOLOGY="n1|10.0.2.1/30<->n2|10.0.2.2/30 n2|10.0.2.5/30<->n3|10.0.2.6/30 n2|10.0.2.9/30<->n4|10.0.2.10/30" 91STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 92STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 93STATIC_ROUTE2="jexec n4 route add -inet default 10.0.2.9" 94STATIC_ROUTE_CNT=3 95 96# MAC manifacturer prefix. This can be modified according to needs. 97MAC_PREFIX="00:1d:92" 98 99# Temporary file is important for proper execution of script. 100TEMP_FILE="/var/tmp/.virtual.chain.tmp" 101 102# Set root directory for jails to be created. 103JAIL_PATH="/usr/jails/router" 104 105 106#################################################################### 107#### Nothing below this point should need to be modified. #### 108#################################################################### 109 110 111# Start/restart routine. 112virtual_chain_start() { 113 114 # Load netgraph KLD's as necessary. 115 116 for KLD in ng_ether ng_bridge ng_eiface; do 117 if ! kldstat -v | grep -qw ${KLD}; then 118 echo -n "Loading ${KLD}.ko... " 119 kldload ${KLD} || exit 1 120 echo "done" 121 fi 122 done 123 124 # Reset all interfaces and jails. If temporary file can not be found 125 # script assumes that there is no previous configuration. 126 127 if [ ! -e ${TEMP_FILE} ]; then 128 echo "No previous configuration(${TEMP_FILE}) found to clean-up." 129 else 130 echo -n "Cleaning previous configuration..." 131 virtual_chain_stop 132 echo "done" 133 fi 134 135 # Create temporary file for usage. This file includes generated 136 # interface names and jail names. All bridges, interfaces and jails 137 # are written to file while created. In clean-up process written 138 # objects are cleaned (i.e removed) from system. 139 140 if [ -e ${TEMP_FILE} ]; then 141 touch ${TEMP_FILE} 142 fi 143 144 145 # Attach other interfaces as well. 146 for CONNECTION in ${TARGET_TOPOLOGY}; do 147 148 # Virtual connections are defined in TARGET_TOPOLOGY variable. 149 # They have the form of 'nodeName|IPaddr'. Below two lines split 150 151 PEER1=`echo ${CONNECTION} | awk -F"<->" '{print $1}'` 152 PEER1_NAME=`echo ${PEER1} | awk -F"|" '{print $1}'` 153 PEER1_IP=`echo ${PEER1} | awk -F"|" '{print $2}'` 154 155 PEER2=`echo ${CONNECTION} | awk -F"<->" '{print $2}'` 156 PEER2_NAME=`echo ${PEER2} | awk -F"|" '{print $1}'` 157 PEER2_IP=`echo ${PEER2} | awk -F"|" '{print $2}'` 158 159 # !!! if not created already.. 160 # Create virtual node (jail) with given name and using 161 # JAIL_PATH as root directory for jail. 162 163 virtual_chain_create_peer_if_necessary ${PEER1_NAME} 164 virtual_chain_create_peer_if_necessary ${PEER2_NAME} 165 166 # create an interface for peer with the given peer IP. Get interface 167 # for future use; you will connect this interface to the other 168 # peers' (PEER2) interface. 169 virtual_chain_create_interface_with_ip ${PEER1_NAME} ${PEER1_IP} 170 PEER1_INTERFACE=${RET_INTERFACE} 171 172 # create an interface for peer with the given peer IP. Get interface 173 # for future use; you will connect this interface to the other 174 # peers' (PEER2) interface. 175 virtual_chain_create_interface_with_ip ${PEER2_NAME} ${PEER2_IP} 176 PEER2_INTERFACE=${RET_INTERFACE} 177 178 # Connect virtual interface to other interface. Syntax is : 179 # ngctl connect INTERFACE1: INTERFACE2: ether ether. 180 181 echo -n "Connecting ${PEER1_INTERFACE}:ether to ${PEER2_INTERFACE}:ether..." 182 ngctl connect ${PEER1_INTERFACE}: ${PEER2_INTERFACE}: ether ether \ 183 || exit 1 184 echo "done" 185 186 done 187 188 # Executes static route add commands. 189 i=0 190 while [ $i != $STATIC_ROUTE_CNT ]; do 191 eval ROUTE=\${STATIC_ROUTE${i}} 192 ret=`${ROUTE}` 193 i=`expr $i + 1` 194 done 195 196 echo "Virtual WAN established succesfully!" 197} 198 199virtual_chain_create_interface_with_ip() { 200 201 NODE_NAME=$1 202 NODE_IP=$2 203 204 # Create a ng_eiface object for virtual node. ng_eiface 205 # object has a hook that can be connected to one of bridge 206 # links. After creating interface get its automatically 207 # generated name for further usage. 208 209 echo "Creating eiface interface for virtual node ${NODE_NAME}." 210 ngctl mkpeer eiface ether ether 211 EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'` 212 echo "Interface ${EIFACE} is created." 213 214 # Write name of the interface to temp file. Clean-up procedure 215 # will use this name to shutdown interface. 216 217 echo "interface ${EIFACE}" >> ${TEMP_FILE} 218 219 # Move virtual interface to virtual node. Note that Interface 220 # name will not be changed at the end of this movement. Moved 221 # interface can be seen at the output of ifconfig command in 222 # jail: 'jexec jailname ifconfig' 223 224 echo "Moving ${EIFACE} to ${NODE_NAME}" 225 ifconfig ${EIFACE} vnet ${NODE_NAME} 226 227 # Make lo0 interface localhost. 228 jexec ${NODE_NAME} ifconfig lo0 localhost 229 230 # Generate a random mac address for virtual interface. First 231 # three octets can be changed by user. Last three octets are 232 # generated randomly. 233 M4=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 234 awk '{ print $1 % 256 }'` 235 M5=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 236 awk '{ print $1 % 256 }'` 237 M6=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 238 awk '{ print $1 % 256 }'` 239 240 MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}` 241 242 # Set the link address (mac address) of virtual interface in 243 # virtual node to randomly generated MAC. 244 echo "Setting MAC address of ${EIFACE} to '${MAC}'" 245 jexec ${NODE_NAME} ifconfig ${EIFACE} link $MAC 246 247 # Either IPv4 or IPv6 can be used in this script. Ifconfig 248 # IP setting syntax differs slightly for two IP versions. 249 # For version 4 'inet' keyword is used whereas for version 6 250 # 'inet6' is used. Below line tries to decide which IP version 251 # is given and sets IPVER to 'inet' or 'inet6'. 252 253 IPVER=`echo ${NODE_IP} | awk -F"." '{ split($4,last,"/"); \ 254 if( NF==4 && $1>0 && $1<256 && $2<256 && $3<256 && \ 255 last[1]<256) print "inet"; else print "inet6"}'` 256 257 # Set IP address of virtual interface in virtual node. 258 echo "Setting IP address of ${EIFACE} to '${NODE_IP}'" 259 jexec ${NODE_NAME} ifconfig ${EIFACE} ${IPVER} ${NODE_IP} 260 261 RET_INTERFACE=${EIFACE} 262} 263 264virtual_chain_create_peer_if_necessary() { 265 266 if ! grep -q $1 ${TEMP_FILE} ; then 267 268 echo -n "Creating virtual node (jail) ${1}..." 269 jail -c vnet name=${1} host.hostname=${1} \ 270 path=${JAIL_PATH} persist 271 jexec ${1} sysctl -w net.inet.ip.forwarding=1 272 jexec ${1} sysctl -w net.inet6.ip6.forwarding=1 273 echo "done" 274 275 # Write name of the jail to temp file. Clean-up 276 # procedure will use this name to remove jail. 277 278 echo "node ${1}" >> ${TEMP_FILE} 279 fi 280 281} 282 283 284# Stop routine. 285virtual_chain_stop() { 286 287 if [ ! -e ${TEMP_FILE} ]; then 288 echo "Nothing to stop! ${TEMP_FILE}: temp file not found" 289 else 290 291 echo -n "Shutdown bridge interface.." 292 OBJECTS=`cat ${TEMP_FILE} | grep bridge | awk '{print $2}'` 293 for BRIDGE in ${OBJECTS}; do 294 ngctl shutdown ${BRIDGE}: >/dev/null 2>&1 295 done 296 echo "done" 297 298 echo -n "Shutdown all eiface interfaces..." 299 OBJECTS=`cat ${TEMP_FILE} | grep interface | awk '{print $2}'` 300 for INTERFACE in ${OBJECTS}; do 301 ngctl shutdown ${INTERFACE}: >/dev/null 2>&1 302 done 303 echo "done" 304 305 echo -n "Removing all jails..." 306 OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'` 307 for NODE in ${OBJECTS}; do 308 jail -r ${NODE} 309 done 310 echo "done" 311 312 echo "Removing tempfile ${TEMP_FILE}" 313 rm ${TEMP_FILE} 314 fi 315 echo "Virtual LAN objects removed succesfully!" 316 317} 318 319virtual_chain_usage() { 320 echo "usage: $0 start [target_topology]" 321 echo " : $0 [ stop | help ]" 322} 323 324 325# Main entry point. 326 327 328case $# in 329 1) 330 case $1 in 331 start) 332 echo -n "Creating default target topology:" 333 echo " ${TARGET_TOPOLOGY}" 334 virtual_chain_start 335 ;; 336 stop) 337 338 if [ ! -e ${TEMP_FILE} ]; then 339 echo -n "Noting to stop! ${TEMP_FILE}:" 340 echo " temp file not found" 341 else 342 virtual_chain_stop 343 fi 344 ;; 345 help) 346 virtual_chain_usage 347 exit 1 348 ;; 349 *) 350 virtual_chain_usage 351 exit 1 352 353 esac 354 ;; 355 2) 356 case $1 in 357 start) 358 TARGET_TOPOLOGY=$2 359 echo -n "Creating target topology:" 360 echo "${TARGET_TOPOLOGY}" 361 virtual_chain_start 362 ;; 363 *) 364 virtual_chain_usage 365 exit 1 366 esac 367 ;; 368 369 *) 370 virtual_chain_usage 371 exit 1 372esac 373 374