]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - share/examples/netgraph/virtual.lan
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / share / examples / netgraph / virtual.lan
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 adds virtual nodes to one of the physical interfaces
18 # visible on your local area network (LAN). Virtual nodes seems real
19 # to external observers.
20 # If traceroute is executed to one of virtual nodes, the IP
21 # address of the physical interface will not be seen in the output.
22 # Virtual nodes are generated via jails and network connections are
23 # established using ng_bridge(4) and ng_eiface(4) node types.
24 #
25 # To use this script:
26 #
27 # 0. Make your own copy of this example script.
28 #
29 # 1. Edit the definition of ${ETHER_INTF} as described below
30 #    to define your real interface connected to the LAN. Virtual nodes
31 #    will placed on the same physical network as this interface.
32 #
33 # 2. Edit the definition of ${TARGET_TOPOLOGY} to define your virtual
34 #    nodes. Virtual topology definition includes node names and their
35 #    IP address. Target top. syntax: ( node1|ip1/24 node2|ip2/24 ... )
36 #    Example 1: ( n1|122.122.122.12/24, n2|122.122.122.13/24 ...)
37 #    Example 2: ( n1|2001:b90::14a/125, n1|2001:b90::14b/125 ...)
38 #
39 # 3. Run this script with "start" as the command line argument.
40 #
41 # 4. Stop bridging by running this script with "stop" as the
42 #    command line argument.
43 #
44 # 5. This script uses a template file in order to carry information
45 #    between start and stop calls.
46 #      In the start call, the netgraph interfaces and jails are created.
47 #      At the stop phase, all created objects should be removed.
48 #    DO NOT delete the temporary file between the start and stop phases.
49 #
50 # To add virtual nodes for multiple independent LANs, create multiple
51 # copies of this script with different variable definitions.
52 #
53 # Target Topology:
54 #
55 #
56 #                 +---------------+ +---------------+ +---------------+
57 #                 |  n0 (vimage)  | |  n1 (vimage)  | |  nk (vimage)  |
58 #                 |               | |               | |               |
59 #                 | +-----------+ | | +-----------+ | | +-----------+ |
60 #                 | |  ngeth0   | | | |  ngeth1   | | | |  ngethk   | |
61 #                 | |(ng_eiface)| | | |(ng_eiface)| | | |(ng_eiface)| |
62 #                 | +--+-----+--+ | | +--+-----+--+ | | +--+-----+--+ |
63 #                 |    |ether|    | |    |ether|    | |    |ether|    |
64 #                 |    +--X--+    | |    +--X--+    | |    +---X-+    |
65 #   +-----+       +--------\------+ +--------\------+ +-------/-------+
66 #   |upper|----\            \ip_addr          \ip_addr       /ip_addr
67 # +-+-----+--+  \            \                 \             \
68 # |   em0    |   \            +--------+        +-+           \
69 # |(ng_ether)|    +-----------+         \          \           \
70 # +-+-----+--+                 \         \          /           \
71 #   |lower|    +---------\      \         \        /            /
72 #   +--X--+   /        O--X--O O-X---O O---X-O O--X--O     O---X---O
73 #      \      |        |link0| |link1| |link2| |link3|     |linkk+2|
74 #       \     /      +-O-----O-O-----O-O-----O-O-----O-----O-------O-+
75 #        +---+       |                                               |
76 #                    |          bridge (ng_bridge)                   |
77 #                    +-----------------------------------------------+
78 #
79 #
80
81 # Give the name of ethernet interface. Virtual nodes will be seen as
82 # local neighbours of this interface.
83
84 ETHER_INTF="em0"
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="c1|10.0.2.20/24 c2|10.0.2.21/24 c3|10.0.2.22/24"
90
91 # MAC manufacturer prefix. This can be modified according to needs.
92 MAC_PREFIX="00:1d:92"
93
94 # Temporary file is important for proper execution of script.
95 TEMP_FILE="/var/tmp/.virtual.lan.tmp"
96
97 # Set root directory for jails to be created.
98 JAIL_PATH="/usr/jails/node"
99
100
101 ####################################################################
102 ####    Nothing below this point should need to be modified.    ####
103 ####################################################################
104
105
106 # Start/restart routine.
107 virtual_lan_start() {
108
109         # Load netgraph KLD's as necessary.
110
111         for KLD in ng_ether ng_bridge ng_eiface; do
112                 if ! kldstat -v | grep -qw ${KLD}; then
113                         echo -n "Loading ${KLD}.ko... "
114                         kldload ${KLD} || exit 1
115                         echo "done"
116                 fi
117         done
118
119         # Reset all interfaces and jails. If temporary file can not be found
120         # script assumes that there is no previous configuration.
121
122         if [ ! -e ${TEMP_FILE} ]; then
123                 echo "No previous configuration(${TEMP_FILE}) found to clean-up."
124         else
125                 echo -n "Cleaning previous configuration..."
126                 virtual_lan_stop
127                 echo "done"
128         fi
129
130         # Create temporary file for usage. This file includes generated
131         # interface names and jail names. All bridges, interfaces and jails
132         # are written to file while created. In clean-up process written
133         # objects are cleaned (i.e. removed) from system.
134
135         if [ -e ${TEMP_FILE} ]; then
136                 touch ${TEMP_FILE}
137         fi
138
139         echo -n "Verifying ethernet interface existence..."
140         # Verify ethernet interface exist.
141         if ! ngctl info ${ETHER_INTF}: >/dev/null 2>&1; then
142                 echo "Error: interface ${ETHER_INTF} does not exist"
143                 exit 1
144         fi
145         ifconfig ${ETHER_INTF} up || exit 1
146         echo "done"
147
148         # Get current number of bridge interfaces in the system. This number
149         # is used to create a name for new bridge.
150         BRIDGE_COUNT=`ngctl l | grep bridge | wc -l | sed -e "s/ //g"`
151         BRIDGE_NAME="bridge${BRIDGE_COUNT}"
152
153         # Create new ng_bridge(4) node and attach it to the ethernet interface.
154         # Connect ng_ether:lower hook to bridge:link0 when creating bridge and
155         # connect ng_ether:upper hook to bridge:link1 after bridge name is set.
156
157         echo "Creating bridge interface: ${BRIDGE_NAME}..."
158         ngctl mkpeer ${ETHER_INTF}: bridge lower link0 || exit 1
159         ngctl name ${ETHER_INTF}:lower ${BRIDGE_NAME} || exit 1
160         ngctl connect ${ETHER_INTF}: ${BRIDGE_NAME}: upper link1 || exit 1
161         echo "Bridge ${BRIDGE_NAME} is created and ${ETHER_INTF} is connected."
162
163         # In the above code block two hooks are connected to bridge interface,
164         # therefore LINKNUM is set to 2 indicating total number of connected
165         # hooks on the bridge interface.
166         LINKNUM=2
167
168         # Write name of the bridge to temp file. Clean-up procedure will use
169         # this name to shutdown bridge interface.
170         echo "bridge ${BRIDGE_NAME}" > ${TEMP_FILE}
171
172
173         # Attach other interfaces as well.
174         for NODE in ${TARGET_TOPOLOGY}; do
175
176                 # Virtual nodes are defined in TARGET_TOPOLOGY variable. They
177                 # have the form of 'nodeName|IPaddr'. Below two lines split
178                 # node definition to get node name and node IP.
179
180                 NODE_NAME=`echo ${NODE} | awk -F"|" '{print $1}'`
181                 NODE_IP=`echo ${NODE} | awk -F"|" '{print $2}'`
182
183                 # Create virtual node (jail) with given name and using
184                 # JAIL_PATH as root directory for jail.
185
186                 echo -n "Creating virtual node (jail) ${NODE_NAME}..."
187                 jail -c vnet name=${NODE_NAME} host.hostname=${NODE_NAME} \
188                         path=${JAIL_PATH} persist
189                 echo "done"
190
191                 # Write name of the jail to temp file. Clean-up procedure will
192                 # use this name to remove jail.
193
194                 echo "node ${NODE_NAME}" >> ${TEMP_FILE}
195
196                 # Create a ng_eiface object for virtual node. ng_eiface
197                 # object has a hook that can be connected to one of bridge
198                 # links. After creating interface get its automatically
199                 # generated name for further usage.
200
201                 echo "Creating eiface interface for virtual node ${NODE_NAME}."
202                 ngctl mkpeer eiface ether ether
203                 EIFACE=`ngctl l | grep ngeth | tail -n 1| awk '{print $2}'`
204                 echo "Interface ${EIFACE} is created."
205
206                 # Write name of the interface to temp file. Clean-up procedure
207                 # will use this name to shutdown interface.
208
209                 echo "interface ${EIFACE}" >> ${TEMP_FILE}
210
211                 # Move virtual interface to virtual node. Note that Interface
212                 # name will not be changed at the end of this movement. Moved
213                 # interface can be seen at the output of ifconfig command in
214                 # jail: 'jexec jailname ifconfig'
215
216                 echo "Moving ${EIFACE} to ${NODE_NAME}"
217                 ifconfig ${EIFACE} vnet ${NODE_NAME}
218
219                 # Make lo0 interface localhost.
220                 jexec ${NODE_NAME} ifconfig lo0 localhost
221
222                 # Generate a random mac address for virtual interface. First
223                 # three octets can be changed by user. Last three octets are
224                 # generated randomly.
225                 M4=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
226                                 awk '{ print $1 % 256 }'`
227                 M5=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
228                                 awk '{ print $1 % 256 }'`
229                 M6=`od -An -N2 -i /dev/random | sed -e 's/ //g' | \
230                                 awk '{ print $1 % 256 }'`
231
232                 MAC=`printf ${MAC_PREFIX}:%02x:%02x:%02x ${M4} ${M5} ${M6}`
233
234                 # Set the link address (mac address) of virtual interface in
235                 # virtual node to randomly generated MAC.
236                 echo "Setting MAC address of ${EIFACE} to '${MAC}'"
237                 jexec ${NODE_NAME} ifconfig ${EIFACE} link $MAC
238
239                 # Either IPv4 or IPv6 can be used in this script. Ifconfig
240                 # IP setting syntax differs slightly for two IP versions.
241                 # For version 4 'inet' keyword is used whereas for version 6
242                 # 'inet6' is used. Below line tries to decide which IP version
243                 # is given and sets IPVER to 'inet' or 'inet6'.
244
245                 IPVER=`echo ${NODE_IP} | awk -F"." '{ split($4,last,"/"); \
246                         if( NF==4 && $1>0 && $1<256 && $2<256 && $3<256 && \
247                         last[1]<256) print "inet"; else print "inet6"}'`
248
249                 # Set IP address of virtual interface in virtual node.
250                 echo "Setting IP address of ${EIFACE} to '${NODE_IP}'"
251                 jexec ${NODE_NAME} ifconfig ${EIFACE} ${IPVER} ${NODE_IP}
252
253                 # Connect virtual interface to bridge interface. Syntax is :
254                 # ngctl connect INTERFACE: BRIDGE: INTERFACE_HOOK EMPTY_LINK.
255                 # Interface has one hook named 'ether' and below line connects
256                 # ether hook to bridge's first unconnected link.
257
258                 echo -n "Connecting ${EIFACE}:ether to ${BRIDGE_NAME}:link${LINKNUM}..."
259                 ngctl connect ${EIFACE}: ${BRIDGE_NAME}: ether link${LINKNUM} \
260                         || exit 1
261                 echo "done"
262
263                 # Now, bridge has one more connected link thus link count is
264                 # incremented.
265                 LINKNUM=`expr ${LINKNUM} + 1`
266         done
267         echo "Virtual LAN established successfully!"
268
269 }
270
271 # Stop routine.
272 virtual_lan_stop() {
273
274         if [ ! -e ${TEMP_FILE} ]; then
275                 echo "Nothing to stop! ${TEMP_FILE}: temp file not found"
276         else
277
278                 echo -n "Shutdown bridge interface.."
279                 OBJECTS=`cat ${TEMP_FILE} | grep bridge | awk '{print $2}'`
280                 for BRIDGE in ${OBJECTS}; do
281                         ngctl shutdown ${BRIDGE}: >/dev/null 2>&1
282                 done
283                 echo "done"
284
285                 echo -n "Shutdown all eiface interfaces..."
286                 OBJECTS=`cat ${TEMP_FILE} | grep interface | awk '{print $2}'`
287                 for INTERFACE in ${OBJECTS}; do
288                         ngctl shutdown ${INTERFACE}: >/dev/null 2>&1
289                 done
290                 echo "done"
291
292                 echo -n "Removing all jails..."
293                 OBJECTS=`cat ${TEMP_FILE} | grep node | awk '{print $2}'`
294                 for NODE in ${OBJECTS}; do
295                         jail -r ${NODE}
296                 done
297                 echo "done"
298
299                 echo "Removing tempfile ${TEMP_FILE}"
300                 rm ${TEMP_FILE}
301         fi
302         echo "Virtual LAN objects removed successfully!"
303
304 }
305
306 virtual_lan_usage() {
307         echo "usage: $0 start [target_topology]"
308         echo "     : $0 [ stop | help ]"
309 }
310
311
312 # Main entry point.
313
314 case $# in
315         1)
316                 case $1 in
317                         start)
318                                 echo -n "Creating default target topology:"
319                                 echo " ${TARGET_TOPOLOGY}"
320                                 virtual_lan_start
321                                 ;;
322                         stop)
323
324                                 if [ ! -e ${TEMP_FILE} ]; then
325                                         echo -n "Noting to stop! ${TEMP_FILE}:"
326                                         echo " temp file not found"
327                                 else
328                                         virtual_lan_stop
329                                 fi
330                                 ;;
331                         help)
332                                 virtual_lan_usage
333                                 exit 1
334                                 ;;
335                         *)
336                                 virtual_lan_usage
337                                 exit 1
338
339                 esac
340                 ;;
341         2)
342                 case $1 in
343                         start)
344                                 TARGET_TOPOLOGY=$2
345                                 echo -n "Creating target topology:"
346                                 echo "${TARGET_TOPOLOGY}"
347                                 virtual_lan_start
348                                 ;;
349                         *)
350                                 virtual_lan_usage
351                                 exit 1
352                 esac
353                 ;;
354
355         *)
356                 virtual_lan_usage
357                 exit 1
358 esac
359