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#
17222179Suqs# This script adds virtual nodes to one of the physical interfaces
18214306Sjulian# visible on your local area network (LAN). Virtual nodes seems real
19222179Suqs# to external observers.
20214306Sjulian# If traceroute is executed to one of virtual nodes, the IP
21222179Suqs# address of the physical interface will not be seen in the output.
22214306Sjulian# Virtual nodes are generated via jails and network connections are
23214306Sjulian# established using ng_bridge(4) and ng_eiface(4) node types.
24214306Sjulian#
25214306Sjulian# To use this script:
26214306Sjulian#
27214306Sjulian# 0. Make your own copy of this example script.
28214306Sjulian#
29214306Sjulian# 1. Edit the definition of ${ETHER_INTF} as described below
30214306Sjulian#    to define your real interface connected to the LAN. Virtual nodes
31222179Suqs#    will placed on the same physical network as this interface.
32214306Sjulian#
33222179Suqs# 2. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual
34214306Sjulian#    nodes. Virtual topology definition includes node names and their
35222179Suqs#    IP address. Target top. syntax: ( node1|ip1/24 node2|ip2/24 ... )
36214306Sjulian#    Example 1: ( n1|122.122.122.12/24, n2|122.122.122.13/24 ...)
37214306Sjulian#    Example 2: ( n1|2001:b90::14a/125, n1|2001:b90::14b/125 ...)
38214306Sjulian#
39214306Sjulian# 3. Run this script with "start" as the command line argument.
40214306Sjulian#
41214306Sjulian# 4. Stop bridging by running this script with "stop" as the
42214306Sjulian#    command line argument.
43222179Suqs#
44222179Suqs# 5. This script uses a template file in order to carry information
45222179Suqs#    between start and stop calls.
46214306Sjulian#      In the start call, the netgraph interfaces and jails are created.
47214306Sjulian#      At the stop phase, all created objects should be removed.
48214306Sjulian#    DO NOT delete the temporary file between the start and stop phases.
49214306Sjulian#
50214306Sjulian# To add virtual nodes for multiple independent LANs, create multiple
51214306Sjulian# copies of this script with different variable definitions.
52214306Sjulian#
53214306Sjulian# Target Topology:
54214306Sjulian#
55214306Sjulian#
56214306Sjulian#                 +---------------+ +---------------+ +---------------+
57214306Sjulian#                 |  n0 (vimage)  | |  n1 (vimage)  | |  nk (vimage)  |
58214306Sjulian#                 |               | |               | |               |
59214306Sjulian#                 | +-----------+ | | +-----------+ | | +-----------+ |
60214306Sjulian#                 | |  ngeth0   | | | |  ngeth1   | | | |  ngethk   | |
61214306Sjulian#                 | |(ng_eiface)| | | |(ng_eiface)| | | |(ng_eiface)| |
62214306Sjulian#                 | +--+-----+--+ | | +--+-----+--+ | | +--+-----+--+ |
63214306Sjulian#                 |    |ether|    | |    |ether|    | |    |ether|    |
64214306Sjulian#                 |    +--X--+    | |    +--X--+    | |    +---X-+    |
65214306Sjulian#   +-----+       +--------\------+ +--------\------+ +-------/-------+
66214306Sjulian#   |upper|----\            \ip_addr          \ip_addr       /ip_addr
67214306Sjulian# +-+-----+--+  \            \                 \             \
68214306Sjulian# |   em0    |   \            +--------+        +-+           \
69214306Sjulian# |(ng_ether)|    +-----------+         \          \           \
70214306Sjulian# +-+-----+--+                 \         \          /           \
71214306Sjulian#   |lower|    +---------\      \         \        /            /
72214306Sjulian#   +--X--+   /        O--X--O O-X---O O---X-O O--X--O     O---X---O
73214306Sjulian#      \      |        |link0| |link1| |link2| |link3|     |linkk+2|
74214306Sjulian#       \     /      +-O-----O-O-----O-O-----O-O-----O-----O-------O-+
75214306Sjulian#        +---+       |                                               |
76214306Sjulian#                    |          bridge (ng_bridge)                   |
77214306Sjulian#                    +-----------------------------------------------+
78214306Sjulian#
79214306Sjulian#
80214306Sjulian
81214306Sjulian# Give the name of ethernet interface. Virtual nodes will be seen as
82222179Suqs# local neighbours of this interface.
83214306Sjulian
84214306SjulianETHER_INTF="em0"
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="c1|10.0.2.20/24 c2|10.0.2.21/24 c3|10.0.2.22/24"
90214306Sjulian
91222179Suqs# MAC manufacturer prefix. This can be modified according to needs.
92222179SuqsMAC_PREFIX="00:1d:92"
93214306Sjulian
94222179Suqs# Temporary file is important for proper execution of script.
95214306SjulianTEMP_FILE="/var/tmp/.virtual.lan.tmp"
96214306Sjulian
97214306Sjulian# Set root directory for jails to be created.
98214306SjulianJAIL_PATH="/usr/jails/node"
99214306Sjulian
100214306Sjulian
101214306Sjulian####################################################################
102214306Sjulian####    Nothing below this point should need to be modified.    ####
103214306Sjulian####################################################################
104214306Sjulian
105214306Sjulian
106214306Sjulian# Start/restart routine.
107214306Sjulianvirtual_lan_start() {
108214306Sjulian
109214306Sjulian	# Load netgraph KLD's as necessary.
110222179Suqs
111214306Sjulian	for KLD in ng_ether ng_bridge ng_eiface; do
112214306Sjulian		if ! kldstat -v | grep -qw ${KLD}; then
113214306Sjulian			echo -n "Loading ${KLD}.ko... "
114214306Sjulian			kldload ${KLD} || exit 1
115214306Sjulian			echo "done"
116214306Sjulian		fi
117214306Sjulian	done
118214306Sjulian
119214306Sjulian	# Reset all interfaces and jails. If temporary file can not be found
120222179Suqs	# script assumes that there is no previous configuration.
121222179Suqs
122214306Sjulian	if [ ! -e ${TEMP_FILE} ]; then
123214306Sjulian		echo "No previous configuration(${TEMP_FILE}) found to clean-up."
124214306Sjulian	else
125214306Sjulian		echo -n "Cleaning previous configuration..."
126214306Sjulian		virtual_lan_stop
127214306Sjulian		echo "done"
128222179Suqs	fi
129214306Sjulian
130222179Suqs	# Create temporary file for usage. This file includes generated
131214306Sjulian	# interface names and jail names. All bridges, interfaces and jails
132222179Suqs	# are written to file while created. In clean-up process written
133222179Suqs	# objects are cleaned (i.e. removed) from system.
134222179Suqs
135214306Sjulian	if [ -e ${TEMP_FILE} ]; then
136214306Sjulian		touch ${TEMP_FILE}
137214306Sjulian	fi
138214306Sjulian
139214306Sjulian	echo -n "Verifying ethernet interface existence..."
140214306Sjulian	# Verify ethernet interface exist.
141214306Sjulian	if ! ngctl info ${ETHER_INTF}: >/dev/null 2>&1; then
142214306Sjulian		echo "Error: interface ${ETHER_INTF} does not exist"
143214306Sjulian		exit 1
144214306Sjulian	fi
145214306Sjulian	ifconfig ${ETHER_INTF} up || exit 1
146214306Sjulian	echo "done"
147214306Sjulian
148214306Sjulian	# Get current number of bridge interfaces in the system. This number
149214306Sjulian	# is used to create a name for new bridge.
150214306Sjulian	BRIDGE_COUNT=`ngctl l | grep bridge | wc -l | sed -e "s/ //g"`
151214306Sjulian	BRIDGE_NAME="bridge${BRIDGE_COUNT}"
152222179Suqs
153214306Sjulian	# Create new ng_bridge(4) node and attach it to the ethernet interface.
154214306Sjulian	# Connect ng_ether:lower hook to bridge:link0 when creating bridge and
155214306Sjulian	# connect ng_ether:upper hook to bridge:link1 after bridge name is set.
156222179Suqs
157214306Sjulian	echo "Creating bridge interface: ${BRIDGE_NAME}..."
158214306Sjulian	ngctl mkpeer ${ETHER_INTF}: bridge lower link0 || exit 1
159214306Sjulian	ngctl name ${ETHER_INTF}:lower ${BRIDGE_NAME} || exit 1
160214306Sjulian	ngctl connect ${ETHER_INTF}: ${BRIDGE_NAME}: upper link1 || exit 1
161214306Sjulian	echo "Bridge ${BRIDGE_NAME} is created and ${ETHER_INTF} is connected."
162222179Suqs
163214306Sjulian	# In the above code block two hooks are connected to bridge interface,
164222179Suqs	# therefore LINKNUM is set to 2 indicating total number of connected
165222179Suqs	# hooks on the bridge interface.
166214306Sjulian	LINKNUM=2
167214306Sjulian
168214306Sjulian	# Write name of the bridge to temp file. Clean-up procedure will use
169214306Sjulian	# this name to shutdown bridge interface.
170214306Sjulian	echo "bridge ${BRIDGE_NAME}" > ${TEMP_FILE}
171214306Sjulian
172214306Sjulian
173214306Sjulian	# Attach other interfaces as well.
174214306Sjulian	for NODE in ${TARGET_TOPOLOGY}; do
175222179Suqs
176214306Sjulian		# Virtual nodes are defined in TARGET_TOPOLOGY variable. They
177214306Sjulian		# have the form of 'nodeName|IPaddr'. Below two lines split
178214306Sjulian		# node definition to get node name and node IP.
179214306Sjulian
180214306Sjulian		NODE_NAME=`echo ${NODE} | awk -F"|" '{print $1}'`
181214306Sjulian		NODE_IP=`echo ${NODE} | awk -F"|" '{print $2}'`
182214306Sjulian
183222179Suqs		# Create virtual node (jail) with given name and using
184214306Sjulian		# JAIL_PATH as root directory for jail.
185214306Sjulian
186222179Suqs		echo -n "Creating virtual node (jail) ${NODE_NAME}..."
187214306Sjulian		jail -c vnet name=${NODE_NAME} host.hostname=${NODE_NAME} \
188222179Suqs			path=${JAIL_PATH} persist
189214306Sjulian		echo "done"
190222179Suqs
191222179Suqs		# Write name of the jail to temp file. Clean-up procedure will
192214306Sjulian		# use this name to remove jail.
193222179Suqs
194214306Sjulian		echo "node ${NODE_NAME}" >> ${TEMP_FILE}
195214306Sjulian
196222179Suqs		# Create a ng_eiface object for virtual node. ng_eiface
197214306Sjulian		# object has a hook that can be connected to one of bridge
198222179Suqs		# links. After creating interface get its automatically
199222179Suqs		# generated name for further usage.
200214306Sjulian
201214306Sjulian		echo "Creating eiface interface for virtual node ${NODE_NAME}."
202214306Sjulian		ngctl mkpeer eiface ether ether
203214306Sjulian		EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'`
204222179Suqs		echo "Interface ${EIFACE} is created."
205222179Suqs
206214306Sjulian		# Write name of the interface to temp file. Clean-up procedure
207214306Sjulian		# will use this name to shutdown interface.
208222179Suqs
209214306Sjulian		echo "interface ${EIFACE}" >> ${TEMP_FILE}
210222179Suqs
211222179Suqs		# Move virtual interface to virtual node. Note that Interface
212214306Sjulian		# name will not be changed at the end of this movement. Moved
213214306Sjulian		# interface can be seen at the output of ifconfig command in
214214306Sjulian		# jail: 'jexec jailname ifconfig'
215214306Sjulian
216222179Suqs		echo "Moving ${EIFACE} to ${NODE_NAME}"
217214306Sjulian		ifconfig ${EIFACE} vnet ${NODE_NAME}
218222179Suqs
219214306Sjulian		# Make lo0 interface localhost.
220214306Sjulian		jexec ${NODE_NAME} ifconfig lo0 localhost
221214306Sjulian
222214306Sjulian		# Generate a random mac address for virtual interface. First
223214306Sjulian		# three octets can be changed by user. Last three octets are
224222179Suqs		# generated randomly.
225214306Sjulian		M4=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
226214306Sjulian				awk '{ print $1 % 256 }'`
227214306Sjulian		M5=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
228214306Sjulian				awk '{ print $1 % 256 }'`
229214306Sjulian		M6=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
230214306Sjulian				awk '{ print $1 % 256 }'`
231214306Sjulian
232214306Sjulian		MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}`
233214306Sjulian
234214306Sjulian		# Set the link address (mac address) of virtual interface in
235214306Sjulian		# virtual node to randomly generated MAC.
236214306Sjulian		echo "Setting MAC address of ${EIFACE} to '${MAC}'"
237214306Sjulian		jexec ${NODE_NAME} ifconfig ${EIFACE} link $MAC
238214306Sjulian
239222179Suqs		# Either IPv4 or IPv6 can be used in this script. Ifconfig
240214306Sjulian		# IP setting syntax differs slightly for two IP versions.
241214306Sjulian		# For version 4 'inet' keyword is used whereas for version 6
242214306Sjulian		# 'inet6' is used. Below line tries to decide which IP version
243222179Suqs		# is given and sets IPVER to 'inet' or 'inet6'.
244214306Sjulian
245214306Sjulian		IPVER=`echo ${NODE_IP} | awk -F"." '{ split($4,last,"/"); \
246214306Sjulian			if( NF==4 && $1>0 && $1<256 && $2<256 && $3<256 && \
247214306Sjulian			last[1]<256) print "inet"; else print "inet6"}'`
248222179Suqs
249214306Sjulian		# Set IP address of virtual interface in virtual node.
250214306Sjulian		echo "Setting IP address of ${EIFACE} to '${NODE_IP}'"
251214306Sjulian		jexec ${NODE_NAME} ifconfig ${EIFACE} ${IPVER} ${NODE_IP}
252222179Suqs
253214306Sjulian		# Connect virtual interface to bridge interface. Syntax is :
254214306Sjulian		# ngctl connect INTERFACE: BRIDGE: INTERFACE_HOOK EMPTY_LINK.
255214306Sjulian		# Interface has one hook named 'ether' and below line connects
256222179Suqs		# ether hook to bridge's first unconnected link.
257222179Suqs
258214306Sjulian		echo -n "Connecting ${EIFACE}:ether to ${BRIDGE_NAME}:link${LINKNUM}..."
259214306Sjulian		ngctl connect ${EIFACE}: ${BRIDGE_NAME}: ether link${LINKNUM} \
260214306Sjulian			|| exit 1
261214306Sjulian		echo "done"
262214306Sjulian
263214306Sjulian		# Now, bridge has one more connected link thus link count is
264222179Suqs		# incremented.
265214306Sjulian		LINKNUM=`expr ${LINKNUM} + 1`
266214306Sjulian	done
267222179Suqs	echo "Virtual LAN established successfully!"
268214306Sjulian
269214306Sjulian}
270214306Sjulian
271214306Sjulian# Stop routine.
272214306Sjulianvirtual_lan_stop() {
273214306Sjulian
274214306Sjulian	if [ ! -e ${TEMP_FILE} ]; then
275214306Sjulian		echo "Nothing to stop! ${TEMP_FILE}: temp file not found"
276214306Sjulian	else
277214306Sjulian
278222179Suqs		echo -n "Shutdown bridge interface.."
279214306Sjulian		OBJECTS=`cat ${TEMP_FILE} | grep bridge | awk '{print $2}'`
280214306Sjulian		for BRIDGE in ${OBJECTS}; do
281214306Sjulian			ngctl shutdown ${BRIDGE}: >/dev/null 2>&1
282214306Sjulian		done
283214306Sjulian		echo "done"
284222179Suqs
285222179Suqs		echo -n "Shutdown all eiface interfaces..."
286214306Sjulian		OBJECTS=`cat ${TEMP_FILE} | grep interface | awk '{print $2}'`
287214306Sjulian		for INTERFACE in ${OBJECTS}; do
288214306Sjulian			ngctl shutdown ${INTERFACE}: >/dev/null 2>&1
289214306Sjulian		done
290214306Sjulian		echo "done"
291222179Suqs
292222179Suqs		echo -n "Removing all jails..."
293214306Sjulian		OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'`
294214306Sjulian		for NODE in ${OBJECTS}; do
295214306Sjulian			jail -r ${NODE}
296214306Sjulian		done
297214306Sjulian		echo "done"
298222179Suqs
299222179Suqs		echo "Removing tempfile ${TEMP_FILE}"
300214306Sjulian		rm ${TEMP_FILE}
301214306Sjulian	fi
302222179Suqs	echo "Virtual LAN objects removed successfully!"
303214306Sjulian
304214306Sjulian}
305214306Sjulian
306214306Sjulianvirtual_lan_usage() {
307214306Sjulian	echo "usage: $0 start [target_topology]"
308214306Sjulian	echo "     : $0 [ stop | help ]"
309214306Sjulian}
310214306Sjulian
311214306Sjulian
312214306Sjulian# Main entry point.
313214306Sjulian
314214306Sjuliancase $# in
315214306Sjulian	1)
316214306Sjulian		case $1 in
317214306Sjulian                        start)
318214306Sjulian                                echo -n "Creating default target topology:"
319214306Sjulian				echo " ${TARGET_TOPOLOGY}"
320214306Sjulian                                virtual_lan_start
321214306Sjulian                                ;;
322214306Sjulian                        stop)
323214306Sjulian
324214306Sjulian				if [ ! -e ${TEMP_FILE} ]; then
325214306Sjulian					echo -n "Noting to stop! ${TEMP_FILE}:"
326214306Sjulian					echo " temp file not found"
327214306Sjulian				else
328214306Sjulian					virtual_lan_stop
329222179Suqs				fi
330214306Sjulian                                ;;
331214306Sjulian                        help)
332214306Sjulian                                virtual_lan_usage
333214306Sjulian				exit 1
334214306Sjulian                                ;;
335214306Sjulian                        *)
336214306Sjulian                                virtual_lan_usage
337214306Sjulian                                exit 1
338214306Sjulian
339214306Sjulian                esac
340214306Sjulian		;;
341214306Sjulian	2)
342214306Sjulian	        case $1 in
343214306Sjulian			start)
344214306Sjulian                        	TARGET_TOPOLOGY=$2
345214306Sjulian                                echo -n "Creating target topology:"
346214306Sjulian				echo "${TARGET_TOPOLOGY}"
347214306Sjulian                                virtual_lan_start
348214306Sjulian                                ;;
349214306Sjulian                        *)
350214306Sjulian                        	virtual_lan_usage
351214306Sjulian                                exit 1
352214306Sjulian                esac
353214306Sjulian		;;
354214306Sjulian
355214306Sjulian	*)
356214306Sjulian                virtual_lan_usage
357214306Sjulian                exit 1
358214306Sjulianesac
359214306Sjulian
360