virtual.chain revision 214306
1151497Sru#!/bin/sh 2104862Sru# 3104862Sru# Copyright (c) 2010, Yavuz Gokirmak 4104862Sru# 5104862Sru# All rights reserved. 6104862Sru# 7104862Sru# This source code may be used, modified, copied, distributed, and 8104862Sru# sold, in both source and binary form provided that the above 9104862Sru# copyright and these terms are retained, verbatim, as the first 10104862Sru# lines of this file. Under no circumstances is the author 11104862Sru# responsible for the proper functioning of the software nor does 12104862Sru# the author assume any responsibility for damages incurred with 13104862Sru# its use. 14151497Sru# 15104862Sru# $FreeBSD: head/share/examples/netgraph/virtual.chain 214306 2010-10-24 22:59:38Z julian $ 16104862Sru# 17104862Sru# 18104862Sru# This script creates and connects n router like nodes. Complex wide 19104862Sru# area topologies can be created with the help of script. 20104862Sru# 21104862Sru# Virtual nodes are generated via jails and network connections are 22104862Sru# established using ng_eiface(4) node types. 23104862Sru# 24104862Sru# To use this script: 25104862Sru# 26104862Sru# 0. Make your own copy of this example script. 27151497Sru# 28104862Sru# 1. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual 29104862Sru# nodes. Virtual topology definition includes node names and their 30104862Sru# IP address. Target top. sytax: ( name|ip<->name|ip ... ) 31104862Sru# Example 1: ( n1|10.0.2.1/30<->n2|10.0.2.2/30 ...) 32104862Sru# Example 2: ( n1|2001:b90::14a/125<->n1|2001:b90::14b/125 ...) 33104862Sru# 34104862Sru# 2. Run this script with "start" as the command line argument. 35104862Sru# 36104862Sru# 3. Add necessary static route commands for each virtual node. For 37104862Sru# example assume you have three virtual nodes connected each other 38104862Sru# llike a chain ( n1 is connected to n2, n2 is connecte to n3 ). 39104862Sru# In order to estabklish connectivity among these virtual nodes, 40104862Sru# you have to add default routes to node n1 and node n3. Example 41104862Sru# static route command is: 42104862Sru# STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 43104862Sru# STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 44104862Sru# After defining default routes with above format you have to set 45104862Sru# the total number of static route commands as: 46104862Sru# STATIC_ROUTE_CNT=2 47104862Sru# 48104862Sru# 4. Stop bridging by running this script with "stop" as the 49104862Sru# command line argument. 50151497Sru# 51151497Sru# 5. This cript uses a template file in order to carry information 52151497Sru# between start and stop calls. 53151497Sru# In the start call, the netgraph interfaces and jails are created. 54151497Sru# At the stop phase, all created objects should be removed. 55104862Sru# DO NOT delete the temporary file between the start and stop phases. 56104862Sru# 57104862Sru# Target Topology: 58104862Sru# 59104862Sru# +---------------+ +---------------------------------------------+ 60104862Sru# | n1 (vimage) | | n2 (vimage) | 61104862Sru# | | | | 62104862Sru# | +-----------+ | | +-----------+ +-----------+ +-----------+ | 63104862Sru# | | ngeth0 | | | | ngeth1 | | ngeth2 | | ngeth4 | | 64104862Sru# | |(ng_eiface)| | | |(ng_eiface)| |(ng_eiface)| |(ng_eiface)| | 65104862Sru# | +--+-----+--+ | | +--+-----+--+ +--+-----+--+ +--+-----+--+ | 66104862Sru# | |ether| | | |ether| |ether| |ether| | 67104862Sru# | +-X---+ | | +--X--+ +--X--+ +--X--+ | 68104862Sru# +-------X-------+ +------X--------------X---------------X-------+ 69104862Sru# X X X X 70104862Sru# X X X X 71151497Sru# XXXXXXXXXXXXXXX X X 72104862Sru# X X 73151497Sru# +--------X------+ +--------X------+ 74104862Sru# | -+--X--+- | | -+--X--+- | 75104862Sru# | |ether| | | |ether| | 76104862Sru# | +--+-----+--+ | | +--+-----+--+ | 77104862Sru# | | ngeth3 | | | | ngeth5 | | 78104862Sru# | |(ng_eiface)| | | |(ng_eiface)| | 79104862Sru# | +-----------+ | | +-----------+ | 80104862Sru# | | | | 81104862Sru# | n3 (vimage) | | n4 (vimage) | 82104862Sru# +---------------+ +---------------+ 83104862Sru# 84104862Sru# 85104862Sru# 86104862Sru 87104862Sru# List the names of virtual nodes and their IP addresses. Use ':' 88104862Sru# character to seperate node name from node IP address and netmask. 89104862Sru 90104862SruTARGET_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" 91104862SruSTATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 92104862SruSTATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 93104862SruSTATIC_ROUTE2="jexec n4 route add -inet default 10.0.2.9" 94104862SruSTATIC_ROUTE_CNT=3 95104862Sru 96104862Sru# MAC manifacturer prefix. This can be modified according to needs. 97104862SruMAC_PREFIX="00:1d:92" 98104862Sru 99104862Sru# Temporary file is important for proper execution of script. 100104862SruTEMP_FILE="/var/tmp/.virtual.chain.tmp" 101104862Sru 102104862Sru# Set root directory for jails to be created. 103104862SruJAIL_PATH="/usr/jails/router" 104104862Sru 105104862Sru 106104862Sru#################################################################### 107104862Sru#### Nothing below this point should need to be modified. #### 108104862Sru#################################################################### 109104862Sru 110104862Sru 111104862Sru# Start/restart routine. 112104862Sruvirtual_chain_start() { 113104862Sru 114104862Sru # Load netgraph KLD's as necessary. 115104862Sru 116104862Sru for KLD in ng_ether ng_bridge ng_eiface; do 117104862Sru if ! kldstat -v | grep -qw ${KLD}; then 118104862Sru echo -n "Loading ${KLD}.ko... " 119104862Sru kldload ${KLD} || exit 1 120104862Sru echo "done" 121104862Sru fi 122104862Sru done 123104862Sru 124151497Sru # Reset all interfaces and jails. If temporary file can not be found 125104862Sru # script assumes that there is no previous configuration. 126104862Sru 127104862Sru if [ ! -e ${TEMP_FILE} ]; then 128104862Sru echo "No previous configuration(${TEMP_FILE}) found to clean-up." 129151497Sru else 130104862Sru echo -n "Cleaning previous configuration..." 131104862Sru virtual_chain_stop 132104862Sru echo "done" 133104862Sru fi 134104862Sru 135104862Sru # Create temporary file for usage. This file includes generated 136104862Sru # interface names and jail names. All bridges, interfaces and jails 137104862Sru # are written to file while created. In clean-up process written 138104862Sru # objects are cleaned (i.e removed) from system. 139104862Sru 140104862Sru if [ -e ${TEMP_FILE} ]; then 141104862Sru touch ${TEMP_FILE} 142104862Sru fi 143104862Sru 144104862Sru 145104862Sru # Attach other interfaces as well. 146104862Sru for CONNECTION in ${TARGET_TOPOLOGY}; do 147104862Sru 148104862Sru # Virtual connections are defined in TARGET_TOPOLOGY variable. 149104862Sru # They have the form of 'nodeName|IPaddr'. Below two lines split 150104862Sru 151104862Sru PEER1=`echo ${CONNECTION} | awk -F"<->" '{print $1}'` 152104862Sru PEER1_NAME=`echo ${PEER1} | awk -F"|" '{print $1}'` 153104862Sru PEER1_IP=`echo ${PEER1} | awk -F"|" '{print $2}'` 154104862Sru 155104862Sru PEER2=`echo ${CONNECTION} | awk -F"<->" '{print $2}'` 156104862Sru PEER2_NAME=`echo ${PEER2} | awk -F"|" '{print $1}'` 157104862Sru PEER2_IP=`echo ${PEER2} | awk -F"|" '{print $2}'` 158104862Sru 159104862Sru # !!! if not created already.. 160104862Sru # Create virtual node (jail) with given name and using 161104862Sru # JAIL_PATH as root directory for jail. 162104862Sru 163104862Sru virtual_chain_create_peer_if_necessary ${PEER1_NAME} 164104862Sru virtual_chain_create_peer_if_necessary ${PEER2_NAME} 165104862Sru 166104862Sru # create an interface for peer with the given peer IP. Get interface 167104862Sru # for future use; you will connect this interface to the other 168104862Sru # peers' (PEER2) interface. 169104862Sru virtual_chain_create_interface_with_ip ${PEER1_NAME} ${PEER1_IP} 170104862Sru PEER1_INTERFACE=${RET_INTERFACE} 171104862Sru 172104862Sru # create an interface for peer with the given peer IP. Get interface 173104862Sru # for future use; you will connect this interface to the other 174104862Sru # peers' (PEER2) interface. 175151497Sru virtual_chain_create_interface_with_ip ${PEER2_NAME} ${PEER2_IP} 176104862Sru PEER2_INTERFACE=${RET_INTERFACE} 177104862Sru 178104862Sru # Connect virtual interface to other interface. Syntax is : 179104862Sru # ngctl connect INTERFACE1: INTERFACE2: ether ether. 180104862Sru 181104862Sru echo -n "Connecting ${PEER1_INTERFACE}:ether to ${PEER2_INTERFACE}:ether..." 182104862Sru ngctl connect ${PEER1_INTERFACE}: ${PEER2_INTERFACE}: ether ether \ 183104862Sru || exit 1 184104862Sru echo "done" 185104862Sru 186104862Sru done 187104862Sru 188151497Sru # Executes static route add commands. 189104862Sru i=0 190104862Sru while [ $i != $STATIC_ROUTE_CNT ]; do 191104862Sru eval ROUTE=\${STATIC_ROUTE${i}} 192104862Sru ret=`${ROUTE}` 193104862Sru i=`expr $i + 1` 194104862Sru done 195104862Sru 196104862Sru echo "Virtual WAN established succesfully!" 197104862Sru} 198104862Sru 199104862Sruvirtual_chain_create_interface_with_ip() { 200104862Sru 201104862Sru NODE_NAME=$1 202104862Sru NODE_IP=$2 203104862Sru 204151497Sru # Create a ng_eiface object for virtual node. ng_eiface 205151497Sru # object has a hook that can be connected to one of bridge 206151497Sru # links. After creating interface get its automatically 207151497Sru # generated name for further usage. 208151497Sru 209151497Sru echo "Creating eiface interface for virtual node ${NODE_NAME}." 210151497Sru ngctl mkpeer eiface ether ether 211151497Sru EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'` 212151497Sru echo "Interface ${EIFACE} is created." 213151497Sru 214151497Sru # Write name of the interface to temp file. Clean-up procedure 215151497Sru # will use this name to shutdown interface. 216151497Sru 217151497Sru echo "interface ${EIFACE}" >> ${TEMP_FILE} 218151497Sru 219151497Sru # Move virtual interface to virtual node. Note that Interface 220151497Sru # name will not be changed at the end of this movement. Moved 221151497Sru # interface can be seen at the output of ifconfig command in 222104862Sru # jail: 'jexec jailname ifconfig' 223104862Sru 224104862Sru echo "Moving ${EIFACE} to ${NODE_NAME}" 225104862Sru ifconfig ${EIFACE} vnet ${NODE_NAME} 226104862Sru 227104862Sru # Make lo0 interface localhost. 228104862Sru jexec ${NODE_NAME} ifconfig lo0 localhost 229104862Sru 230104862Sru # 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