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