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