1214306Sjulian#!/bin/sh 2214306Sjulian# 3222179Suqs# Copyright (c) 2010, Yavuz Gokirmak 4214306Sjulian# 5214306Sjulian# All rights reserved. 6214306Sjulian# 7214306Sjulian# This source code may be used, modified, copied, distributed, and 8214306Sjulian# sold, in both source and binary form provided that the above 9214306Sjulian# copyright and these terms are retained, verbatim, as the first 10214306Sjulian# lines of this file. Under no circumstances is the author 11214306Sjulian# responsible for the proper functioning of the software nor does 12214306Sjulian# the author assume any responsibility for damages incurred with 13214306Sjulian# its use. 14214306Sjulian# 15214306Sjulian# $FreeBSD$ 16214306Sjulian# 17214306Sjulian# This script creates and connects n router like nodes. Complex wide 18214306Sjulian# area topologies can be created with the help of script. 19214306Sjulian# 20214306Sjulian# Virtual nodes are generated via jails and network connections are 21214306Sjulian# established using ng_eiface(4) node types. 22214306Sjulian# 23214306Sjulian# To use this script: 24214306Sjulian# 25214306Sjulian# 0. Make your own copy of this example script. 26214306Sjulian# 27222179Suqs# 1. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual 28214306Sjulian# nodes. Virtual topology definition includes node names and their 29222179Suqs# IP address. Target top. syntax: ( name|ip<->name|ip ... ) 30214306Sjulian# Example 1: ( n1|10.0.2.1/30<->n2|10.0.2.2/30 ...) 31214306Sjulian# Example 2: ( n1|2001:b90::14a/125<->n1|2001:b90::14b/125 ...) 32214306Sjulian# 33214306Sjulian# 2. Run this script with "start" as the command line argument. 34214306Sjulian# 35214306Sjulian# 3. Add necessary static route commands for each virtual node. For 36214306Sjulian# example assume you have three virtual nodes connected each other 37222179Suqs# like a chain (n1 is connected to n2, n2 is connected to n3). 38222179Suqs# In order to establish connectivity among these virtual nodes, 39214306Sjulian# you have to add default routes to node n1 and node n3. Example 40214306Sjulian# static route command is: 41222179Suqs# STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 42222179Suqs# STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 43222179Suqs# After defining default routes with above format you have to set 44214306Sjulian# the total number of static route commands as: 45214306Sjulian# STATIC_ROUTE_CNT=2 46214306Sjulian# 47214306Sjulian# 4. Stop bridging by running this script with "stop" as the 48214306Sjulian# command line argument. 49222179Suqs# 50222179Suqs# 5. This script uses a template file in order to carry information 51222179Suqs# between start and stop calls. 52214306Sjulian# In the start call, the netgraph interfaces and jails are created. 53214306Sjulian# At the stop phase, all created objects should be removed. 54214306Sjulian# DO NOT delete the temporary file between the start and stop phases. 55214306Sjulian# 56214306Sjulian# Target Topology: 57214306Sjulian# 58214306Sjulian# +---------------+ +---------------------------------------------+ 59214306Sjulian# | n1 (vimage) | | n2 (vimage) | 60214306Sjulian# | | | | 61214306Sjulian# | +-----------+ | | +-----------+ +-----------+ +-----------+ | 62214306Sjulian# | | ngeth0 | | | | ngeth1 | | ngeth2 | | ngeth4 | | 63214306Sjulian# | |(ng_eiface)| | | |(ng_eiface)| |(ng_eiface)| |(ng_eiface)| | 64214306Sjulian# | +--+-----+--+ | | +--+-----+--+ +--+-----+--+ +--+-----+--+ | 65214306Sjulian# | |ether| | | |ether| |ether| |ether| | 66214306Sjulian# | +-X---+ | | +--X--+ +--X--+ +--X--+ | 67214306Sjulian# +-------X-------+ +------X--------------X---------------X-------+ 68214306Sjulian# X X X X 69214306Sjulian# X X X X 70214306Sjulian# XXXXXXXXXXXXXXX X X 71214306Sjulian# X X 72214306Sjulian# +--------X------+ +--------X------+ 73214306Sjulian# | -+--X--+- | | -+--X--+- | 74214306Sjulian# | |ether| | | |ether| | 75214306Sjulian# | +--+-----+--+ | | +--+-----+--+ | 76214306Sjulian# | | ngeth3 | | | | ngeth5 | | 77214306Sjulian# | |(ng_eiface)| | | |(ng_eiface)| | 78214306Sjulian# | +-----------+ | | +-----------+ | 79214306Sjulian# | | | | 80214306Sjulian# | n3 (vimage) | | n4 (vimage) | 81214306Sjulian# +---------------+ +---------------+ 82214306Sjulian# 83214306Sjulian# 84214306Sjulian# 85214306Sjulian 86222179Suqs# List the names of virtual nodes and their IP addresses. Use ':' 87222179Suqs# character to separate node name from node IP address and netmask. 88214306Sjulian 89214306SjulianTARGET_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" 90214306SjulianSTATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 91214306SjulianSTATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 92214306SjulianSTATIC_ROUTE2="jexec n4 route add -inet default 10.0.2.9" 93214306SjulianSTATIC_ROUTE_CNT=3 94214306Sjulian 95222179Suqs# MAC manufacturer prefix. This can be modified according to needs. 96222179SuqsMAC_PREFIX="00:1d:92" 97214306Sjulian 98222179Suqs# Temporary file is important for proper execution of script. 99214306SjulianTEMP_FILE="/var/tmp/.virtual.chain.tmp" 100214306Sjulian 101214306Sjulian# Set root directory for jails to be created. 102214306SjulianJAIL_PATH="/usr/jails/router" 103214306Sjulian 104214306Sjulian 105214306Sjulian#################################################################### 106214306Sjulian#### Nothing below this point should need to be modified. #### 107214306Sjulian#################################################################### 108214306Sjulian 109214306Sjulian 110214306Sjulian# Start/restart routine. 111214306Sjulianvirtual_chain_start() { 112214306Sjulian 113214306Sjulian # Load netgraph KLD's as necessary. 114222179Suqs 115214306Sjulian for KLD in ng_ether ng_bridge ng_eiface; do 116214306Sjulian if ! kldstat -v | grep -qw ${KLD}; then 117214306Sjulian echo -n "Loading ${KLD}.ko... " 118214306Sjulian kldload ${KLD} || exit 1 119214306Sjulian echo "done" 120214306Sjulian fi 121214306Sjulian done 122214306Sjulian 123214306Sjulian # Reset all interfaces and jails. If temporary file can not be found 124222179Suqs # script assumes that there is no previous configuration. 125222179Suqs 126214306Sjulian if [ ! -e ${TEMP_FILE} ]; then 127214306Sjulian echo "No previous configuration(${TEMP_FILE}) found to clean-up." 128214306Sjulian else 129214306Sjulian echo -n "Cleaning previous configuration..." 130214306Sjulian virtual_chain_stop 131214306Sjulian echo "done" 132222179Suqs fi 133214306Sjulian 134222179Suqs # Create temporary file for usage. This file includes generated 135214306Sjulian # interface names and jail names. All bridges, interfaces and jails 136222179Suqs # are written to file while created. In clean-up process written 137222179Suqs # objects are cleaned (i.e. removed) from system. 138222179Suqs 139214306Sjulian if [ -e ${TEMP_FILE} ]; then 140214306Sjulian touch ${TEMP_FILE} 141214306Sjulian fi 142214306Sjulian 143214306Sjulian 144214306Sjulian # Attach other interfaces as well. 145214306Sjulian for CONNECTION in ${TARGET_TOPOLOGY}; do 146222179Suqs 147214306Sjulian # Virtual connections are defined in TARGET_TOPOLOGY variable. 148214306Sjulian # They have the form of 'nodeName|IPaddr'. Below two lines split 149222179Suqs 150214306Sjulian PEER1=`echo ${CONNECTION} | awk -F"<->" '{print $1}'` 151214306Sjulian PEER1_NAME=`echo ${PEER1} | awk -F"|" '{print $1}'` 152214306Sjulian PEER1_IP=`echo ${PEER1} | awk -F"|" '{print $2}'` 153222179Suqs 154214306Sjulian PEER2=`echo ${CONNECTION} | awk -F"<->" '{print $2}'` 155214306Sjulian PEER2_NAME=`echo ${PEER2} | awk -F"|" '{print $1}'` 156214306Sjulian PEER2_IP=`echo ${PEER2} | awk -F"|" '{print $2}'` 157214306Sjulian 158214306Sjulian # !!! if not created already.. 159222179Suqs # Create virtual node (jail) with given name and using 160214306Sjulian # JAIL_PATH as root directory for jail. 161214306Sjulian 162214306Sjulian virtual_chain_create_peer_if_necessary ${PEER1_NAME} 163214306Sjulian virtual_chain_create_peer_if_necessary ${PEER2_NAME} 164214306Sjulian 165214306Sjulian # create an interface for peer with the given peer IP. Get interface 166222179Suqs # for future use; you will connect this interface to the other 167214306Sjulian # peers' (PEER2) interface. 168214306Sjulian virtual_chain_create_interface_with_ip ${PEER1_NAME} ${PEER1_IP} 169214306Sjulian PEER1_INTERFACE=${RET_INTERFACE} 170222179Suqs 171214306Sjulian # create an interface for peer with the given peer IP. Get interface 172222179Suqs # for future use; you will connect this interface to the other 173214306Sjulian # peers' (PEER2) interface. 174214306Sjulian virtual_chain_create_interface_with_ip ${PEER2_NAME} ${PEER2_IP} 175214306Sjulian PEER2_INTERFACE=${RET_INTERFACE} 176214306Sjulian 177214306Sjulian # Connect virtual interface to other interface. Syntax is : 178214306Sjulian # ngctl connect INTERFACE1: INTERFACE2: ether ether. 179222179Suqs 180214306Sjulian echo -n "Connecting ${PEER1_INTERFACE}:ether to ${PEER2_INTERFACE}:ether..." 181214306Sjulian ngctl connect ${PEER1_INTERFACE}: ${PEER2_INTERFACE}: ether ether \ 182214306Sjulian || exit 1 183214306Sjulian echo "done" 184214306Sjulian 185214306Sjulian done 186214306Sjulian 187214306Sjulian # Executes static route add commands. 188214306Sjulian i=0 189214306Sjulian while [ $i != $STATIC_ROUTE_CNT ]; do 190214306Sjulian eval ROUTE=\${STATIC_ROUTE${i}} 191214306Sjulian ret=`${ROUTE}` 192214306Sjulian i=`expr $i + 1` 193214306Sjulian done 194214306Sjulian 195222179Suqs echo "Virtual WAN established successfully!" 196214306Sjulian} 197214306Sjulian 198214306Sjulianvirtual_chain_create_interface_with_ip() { 199214306Sjulian 200214306Sjulian NODE_NAME=$1 201214306Sjulian NODE_IP=$2 202214306Sjulian 203222179Suqs # Create a ng_eiface object for virtual node. ng_eiface 204214306Sjulian # object has a hook that can be connected to one of bridge 205222179Suqs # links. After creating interface get its automatically 206222179Suqs # generated name for further usage. 207214306Sjulian 208214306Sjulian echo "Creating eiface interface for virtual node ${NODE_NAME}." 209214306Sjulian ngctl mkpeer eiface ether ether 210214306Sjulian EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'` 211222179Suqs echo "Interface ${EIFACE} is created." 212222179Suqs 213214306Sjulian # Write name of the interface to temp file. Clean-up procedure 214214306Sjulian # will use this name to shutdown interface. 215222179Suqs 216214306Sjulian echo "interface ${EIFACE}" >> ${TEMP_FILE} 217214306Sjulian 218222179Suqs # Move virtual interface to virtual node. Note that Interface 219214306Sjulian # name will not be changed at the end of this movement. Moved 220214306Sjulian # interface can be seen at the output of ifconfig command in 221214306Sjulian # jail: 'jexec jailname ifconfig' 222214306Sjulian 223222179Suqs echo "Moving ${EIFACE} to ${NODE_NAME}" 224214306Sjulian ifconfig ${EIFACE} vnet ${NODE_NAME} 225222179Suqs 226214306Sjulian # Make lo0 interface localhost. 227214306Sjulian jexec ${NODE_NAME} ifconfig lo0 localhost 228214306Sjulian 229214306Sjulian # Generate a random mac address for virtual interface. First 230214306Sjulian # three octets can be changed by user. Last three octets are 231222179Suqs # generated randomly. 232214306Sjulian M4=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 233214306Sjulian awk '{ print $1 % 256 }'` 234214306Sjulian M5=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 235214306Sjulian awk '{ print $1 % 256 }'` 236214306Sjulian M6=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 237214306Sjulian awk '{ print $1 % 256 }'` 238214306Sjulian 239214306Sjulian MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}` 240214306Sjulian 241214306Sjulian # Set the link address (mac address) of virtual interface in 242214306Sjulian # virtual node to randomly generated MAC. 243214306Sjulian echo "Setting MAC address of ${EIFACE} to '${MAC}'" 244214306Sjulian jexec ${NODE_NAME} ifconfig ${EIFACE} link $MAC 245214306Sjulian 246222179Suqs # Either IPv4 or IPv6 can be used in this script. Ifconfig 247214306Sjulian # IP setting syntax differs slightly for two IP versions. 248214306Sjulian # For version 4 'inet' keyword is used whereas for version 6 249214306Sjulian # 'inet6' is used. Below line tries to decide which IP version 250222179Suqs # is given and sets IPVER to 'inet' or 'inet6'. 251214306Sjulian 252214306Sjulian IPVER=`echo ${NODE_IP} | awk -F"." '{ split($4,last,"/"); \ 253214306Sjulian if( NF==4 && $1>0 && $1<256 && $2<256 && $3<256 && \ 254214306Sjulian last[1]<256) print "inet"; else print "inet6"}'` 255222179Suqs 256214306Sjulian # Set IP address of virtual interface in virtual node. 257214306Sjulian echo "Setting IP address of ${EIFACE} to '${NODE_IP}'" 258214306Sjulian jexec ${NODE_NAME} ifconfig ${EIFACE} ${IPVER} ${NODE_IP} 259214306Sjulian 260214306Sjulian RET_INTERFACE=${EIFACE} 261214306Sjulian} 262214306Sjulian 263214306Sjulianvirtual_chain_create_peer_if_necessary() { 264214306Sjulian 265214306Sjulian if ! grep -q $1 ${TEMP_FILE} ; then 266222179Suqs 267222179Suqs echo -n "Creating virtual node (jail) ${1}..." 268214306Sjulian jail -c vnet name=${1} host.hostname=${1} \ 269222179Suqs path=${JAIL_PATH} persist 270214306Sjulian jexec ${1} sysctl -w net.inet.ip.forwarding=1 271214306Sjulian jexec ${1} sysctl -w net.inet6.ip6.forwarding=1 272214306Sjulian echo "done" 273222179Suqs 274222179Suqs # Write name of the jail to temp file. Clean-up 275214306Sjulian # procedure will use this name to remove jail. 276222179Suqs 277214306Sjulian echo "node ${1}" >> ${TEMP_FILE} 278214306Sjulian fi 279214306Sjulian 280214306Sjulian} 281214306Sjulian 282214306Sjulian# Stop routine. 283214306Sjulianvirtual_chain_stop() { 284214306Sjulian 285214306Sjulian if [ ! -e ${TEMP_FILE} ]; then 286214306Sjulian echo "Nothing to stop! ${TEMP_FILE}: temp file not found" 287214306Sjulian else 288214306Sjulian 289222179Suqs echo -n "Shutdown bridge interface.." 290214306Sjulian OBJECTS=`cat ${TEMP_FILE} | grep bridge | awk '{print $2}'` 291214306Sjulian for BRIDGE in ${OBJECTS}; do 292214306Sjulian ngctl shutdown ${BRIDGE}: >/dev/null 2>&1 293214306Sjulian done 294214306Sjulian echo "done" 295222179Suqs 296222179Suqs echo -n "Shutdown all eiface interfaces..." 297214306Sjulian OBJECTS=`cat ${TEMP_FILE} | grep interface | awk '{print $2}'` 298214306Sjulian for INTERFACE in ${OBJECTS}; do 299214306Sjulian ngctl shutdown ${INTERFACE}: >/dev/null 2>&1 300214306Sjulian done 301214306Sjulian echo "done" 302222179Suqs 303222179Suqs echo -n "Removing all jails..." 304214306Sjulian OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'` 305214306Sjulian for NODE in ${OBJECTS}; do 306214306Sjulian jail -r ${NODE} 307214306Sjulian done 308214306Sjulian echo "done" 309222179Suqs 310222179Suqs echo "Removing tempfile ${TEMP_FILE}" 311214306Sjulian rm ${TEMP_FILE} 312214306Sjulian fi 313222179Suqs echo "Virtual LAN objects removed successfully!" 314214306Sjulian 315214306Sjulian} 316214306Sjulian 317214306Sjulianvirtual_chain_usage() { 318214306Sjulian echo "usage: $0 start [target_topology]" 319214306Sjulian echo " : $0 [ stop | help ]" 320214306Sjulian} 321214306Sjulian 322214306Sjulian 323214306Sjulian# Main entry point. 324214306Sjulian 325214306Sjuliancase $# in 326214306Sjulian 1) 327214306Sjulian case $1 in 328214306Sjulian start) 329214306Sjulian echo -n "Creating default target topology:" 330214306Sjulian echo " ${TARGET_TOPOLOGY}" 331214306Sjulian virtual_chain_start 332214306Sjulian ;; 333214306Sjulian stop) 334214306Sjulian 335214306Sjulian if [ ! -e ${TEMP_FILE} ]; then 336214306Sjulian echo -n "Noting to stop! ${TEMP_FILE}:" 337214306Sjulian echo " temp file not found" 338214306Sjulian else 339214306Sjulian virtual_chain_stop 340222179Suqs fi 341214306Sjulian ;; 342214306Sjulian help) 343214306Sjulian virtual_chain_usage 344214306Sjulian exit 1 345214306Sjulian ;; 346214306Sjulian *) 347214306Sjulian virtual_chain_usage 348214306Sjulian exit 1 349214306Sjulian 350214306Sjulian esac 351214306Sjulian ;; 352214306Sjulian 2) 353214306Sjulian case $1 in 354214306Sjulian start) 355214306Sjulian TARGET_TOPOLOGY=$2 356214306Sjulian echo -n "Creating target topology:" 357214306Sjulian echo "${TARGET_TOPOLOGY}" 358214306Sjulian virtual_chain_start 359214306Sjulian ;; 360214306Sjulian *) 361214306Sjulian virtual_chain_usage 362214306Sjulian exit 1 363214306Sjulian esac 364214306Sjulian ;; 365214306Sjulian 366214306Sjulian *) 367214306Sjulian virtual_chain_usage 368214306Sjulian exit 1 369214306Sjulianesac 370214306Sjulian 371