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