virtual.chain revision 214306
1214306Sjulian#!/bin/sh 2214306Sjulian# 3214306Sjulian# 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: head/share/examples/netgraph/virtual.chain 214306 2010-10-24 22:59:38Z julian $ 16214306Sjulian# 17214306Sjulian# 18214306Sjulian# This script creates and connects n router like nodes. Complex wide 19214306Sjulian# area topologies can be created with the help of script. 20214306Sjulian# 21214306Sjulian# Virtual nodes are generated via jails and network connections are 22214306Sjulian# established using ng_eiface(4) node types. 23214306Sjulian# 24214306Sjulian# To use this script: 25214306Sjulian# 26214306Sjulian# 0. Make your own copy of this example script. 27214306Sjulian# 28214306Sjulian# 1. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual 29214306Sjulian# nodes. Virtual topology definition includes node names and their 30214306Sjulian# IP address. Target top. sytax: ( name|ip<->name|ip ... ) 31214306Sjulian# Example 1: ( n1|10.0.2.1/30<->n2|10.0.2.2/30 ...) 32214306Sjulian# Example 2: ( n1|2001:b90::14a/125<->n1|2001:b90::14b/125 ...) 33214306Sjulian# 34214306Sjulian# 2. Run this script with "start" as the command line argument. 35214306Sjulian# 36214306Sjulian# 3. Add necessary static route commands for each virtual node. For 37214306Sjulian# example assume you have three virtual nodes connected each other 38214306Sjulian# llike a chain ( n1 is connected to n2, n2 is connecte to n3 ). 39214306Sjulian# In order to estabklish connectivity among these virtual nodes, 40214306Sjulian# you have to add default routes to node n1 and node n3. Example 41214306Sjulian# static route command is: 42214306Sjulian# STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 43214306Sjulian# STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 44214306Sjulian# After defining default routes with above format you have to set 45214306Sjulian# the total number of static route commands as: 46214306Sjulian# STATIC_ROUTE_CNT=2 47214306Sjulian# 48214306Sjulian# 4. Stop bridging by running this script with "stop" as the 49214306Sjulian# command line argument. 50214306Sjulian# 51214306Sjulian# 5. This cript uses a template file in order to carry information 52214306Sjulian# between start and stop calls. 53214306Sjulian# In the start call, the netgraph interfaces and jails are created. 54214306Sjulian# At the stop phase, all created objects should be removed. 55214306Sjulian# DO NOT delete the temporary file between the start and stop phases. 56214306Sjulian# 57214306Sjulian# Target Topology: 58214306Sjulian# 59214306Sjulian# +---------------+ +---------------------------------------------+ 60214306Sjulian# | n1 (vimage) | | n2 (vimage) | 61214306Sjulian# | | | | 62214306Sjulian# | +-----------+ | | +-----------+ +-----------+ +-----------+ | 63214306Sjulian# | | ngeth0 | | | | ngeth1 | | ngeth2 | | ngeth4 | | 64214306Sjulian# | |(ng_eiface)| | | |(ng_eiface)| |(ng_eiface)| |(ng_eiface)| | 65214306Sjulian# | +--+-----+--+ | | +--+-----+--+ +--+-----+--+ +--+-----+--+ | 66214306Sjulian# | |ether| | | |ether| |ether| |ether| | 67214306Sjulian# | +-X---+ | | +--X--+ +--X--+ +--X--+ | 68214306Sjulian# +-------X-------+ +------X--------------X---------------X-------+ 69214306Sjulian# X X X X 70214306Sjulian# X X X X 71214306Sjulian# XXXXXXXXXXXXXXX X X 72214306Sjulian# X X 73214306Sjulian# +--------X------+ +--------X------+ 74214306Sjulian# | -+--X--+- | | -+--X--+- | 75214306Sjulian# | |ether| | | |ether| | 76214306Sjulian# | +--+-----+--+ | | +--+-----+--+ | 77214306Sjulian# | | ngeth3 | | | | ngeth5 | | 78214306Sjulian# | |(ng_eiface)| | | |(ng_eiface)| | 79214306Sjulian# | +-----------+ | | +-----------+ | 80214306Sjulian# | | | | 81214306Sjulian# | n3 (vimage) | | n4 (vimage) | 82214306Sjulian# +---------------+ +---------------+ 83214306Sjulian# 84214306Sjulian# 85214306Sjulian# 86214306Sjulian 87214306Sjulian# List the names of virtual nodes and their IP addresses. Use ':' 88214306Sjulian# character to seperate node name from node IP address and netmask. 89214306Sjulian 90214306SjulianTARGET_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" 91214306SjulianSTATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2" 92214306SjulianSTATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5" 93214306SjulianSTATIC_ROUTE2="jexec n4 route add -inet default 10.0.2.9" 94214306SjulianSTATIC_ROUTE_CNT=3 95214306Sjulian 96214306Sjulian# MAC manifacturer prefix. This can be modified according to needs. 97214306SjulianMAC_PREFIX="00:1d:92" 98214306Sjulian 99214306Sjulian# Temporary file is important for proper execution of script. 100214306SjulianTEMP_FILE="/var/tmp/.virtual.chain.tmp" 101214306Sjulian 102214306Sjulian# Set root directory for jails to be created. 103214306SjulianJAIL_PATH="/usr/jails/router" 104214306Sjulian 105214306Sjulian 106214306Sjulian#################################################################### 107214306Sjulian#### Nothing below this point should need to be modified. #### 108214306Sjulian#################################################################### 109214306Sjulian 110214306Sjulian 111214306Sjulian# Start/restart routine. 112214306Sjulianvirtual_chain_start() { 113214306Sjulian 114214306Sjulian # Load netgraph KLD's as necessary. 115214306Sjulian 116214306Sjulian for KLD in ng_ether ng_bridge ng_eiface; do 117214306Sjulian if ! kldstat -v | grep -qw ${KLD}; then 118214306Sjulian echo -n "Loading ${KLD}.ko... " 119214306Sjulian kldload ${KLD} || exit 1 120214306Sjulian echo "done" 121214306Sjulian fi 122214306Sjulian done 123214306Sjulian 124214306Sjulian # Reset all interfaces and jails. If temporary file can not be found 125214306Sjulian # script assumes that there is no previous configuration. 126214306Sjulian 127214306Sjulian if [ ! -e ${TEMP_FILE} ]; then 128214306Sjulian echo "No previous configuration(${TEMP_FILE}) found to clean-up." 129214306Sjulian else 130214306Sjulian echo -n "Cleaning previous configuration..." 131214306Sjulian virtual_chain_stop 132214306Sjulian echo "done" 133214306Sjulian fi 134214306Sjulian 135214306Sjulian # Create temporary file for usage. This file includes generated 136214306Sjulian # interface names and jail names. All bridges, interfaces and jails 137214306Sjulian # are written to file while created. In clean-up process written 138214306Sjulian # objects are cleaned (i.e removed) from system. 139214306Sjulian 140214306Sjulian if [ -e ${TEMP_FILE} ]; then 141214306Sjulian touch ${TEMP_FILE} 142214306Sjulian fi 143214306Sjulian 144214306Sjulian 145214306Sjulian # Attach other interfaces as well. 146214306Sjulian for CONNECTION in ${TARGET_TOPOLOGY}; do 147214306Sjulian 148214306Sjulian # Virtual connections are defined in TARGET_TOPOLOGY variable. 149214306Sjulian # They have the form of 'nodeName|IPaddr'. Below two lines split 150214306Sjulian 151214306Sjulian PEER1=`echo ${CONNECTION} | awk -F"<->" '{print $1}'` 152214306Sjulian PEER1_NAME=`echo ${PEER1} | awk -F"|" '{print $1}'` 153214306Sjulian PEER1_IP=`echo ${PEER1} | awk -F"|" '{print $2}'` 154214306Sjulian 155214306Sjulian PEER2=`echo ${CONNECTION} | awk -F"<->" '{print $2}'` 156214306Sjulian PEER2_NAME=`echo ${PEER2} | awk -F"|" '{print $1}'` 157214306Sjulian PEER2_IP=`echo ${PEER2} | awk -F"|" '{print $2}'` 158214306Sjulian 159214306Sjulian # !!! if not created already.. 160214306Sjulian # Create virtual node (jail) with given name and using 161214306Sjulian # JAIL_PATH as root directory for jail. 162214306Sjulian 163214306Sjulian virtual_chain_create_peer_if_necessary ${PEER1_NAME} 164214306Sjulian virtual_chain_create_peer_if_necessary ${PEER2_NAME} 165214306Sjulian 166214306Sjulian # create an interface for peer with the given peer IP. Get interface 167214306Sjulian # for future use; you will connect this interface to the other 168214306Sjulian # peers' (PEER2) interface. 169214306Sjulian virtual_chain_create_interface_with_ip ${PEER1_NAME} ${PEER1_IP} 170214306Sjulian PEER1_INTERFACE=${RET_INTERFACE} 171214306Sjulian 172214306Sjulian # create an interface for peer with the given peer IP. Get interface 173214306Sjulian # for future use; you will connect this interface to the other 174214306Sjulian # peers' (PEER2) interface. 175214306Sjulian virtual_chain_create_interface_with_ip ${PEER2_NAME} ${PEER2_IP} 176214306Sjulian PEER2_INTERFACE=${RET_INTERFACE} 177214306Sjulian 178214306Sjulian # Connect virtual interface to other interface. Syntax is : 179214306Sjulian # ngctl connect INTERFACE1: INTERFACE2: ether ether. 180214306Sjulian 181214306Sjulian echo -n "Connecting ${PEER1_INTERFACE}:ether to ${PEER2_INTERFACE}:ether..." 182214306Sjulian ngctl connect ${PEER1_INTERFACE}: ${PEER2_INTERFACE}: ether ether \ 183214306Sjulian || exit 1 184214306Sjulian echo "done" 185214306Sjulian 186214306Sjulian done 187214306Sjulian 188214306Sjulian # Executes static route add commands. 189214306Sjulian i=0 190214306Sjulian while [ $i != $STATIC_ROUTE_CNT ]; do 191214306Sjulian eval ROUTE=\${STATIC_ROUTE${i}} 192214306Sjulian ret=`${ROUTE}` 193214306Sjulian i=`expr $i + 1` 194214306Sjulian done 195214306Sjulian 196214306Sjulian echo "Virtual WAN established succesfully!" 197214306Sjulian} 198214306Sjulian 199214306Sjulianvirtual_chain_create_interface_with_ip() { 200214306Sjulian 201214306Sjulian NODE_NAME=$1 202214306Sjulian NODE_IP=$2 203214306Sjulian 204214306Sjulian # Create a ng_eiface object for virtual node. ng_eiface 205214306Sjulian # object has a hook that can be connected to one of bridge 206214306Sjulian # links. After creating interface get its automatically 207214306Sjulian # generated name for further usage. 208214306Sjulian 209214306Sjulian echo "Creating eiface interface for virtual node ${NODE_NAME}." 210214306Sjulian ngctl mkpeer eiface ether ether 211214306Sjulian EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'` 212214306Sjulian echo "Interface ${EIFACE} is created." 213214306Sjulian 214214306Sjulian # Write name of the interface to temp file. Clean-up procedure 215214306Sjulian # will use this name to shutdown interface. 216214306Sjulian 217214306Sjulian echo "interface ${EIFACE}" >> ${TEMP_FILE} 218214306Sjulian 219214306Sjulian # Move virtual interface to virtual node. Note that Interface 220214306Sjulian # name will not be changed at the end of this movement. Moved 221214306Sjulian # interface can be seen at the output of ifconfig command in 222214306Sjulian # jail: 'jexec jailname ifconfig' 223214306Sjulian 224214306Sjulian echo "Moving ${EIFACE} to ${NODE_NAME}" 225214306Sjulian ifconfig ${EIFACE} vnet ${NODE_NAME} 226214306Sjulian 227214306Sjulian # Make lo0 interface localhost. 228214306Sjulian jexec ${NODE_NAME} ifconfig lo0 localhost 229214306Sjulian 230214306Sjulian # Generate a random mac address for virtual interface. First 231214306Sjulian # three octets can be changed by user. Last three octets are 232214306Sjulian # generated randomly. 233214306Sjulian M4=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 234214306Sjulian awk '{ print $1 % 256 }'` 235214306Sjulian M5=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 236214306Sjulian awk '{ print $1 % 256 }'` 237214306Sjulian M6=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \ 238214306Sjulian awk '{ print $1 % 256 }'` 239214306Sjulian 240214306Sjulian MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}` 241214306Sjulian 242214306Sjulian # Set the link address (mac address) of virtual interface in 243214306Sjulian # virtual node to randomly generated MAC. 244214306Sjulian echo "Setting MAC address of ${EIFACE} to '${MAC}'" 245214306Sjulian jexec ${NODE_NAME} ifconfig ${EIFACE} link $MAC 246214306Sjulian 247214306Sjulian # Either IPv4 or IPv6 can be used in this script. Ifconfig 248214306Sjulian # IP setting syntax differs slightly for two IP versions. 249214306Sjulian # For version 4 'inet' keyword is used whereas for version 6 250214306Sjulian # 'inet6' is used. Below line tries to decide which IP version 251214306Sjulian # is given and sets IPVER to 'inet' or 'inet6'. 252214306Sjulian 253214306Sjulian IPVER=`echo ${NODE_IP} | awk -F"." '{ split($4,last,"/"); \ 254214306Sjulian if( NF==4 && $1>0 && $1<256 && $2<256 && $3<256 && \ 255214306Sjulian last[1]<256) print "inet"; else print "inet6"}'` 256214306Sjulian 257214306Sjulian # Set IP address of virtual interface in virtual node. 258214306Sjulian echo "Setting IP address of ${EIFACE} to '${NODE_IP}'" 259214306Sjulian jexec ${NODE_NAME} ifconfig ${EIFACE} ${IPVER} ${NODE_IP} 260214306Sjulian 261214306Sjulian RET_INTERFACE=${EIFACE} 262214306Sjulian} 263214306Sjulian 264214306Sjulianvirtual_chain_create_peer_if_necessary() { 265214306Sjulian 266214306Sjulian if ! grep -q $1 ${TEMP_FILE} ; then 267214306Sjulian 268214306Sjulian echo -n "Creating virtual node (jail) ${1}..." 269214306Sjulian jail -c vnet name=${1} host.hostname=${1} \ 270214306Sjulian path=${JAIL_PATH} persist 271214306Sjulian jexec ${1} sysctl -w net.inet.ip.forwarding=1 272214306Sjulian jexec ${1} sysctl -w net.inet6.ip6.forwarding=1 273214306Sjulian echo "done" 274214306Sjulian 275214306Sjulian # Write name of the jail to temp file. Clean-up 276214306Sjulian # procedure will use this name to remove jail. 277214306Sjulian 278214306Sjulian echo "node ${1}" >> ${TEMP_FILE} 279214306Sjulian fi 280214306Sjulian 281214306Sjulian} 282214306Sjulian 283214306Sjulian 284214306Sjulian# Stop routine. 285214306Sjulianvirtual_chain_stop() { 286214306Sjulian 287214306Sjulian if [ ! -e ${TEMP_FILE} ]; then 288214306Sjulian echo "Nothing to stop! ${TEMP_FILE}: temp file not found" 289214306Sjulian else 290214306Sjulian 291214306Sjulian echo -n "Shutdown bridge interface.." 292214306Sjulian OBJECTS=`cat ${TEMP_FILE} | grep bridge | awk '{print $2}'` 293214306Sjulian for BRIDGE in ${OBJECTS}; do 294214306Sjulian ngctl shutdown ${BRIDGE}: >/dev/null 2>&1 295214306Sjulian done 296214306Sjulian echo "done" 297214306Sjulian 298214306Sjulian echo -n "Shutdown all eiface interfaces..." 299214306Sjulian OBJECTS=`cat ${TEMP_FILE} | grep interface | awk '{print $2}'` 300214306Sjulian for INTERFACE in ${OBJECTS}; do 301214306Sjulian ngctl shutdown ${INTERFACE}: >/dev/null 2>&1 302214306Sjulian done 303214306Sjulian echo "done" 304214306Sjulian 305214306Sjulian echo -n "Removing all jails..." 306214306Sjulian OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'` 307214306Sjulian for NODE in ${OBJECTS}; do 308214306Sjulian jail -r ${NODE} 309214306Sjulian done 310214306Sjulian echo "done" 311214306Sjulian 312214306Sjulian echo "Removing tempfile ${TEMP_FILE}" 313214306Sjulian rm ${TEMP_FILE} 314214306Sjulian fi 315214306Sjulian echo "Virtual LAN objects removed succesfully!" 316214306Sjulian 317214306Sjulian} 318214306Sjulian 319214306Sjulianvirtual_chain_usage() { 320214306Sjulian echo "usage: $0 start [target_topology]" 321214306Sjulian echo " : $0 [ stop | help ]" 322214306Sjulian} 323214306Sjulian 324214306Sjulian 325214306Sjulian# Main entry point. 326214306Sjulian 327214306Sjulian 328214306Sjuliancase $# in 329214306Sjulian 1) 330214306Sjulian case $1 in 331214306Sjulian start) 332214306Sjulian echo -n "Creating default target topology:" 333214306Sjulian echo " ${TARGET_TOPOLOGY}" 334214306Sjulian virtual_chain_start 335214306Sjulian ;; 336214306Sjulian stop) 337214306Sjulian 338214306Sjulian if [ ! -e ${TEMP_FILE} ]; then 339214306Sjulian echo -n "Noting to stop! ${TEMP_FILE}:" 340214306Sjulian echo " temp file not found" 341214306Sjulian else 342214306Sjulian virtual_chain_stop 343214306Sjulian fi 344214306Sjulian ;; 345214306Sjulian help) 346214306Sjulian virtual_chain_usage 347214306Sjulian exit 1 348214306Sjulian ;; 349214306Sjulian *) 350214306Sjulian virtual_chain_usage 351214306Sjulian exit 1 352214306Sjulian 353214306Sjulian esac 354214306Sjulian ;; 355214306Sjulian 2) 356214306Sjulian case $1 in 357214306Sjulian start) 358214306Sjulian TARGET_TOPOLOGY=$2 359214306Sjulian echo -n "Creating target topology:" 360214306Sjulian echo "${TARGET_TOPOLOGY}" 361214306Sjulian virtual_chain_start 362214306Sjulian ;; 363214306Sjulian *) 364214306Sjulian virtual_chain_usage 365214306Sjulian exit 1 366214306Sjulian esac 367214306Sjulian ;; 368214306Sjulian 369214306Sjulian *) 370214306Sjulian virtual_chain_usage 371214306Sjulian exit 1 372214306Sjulianesac 373214306Sjulian 374