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