3 # Copyright (c) 2010, Yavuz Gokirmak
7 # This source code may be used, modified, copied, distributed, and
8 # sold, in both source and binary form provided that the above
9 # copyright and these terms are retained, verbatim, as the first
10 # lines of this file. Under no circumstances is the author
11 # responsible for the proper functioning of the software nor does
12 # the author assume any responsibility for damages incurred with
17 # This script creates and connects n router like nodes. Complex wide
18 # area topologies can be created with the help of script.
20 # Virtual nodes are generated via jails and network connections are
21 # established using ng_eiface(4) node types.
25 # 0. Make your own copy of this example script.
27 # 1. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual
28 # nodes. Virtual topology definition includes node names and their
29 # IP address. Target top. syntax: ( name|ip<->name|ip ... )
30 # Example 1: ( n1|10.0.2.1/30<->n2|10.0.2.2/30 ...)
31 # Example 2: ( n1|2001:b90::14a/125<->n1|2001:b90::14b/125 ...)
33 # 2. Run this script with "start" as the command line argument.
35 # 3. Add necessary static route commands for each virtual node. For
36 # example assume you have three virtual nodes connected each other
37 # like a chain (n1 is connected to n2, n2 is connected to n3).
38 # In order to establish connectivity among these virtual nodes,
39 # you have to add default routes to node n1 and node n3. Example
40 # static route command is:
41 # STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2"
42 # STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5"
43 # After defining default routes with above format you have to set
44 # the total number of static route commands as:
47 # 4. Stop bridging by running this script with "stop" as the
48 # command line argument.
50 # 5. This script uses a template file in order to carry information
51 # between start and stop calls.
52 # In the start call, the netgraph interfaces and jails are created.
53 # At the stop phase, all created objects should be removed.
54 # DO NOT delete the temporary file between the start and stop phases.
58 # +---------------+ +---------------------------------------------+
59 # | n1 (vimage) | | n2 (vimage) |
61 # | +-----------+ | | +-----------+ +-----------+ +-----------+ |
62 # | | ngeth0 | | | | ngeth1 | | ngeth2 | | ngeth4 | |
63 # | |(ng_eiface)| | | |(ng_eiface)| |(ng_eiface)| |(ng_eiface)| |
64 # | +--+-----+--+ | | +--+-----+--+ +--+-----+--+ +--+-----+--+ |
65 # | |ether| | | |ether| |ether| |ether| |
66 # | +-X---+ | | +--X--+ +--X--+ +--X--+ |
67 # +-------X-------+ +------X--------------X---------------X-------+
72 # +--------X------+ +--------X------+
73 # | -+--X--+- | | -+--X--+- |
74 # | |ether| | | |ether| |
75 # | +--+-----+--+ | | +--+-----+--+ |
76 # | | ngeth3 | | | | ngeth5 | |
77 # | |(ng_eiface)| | | |(ng_eiface)| |
78 # | +-----------+ | | +-----------+ |
80 # | n3 (vimage) | | n4 (vimage) |
81 # +---------------+ +---------------+
86 # List the names of virtual nodes and their IP addresses. Use ':'
87 # character to separate node name from node IP address and netmask.
89 TARGET_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"
90 STATIC_ROUTE0="jexec n1 route add -inet default 10.0.2.2"
91 STATIC_ROUTE1="jexec n3 route add -inet default 10.0.2.5"
92 STATIC_ROUTE2="jexec n4 route add -inet default 10.0.2.9"
95 # MAC manufacturer prefix. This can be modified according to needs.
98 # Temporary file is important for proper execution of script.
99 TEMP_FILE="/var/tmp/.virtual.chain.tmp"
101 # Set root directory for jails to be created.
102 JAIL_PATH="/usr/jails/router"
105 ####################################################################
106 #### Nothing below this point should need to be modified. ####
107 ####################################################################
110 # Start/restart routine.
111 virtual_chain_start() {
113 # Load netgraph KLD's as necessary.
115 for KLD in ng_ether ng_bridge ng_eiface; do
116 if ! kldstat -v | grep -qw ${KLD}; then
117 echo -n "Loading ${KLD}.ko... "
118 kldload ${KLD} || exit 1
123 # Reset all interfaces and jails. If temporary file can not be found
124 # script assumes that there is no previous configuration.
126 if [ ! -e ${TEMP_FILE} ]; then
127 echo "No previous configuration(${TEMP_FILE}) found to clean-up."
129 echo -n "Cleaning previous configuration..."
134 # Create temporary file for usage. This file includes generated
135 # interface names and jail names. All bridges, interfaces and jails
136 # are written to file while created. In clean-up process written
137 # objects are cleaned (i.e. removed) from system.
139 if [ -e ${TEMP_FILE} ]; then
144 # Attach other interfaces as well.
145 for CONNECTION in ${TARGET_TOPOLOGY}; do
147 # Virtual connections are defined in TARGET_TOPOLOGY variable.
148 # They have the form of 'nodeName|IPaddr'. Below two lines split
150 PEER1=`echo ${CONNECTION} | awk -F"<->" '{print $1}'`
151 PEER1_NAME=`echo ${PEER1} | awk -F"|" '{print $1}'`
152 PEER1_IP=`echo ${PEER1} | awk -F"|" '{print $2}'`
154 PEER2=`echo ${CONNECTION} | awk -F"<->" '{print $2}'`
155 PEER2_NAME=`echo ${PEER2} | awk -F"|" '{print $1}'`
156 PEER2_IP=`echo ${PEER2} | awk -F"|" '{print $2}'`
158 # !!! if not created already..
159 # Create virtual node (jail) with given name and using
160 # JAIL_PATH as root directory for jail.
162 virtual_chain_create_peer_if_necessary ${PEER1_NAME}
163 virtual_chain_create_peer_if_necessary ${PEER2_NAME}
165 # create an interface for peer with the given peer IP. Get interface
166 # for future use; you will connect this interface to the other
167 # peers' (PEER2) interface.
168 virtual_chain_create_interface_with_ip ${PEER1_NAME} ${PEER1_IP}
169 PEER1_INTERFACE=${RET_INTERFACE}
171 # create an interface for peer with the given peer IP. Get interface
172 # for future use; you will connect this interface to the other
173 # peers' (PEER2) interface.
174 virtual_chain_create_interface_with_ip ${PEER2_NAME} ${PEER2_IP}
175 PEER2_INTERFACE=${RET_INTERFACE}
177 # Connect virtual interface to other interface. Syntax is :
178 # ngctl connect INTERFACE1: INTERFACE2: ether ether.
180 echo -n "Connecting ${PEER1_INTERFACE}:ether to ${PEER2_INTERFACE}:ether..."
181 ngctl connect ${PEER1_INTERFACE}: ${PEER2_INTERFACE}: ether ether \
187 # Executes static route add commands.
189 while [ $i != $STATIC_ROUTE_CNT ]; do
190 eval ROUTE=\${STATIC_ROUTE${i}}
195 echo "Virtual WAN established successfully!"
198 virtual_chain_create_interface_with_ip() {
203 # Create a ng_eiface object for virtual node. ng_eiface
204 # object has a hook that can be connected to one of bridge
205 # links. After creating interface get its automatically
206 # generated name for further usage.
208 echo "Creating eiface interface for virtual node ${NODE_NAME}."
209 ngctl mkpeer eiface ether ether
210 EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'`
211 echo "Interface ${EIFACE} is created."
213 # Write name of the interface to temp file. Clean-up procedure
214 # will use this name to shutdown interface.
216 echo "interface ${EIFACE}" >> ${TEMP_FILE}
218 # Move virtual interface to virtual node. Note that Interface
219 # name will not be changed at the end of this movement. Moved
220 # interface can be seen at the output of ifconfig command in
221 # jail: 'jexec jailname ifconfig'
223 echo "Moving ${EIFACE} to ${NODE_NAME}"
224 ifconfig ${EIFACE} vnet ${NODE_NAME}
226 # Make lo0 interface localhost.
227 jexec ${NODE_NAME} ifconfig lo0 localhost
229 # Generate a random mac address for virtual interface. First
230 # three octets can be changed by user. Last three octets are
231 # generated randomly.
232 M4=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
233 awk '{ print $1 % 256 }'`
234 M5=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
235 awk '{ print $1 % 256 }'`
236 M6=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
237 awk '{ print $1 % 256 }'`
239 MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}`
241 # Set the link address (mac address) of virtual interface in
242 # virtual node to randomly generated MAC.
243 echo "Setting MAC address of ${EIFACE} to '${MAC}'"
244 jexec ${NODE_NAME} ifconfig ${EIFACE} link $MAC
246 # Either IPv4 or IPv6 can be used in this script. Ifconfig
247 # IP setting syntax differs slightly for two IP versions.
248 # For version 4 'inet' keyword is used whereas for version 6
249 # 'inet6' is used. Below line tries to decide which IP version
250 # is given and sets IPVER to 'inet' or 'inet6'.
252 IPVER=`echo ${NODE_IP} | awk -F"." '{ split($4,last,"/"); \
253 if( NF==4 && $1>0 && $1<256 && $2<256 && $3<256 && \
254 last[1]<256) print "inet"; else print "inet6"}'`
256 # Set IP address of virtual interface in virtual node.
257 echo "Setting IP address of ${EIFACE} to '${NODE_IP}'"
258 jexec ${NODE_NAME} ifconfig ${EIFACE} ${IPVER} ${NODE_IP}
260 RET_INTERFACE=${EIFACE}
263 virtual_chain_create_peer_if_necessary() {
265 if ! grep -q $1 ${TEMP_FILE} ; then
267 echo -n "Creating virtual node (jail) ${1}..."
268 jail -c vnet name=${1} host.hostname=${1} \
269 path=${JAIL_PATH} persist
270 jexec ${1} sysctl -w net.inet.ip.forwarding=1
271 jexec ${1} sysctl -w net.inet6.ip6.forwarding=1
274 # Write name of the jail to temp file. Clean-up
275 # procedure will use this name to remove jail.
277 echo "node ${1}" >> ${TEMP_FILE}
283 virtual_chain_stop() {
285 if [ ! -e ${TEMP_FILE} ]; then
286 echo "Nothing to stop! ${TEMP_FILE}: temp file not found"
289 echo -n "Shutdown bridge interface.."
290 OBJECTS=`cat ${TEMP_FILE} | grep bridge | awk '{print $2}'`
291 for BRIDGE in ${OBJECTS}; do
292 ngctl shutdown ${BRIDGE}: >/dev/null 2>&1
296 echo -n "Shutdown all eiface interfaces..."
297 OBJECTS=`cat ${TEMP_FILE} | grep interface | awk '{print $2}'`
298 for INTERFACE in ${OBJECTS}; do
299 ngctl shutdown ${INTERFACE}: >/dev/null 2>&1
303 echo -n "Removing all jails..."
304 OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'`
305 for NODE in ${OBJECTS}; do
310 echo "Removing tempfile ${TEMP_FILE}"
313 echo "Virtual LAN objects removed successfully!"
317 virtual_chain_usage() {
318 echo "usage: $0 start [target_topology]"
319 echo " : $0 [ stop | help ]"
329 echo -n "Creating default target topology:"
330 echo " ${TARGET_TOPOLOGY}"
335 if [ ! -e ${TEMP_FILE} ]; then
336 echo -n "Noting to stop! ${TEMP_FILE}:"
337 echo " temp file not found"
356 echo -n "Creating target topology:"
357 echo "${TARGET_TOPOLOGY}"