]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - share/examples/netgraph/virtual.chain
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / share / examples / netgraph / virtual.chain
1 #!/bin/sh
2 #
3 # Copyright (c) 2010, Yavuz Gokirmak
4 #
5 # All rights reserved.
6 #
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
13 # its use.
14 #
15 # $FreeBSD$
16 #
17 # This script creates and connects n router like nodes. Complex wide
18 # area topologies can be created with the help of script.
19 #
20 # Virtual nodes are generated via jails and network connections are
21 # established using ng_eiface(4) node types.
22 #
23 # To use this script:
24 #
25 # 0. Make your own copy of this example script.
26 #
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 ...)
32 #
33 # 2. Run this script with "start" as the command line argument.
34 #
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:
45 #      STATIC_ROUTE_CNT=2
46 #
47 # 4. Stop bridging by running this script with "stop" as the
48 #    command line argument.
49 #
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.
55 #
56 # Target Topology:
57 #
58 # +---------------+  +---------------------------------------------+
59 # |  n1 (vimage)  |  |                 n2 (vimage)                 |
60 # |               |  |                                             |
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-------+
68 #         X                X              X                X
69 #          X               X             X                X
70 #           XXXXXXXXXXXXXXX            X                  X
71 #                                    X                    X
72 #                          +--------X------+     +--------X------+
73 #                          |   -+--X--+-   |     |   -+--X--+-   |
74 #                          |    |ether|    |     |    |ether|    |
75 #                          | +--+-----+--+ |     | +--+-----+--+ |
76 #                          | |  ngeth3   | |     | |  ngeth5   | |
77 #                          | |(ng_eiface)| |     | |(ng_eiface)| |
78 #                          | +-----------+ |     | +-----------+ |
79 #                          |               |     |               |
80 #                          |  n3 (vimage)  |     |  n4 (vimage)  |
81 #                          +---------------+     +---------------+
82 #
83 #
84 #
85
86 # List the names of virtual nodes and their IP addresses. Use ':'
87 # character to separate node name from node IP address and netmask.
88
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"
93 STATIC_ROUTE_CNT=3
94
95 # MAC manufacturer prefix. This can be modified according to needs.
96 MAC_PREFIX="00:1d:92"
97
98 # Temporary file is important for proper execution of script.
99 TEMP_FILE="/var/tmp/.virtual.chain.tmp"
100
101 # Set root directory for jails to be created.
102 JAIL_PATH="/usr/jails/router"
103
104
105 ####################################################################
106 ####    Nothing below this point should need to be modified.    ####
107 ####################################################################
108
109
110 # Start/restart routine.
111 virtual_chain_start() {
112
113         # Load netgraph KLD's as necessary.
114
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
119                         echo "done"
120                 fi
121         done
122
123         # Reset all interfaces and jails. If temporary file can not be found
124         # script assumes that there is no previous configuration.
125
126         if [ ! -e ${TEMP_FILE} ]; then
127                 echo "No previous configuration(${TEMP_FILE}) found to clean-up."
128         else
129                 echo -n "Cleaning previous configuration..."
130                 virtual_chain_stop
131                 echo "done"
132         fi
133
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.
138
139         if [ -e ${TEMP_FILE} ]; then
140                 touch ${TEMP_FILE}
141         fi
142
143
144         # Attach other interfaces as well.
145         for CONNECTION in ${TARGET_TOPOLOGY}; do
146
147                 # Virtual connections are defined in TARGET_TOPOLOGY variable.
148                 # They have the form of 'nodeName|IPaddr'. Below two lines split
149
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}'`
153
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}'`
157
158                 # !!! if not created already..
159                 # Create virtual node (jail) with given name and using
160                 # JAIL_PATH as root directory for jail.
161
162                 virtual_chain_create_peer_if_necessary ${PEER1_NAME}
163                 virtual_chain_create_peer_if_necessary ${PEER2_NAME}
164
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}
170
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}
176
177                 # Connect virtual interface to other interface. Syntax is :
178                 # ngctl connect INTERFACE1: INTERFACE2: ether ether.
179
180                 echo -n "Connecting ${PEER1_INTERFACE}:ether to ${PEER2_INTERFACE}:ether..."
181                 ngctl connect ${PEER1_INTERFACE}: ${PEER2_INTERFACE}: ether ether \
182                         || exit 1
183                 echo "done"
184
185         done
186
187         # Executes static route add commands.
188         i=0
189         while [ $i != $STATIC_ROUTE_CNT ]; do
190                 eval ROUTE=\${STATIC_ROUTE${i}}
191                 ret=`${ROUTE}`
192                 i=`expr $i + 1`
193         done
194
195         echo "Virtual WAN established successfully!"
196 }
197
198 virtual_chain_create_interface_with_ip() {
199
200         NODE_NAME=$1
201         NODE_IP=$2
202
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.
207
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."
212
213         # Write name of the interface to temp file. Clean-up procedure
214         # will use this name to shutdown interface.
215
216         echo "interface ${EIFACE}" >> ${TEMP_FILE}
217
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'
222
223         echo "Moving ${EIFACE} to ${NODE_NAME}"
224         ifconfig ${EIFACE} vnet ${NODE_NAME}
225
226         # Make lo0 interface localhost.
227         jexec ${NODE_NAME} ifconfig lo0 localhost
228
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 }'`
238
239         MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}`
240
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
245
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'.
251
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"}'`
255
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}
259
260         RET_INTERFACE=${EIFACE}
261 }
262
263 virtual_chain_create_peer_if_necessary() {
264
265         if ! grep -q $1 ${TEMP_FILE} ; then
266
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
272                 echo "done"
273
274                 # Write name of the jail to temp file. Clean-up
275                 # procedure will use this name to remove jail.
276
277                 echo "node ${1}" >> ${TEMP_FILE}
278         fi
279
280 }
281
282 # Stop routine.
283 virtual_chain_stop() {
284
285         if [ ! -e ${TEMP_FILE} ]; then
286                 echo "Nothing to stop! ${TEMP_FILE}: temp file not found"
287         else
288
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
293                 done
294                 echo "done"
295
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
300                 done
301                 echo "done"
302
303                 echo -n "Removing all jails..."
304                 OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'`
305                 for NODE in ${OBJECTS}; do
306                         jail -r ${NODE}
307                 done
308                 echo "done"
309
310                 echo "Removing tempfile ${TEMP_FILE}"
311                 rm ${TEMP_FILE}
312         fi
313         echo "Virtual LAN objects removed successfully!"
314
315 }
316
317 virtual_chain_usage() {
318         echo "usage: $0 start [target_topology]"
319         echo "     : $0 [ stop | help ]"
320 }
321
322
323 # Main entry point.
324
325 case $# in
326         1)
327                 case $1 in
328                         start)
329                                 echo -n "Creating default target topology:"
330                                 echo " ${TARGET_TOPOLOGY}"
331                                 virtual_chain_start
332                                 ;;
333                         stop)
334
335                                 if [ ! -e ${TEMP_FILE} ]; then
336                                         echo -n "Noting to stop! ${TEMP_FILE}:"
337                                         echo " temp file not found"
338                                 else
339                                         virtual_chain_stop
340                                 fi
341                                 ;;
342                         help)
343                                 virtual_chain_usage
344                                 exit 1
345                                 ;;
346                         *)
347                                 virtual_chain_usage
348                                 exit 1
349
350                 esac
351                 ;;
352         2)
353                 case $1 in
354                         start)
355                                 TARGET_TOPOLOGY=$2
356                                 echo -n "Creating target topology:"
357                                 echo "${TARGET_TOPOLOGY}"
358                                 virtual_chain_start
359                                 ;;
360                         *)
361                                 virtual_chain_usage
362                                 exit 1
363                 esac
364                 ;;
365
366         *)
367                 virtual_chain_usage
368                 exit 1
369 esac
370