firehol/sbin/vnetbuild.in
Philip Whineray 633f4653c7 Clean up packaging
Use configure.ac to maintain version number
Remove redundant NEWS (ChangeLog) and AUTHORS (THANKS) files
Move hooks to their own directory
Rename README to README.md to format nicely on github
Generate README for tar by removing git specifics from README.md
Automate tagging when -rc or final version set in configure.ac
Improve pre-commit checking
2015-11-27 23:56:11 +00:00

695 lines
16 KiB
Bash
Executable File

#!/bin/bash
#
# vnetbuild - linked network namespace setup for humans...
#
# Copyright
#
# Copyright (C) 2015 Phil Whineray <phil@sanewall.org>
# Copyright (C) 2015 Costa Tsaousis <costa@tsaousis.gr>
#
# License
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# See the file COPYING for details.
#
VERSION='$Id$'
PROGRAM_FILE="${0}"
PROGRAM_DIR="${0%/*}"
if [ "$PROGRAM_DIR" = "$0" ]; then PROGRAM_DIR="."; fi
PROGRAM_PWD="${PWD}"
declare -a PROGRAM_ORIGINAL_ARGS=("${@}")
# Start defaults before configure
prefix_POST=/usr
sysconfdir_POST=/etc
localstatedir_POST=/var
libdir_POST=$PROGRAM_DIR
# End defaults before configure
for functions_file in $libdir_POST/functions.common.sh
do
if [ -r $functions_file ]
then
source $functions_file
else
1>&2 echo "Cannot access $functions_file"
exit 1
fi
done
FIREHOL_CONFIG_DIR="$sysconfdir_POST/firehol"
common_disable_localization || exit
marksreset() { :; }
markdef() { :; }
if [ -r "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ]
then
source "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
fi
common_load_commands $PROGRAM_FILE @AUTOCONF_RUN@ <<-!
Y|IP_CMD|@IP@|ip
Y|BRIDGE_CMD|@BRIDGE@|bridge
Y|GREP_CMD|@GREP@|grep
Y|FIND_CMD|@FIND@|find
Y|SH_CMD|@SH@|sh bash ksh
Y|CUT_CMD|@CUT@|cut
Y|CAT_CMD|@CAT@|cat
Y|SED_CMD|@SED@|sed
Y|TR_CMD|@TR@|tr
Y|SLEEP_CMD|@SLEEP@|sleep
Y|MKDIR_CMD|@MKDIR@|mkdir
Y|RM_CMD|@RM@|rm
Y|MKTEMP_CMD|@MKTEMP@|mktemp
N|NEATO_CMD|@NEATO@|neato
!
status=$?
test $status -eq 0 || exit $status
needroot=Y
haderror=""
#gvprog=dot
#gvprog=sfdp
gvprog=$NEATO_CMD
setup="$1"
mode="$2"
outfile="$3"
case "$mode" in
""|-h|help|-v|version)
mode=
needroot=
haderror="Y"
;;
start|stop|status)
:
;;
graphviz)
common_require_cmd $PROGRAM_FILE NEATO_CMD
needroot=
case "$outfile" in
*.gv|"")
graphviz=$CAT_CMD
;;
*.ps)
format=ps
graphviz="$gvprog -T$format"
;;
*.pdf)
format=pdf
graphviz="$gvprog -T$format"
;;
*.png)
format=png
graphviz="$gvprog -T$format"
;;
*)
1>&2 echo "Unrecognised file extension: $mode"
haderror="Y"
;;
esac
;;
*)
1>&2 echo "Unrecognised mode: $mode"
haderror="Y"
needroot=
;;
esac
if [ "$mode" = "" ]
then
$CAT_CMD <<-EOF
FireHOL vnetbuild $VERSION
(C) Copyright 2015 Phil Whineray <phil@firehol.org>
(C) Copyright 2015 Costa Tsaousis <costa@tsaousis.gr>
FireHOL is distributed under the GPL v2+.
Home Page: http://firehol.org
------------------------------------------------------------------------
Get notified of new FireHOL releases by subscribing to the mailing list:
http://lists.firehol.org/mailman/listinfo/firehol-support/
------------------------------------------------------------------------
EOF
fi
if [ "$needroot" -a "${UID}" != "0" ]
then
echo "Error: must be root to use '$mode'"
haderror="Y"
fi
if [ "$haderror" -o $# -lt 2 ]
then
echo ""
echo "Usage: sudo vnetbuild CONFIGFILE stop|start|status"
echo " or: vnetbuild CONFIGFILE graphviz OUTFILE.{gv,png,pdf,ps}"
exit 1
else
shift
shift
fi
setupbase="${setup##*/}"
errline=""
error=""
if ! MYTMP="`$MKTEMP_CMD -d -t vnetbuild-XXXXXX`"
then
echo >&2
echo >&2
echo >&2 "Cannot create temporary directory."
echo >&2
exit 1
fi
myexit() {
status=$?
if [ "$error" != "" ]
then
echo "$setupbase: line $errline: $error"
fi
$RM_CMD -rf $MYTMP
exit $status
}
trap myexit INT
trap myexit HUP
trap myexit 0
CURDIR=`pwd`/
export CURDIR
set -e
$MKDIR_CMD $MYTMP/setup
$SED_CMD = "$setup" > $MYTMP/withnum
(echo "cd $CURDIR"; $SED_CMD -e 'N;s/\n/\t/' -e 's/^/lineno=/' -e '/exec/s/[<>|&]/\\&/g' $MYTMP/withnum) > $MYTMP/setup/$setupbase
$MKDIR_CMD $MYTMP/ns
$MKDIR_CMD $MYTMP/runtime-lines
current_name=
create_namespace() {
errline=$lineno
local type="$1"
current_name="$2"
NSTMP=$MYTMP/ns/$current_name
if [ -d $NSTMP ]
then
error="$current_name: $($CAT_CMD $NSTMP/type) already defined"
return 1
fi
$MKDIR_CMD $NSTMP
$MKDIR_CMD $NSTMP/devices
$MKDIR_CMD $NSTMP/devicepairs
echo $type > $NSTMP/type
echo 0 > $NSTMP/forward
> $NSTMP/routes
> $NSTMP/devlist
> $NSTMP/pairlist
> $NSTMP/bridgelist
echo $current_name >> $MYTMP/nslist
echo $errline > $MYTMP/runtime-lines/$current_name
}
host() {
errline=$lineno
create_namespace host "$1"
}
switch() {
errline=$lineno
create_namespace switch "$1"
}
dev() {
errline=$lineno
device="$1"
shift
if [ ! "$current_name" ]
then
error="cannot define dev outside of a host or switch"
return 1
fi
if [ -f $NSTMP/devices/$device ]
then
error="$current_name/$device: already defined"
return 1
fi
local otherns=
local otherdev=
case $1 in
*/[a-zA-Z]*)
otherns=$(echo $1 | $CUT_CMD -f1 -d/)
otherdev=$(echo $1 | $CUT_CMD -f2 -d/)
shift
if [ -f $MYTMP/ns/$otherns/devicepairs/$otherdev ]
then
error="$otherns/$otherdev: already has paired device"
return 1
fi
;;
esac
local type="$($CAT_CMD $NSTMP/type)"
if [ "$*" != "" -a "$type" = "switch" ]
then
error="device in switch may not specify an IP address"
return 1
fi
f=$NSTMP/devices/$device
> $f
for ip in "$@"
do
case $ip in
*/*)
echo "$ip" >> $f
;;
*)
error="IP address should be expressed as ip/mask"
return 1
;;
esac
done
if [ "$otherdev" ]
then
if [ ! -d $MYTMP/ns/$otherns ]
then
error="$otherns undefined"
return 1
fi
echo "$current_name $device" > $MYTMP/ns/$otherns/devicepairs/$otherdev
echo "n/a n/a" > $NSTMP/devicepairs/$device
echo "$otherns $otherdev" >> $NSTMP/pairlist
echo $errline > $MYTMP/runtime-lines/$otherns-pair-$otherdev
fi
echo $device >> $NSTMP/devlist
echo $errline > $MYTMP/runtime-lines/$current_name-dev-$device
return 0
}
route() {
errline=$lineno
if [ ! "$current_name" ]
then
error="can only specify route in a host"
return 1
fi
local type="$($CAT_CMD $NSTMP/type)"
if [ "$type" = "switch" ]
then
error="can only specify route in a host"
return 1
fi
echo "$*" >> $NSTMP/routes
echo $errline >> $MYTMP/runtime-lines/$current_name-routes
return 0
}
bridgedev() {
errline=$lineno
device="$1"
shift
if [ ! "$current_name" ]
then
error="can only specify bridgedev in a host"
return 1
fi
local type="$($CAT_CMD $NSTMP/type)"
if [ "$type" = "switch" ]
then
error="can only specify bridgedev in a host"
return 1
fi
if [ -f $NSTMP/devices/$device ]
then
error="$current_name/$device: already defined"
return 1
fi
ipf=$NSTMP/devices/$device
devf=$ipf-bridged
> $ipf
> $devf
for ipordev in "$@"
do
case $ipordev in
*/*)
echo "$ipordev" >> $ipf
;;
*)
echo "$ipordev" >> $devf
;;
esac
done
echo $device >> $NSTMP/bridgelist
echo $errline > $MYTMP/runtime-lines/$current_name-dev-$device
return 0
}
exec() {
errline=$lineno
if [ ! "$current_name" ]
then
error="can only specify exec in a host or switch"
return 1
fi
echo "$*" >> $NSTMP/exec
echo $errline >> $MYTMP/runtime-lines/$current_name-exec
return 0
}
is_ipv6() {
case "$1" in
*:*)
return 0
;;
esac
return 1
}
cd $MYTMP/setup
. $setupbase
errline=""
cd $CURDIR
exists_ns() {
if [ "$($IP_CMD netns list | $GREP_CMD "^$1\$")" ]
then
return 0
else
return 1
fi
}
dev_in_ns() {
$IP_CMD netns exec $1 $IP_CMD link list | $GREP_CMD "^[0-9]" | $CUT_CMD -d: -f2 | $CUT_CMD -f1 -d'@' | $TR_CMD -d ' '
}
get_pids() {
# Not in all versions:
# $IP_CMD netns pids $1
$FIND_CMD -L /proc/[0-9]*/ns -maxdepth 1 -samefile /var/run/netns/$1 2>/dev/null | $CUT_CMD -f3 -d/
}
shutdown_ns() {
for i in $(dev_in_ns $1)
do
$IP_CMD netns exec $1 $IP_CMD link set $i down
done
pids=$(get_pids $1)
if [ "$pids" ]; then kill $pids; $SLEEP_CMD 1; fi
pids=$(get_pids $1)
if [ "$pids" ]; then kill -9 $pids; fi
}
startup_ns() {
for i in $(dev_in_ns $1)
do
$IP_CMD netns exec $1 $IP_CMD link set $i up
done
}
while read ns
do
while read dev
do
read errline < $MYTMP/runtime-lines/$ns-dev-$dev
if [ ! -f $MYTMP/ns/$ns/devicepairs/$dev ]
then
error="$ns/$dev has no paired device"
exit 1
fi
done < $MYTMP/ns/$ns/devlist
while read otherns otherdev
do
read errline < $MYTMP/runtime-lines/$otherns-pair-$otherdev
if [ ! -f $MYTMP/ns/$otherns/devices/$otherdev ]
then
error="$otherns/$otherdev not defined to be paired with"
exit 1
fi
done < $MYTMP/ns/$ns/pairlist
done < $MYTMP/nslist
if [ "$mode" = "stop" -o "$mode" = "start" ]
then
while read ns
do
read errline < $MYTMP/runtime-lines/$ns
error="shutting down namespace"
exists_ns $ns && shutdown_ns $ns
done < $MYTMP/nslist
while read ns
do
read errline < $MYTMP/runtime-lines/$ns
error="deleting namespace"
exists_ns $ns && $IP_CMD netns del $ns
done < $MYTMP/nslist
error=""
fi
if [ "$mode" = "stop" ]
then
exit 0
fi
if [ "$mode" = "start" ]
then
while read ns
do
read errline < $MYTMP/runtime-lines/$ns
error="adding namespace"
type="$($CAT_CMD $MYTMP/ns/$ns/type)"
$IP_CMD netns add $ns
if [ "$type" = "switch" ]
then
error="adding bridge to switch namespace"
$IP_CMD netns exec $ns $IP_CMD link add name switch type bridge
fi
done < $MYTMP/nslist
while read ns
do
type="$($CAT_CMD $MYTMP/ns/$ns/type)"
while read dev
do
read errline < $MYTMP/runtime-lines/$ns-dev-$dev
read ons odev < $MYTMP/ns/$ns/devicepairs/$dev
if [ "$ons" != "n/a" ]
then
error="adding virtual ethernet to $type namespace"
#$IP_CMD link add $dev netns $ns type veth peer netns $ons name $odev
$IP_CMD link add $dev netns $ns type veth peer name $odev
$IP_CMD link set $odev netns $ons
else
: # gets set up from the other end
fi
if [ "$type" = "switch" ]
then
error="adding virtual ethernet to bridge"
$IP_CMD netns exec $ns $IP_CMD link set dev $dev master switch
fi
while read ip
do
error="adding ip address to virtual ethernet"
if is_ipv6 $ip; then bc=""; v6="-6"; else bc="broadcast +"; v6=""; fi
$IP_CMD netns exec $ns $IP_CMD $v6 addr add $ip $bc dev $dev
done < $MYTMP/ns/$ns/devices/$dev
done < $MYTMP/ns/$ns/devlist
while read bridge
do
read errline < $MYTMP/runtime-lines/$ns-dev-$bridge
error="adding bridge to host namespace"
$IP_CMD netns exec $ns $IP_CMD link add name $bridge type bridge
while read dev
do
error="adding virtual interface to bridge"
$IP_CMD netns exec $ns $IP_CMD link set dev $dev master $bridge
done < $MYTMP/ns/$ns/devices/$bridge-bridged
while read ip
do
error="adding ip to virtual interface"
if is_ipv6 $ip; then bc=""; v6="-6"; else bc="broadcast +"; v6=""; fi
$IP_CMD netns exec $ns $IP_CMD $v6 addr add $ip $bc dev $bridge
done < $MYTMP/ns/$ns/devices/$bridge
done < $MYTMP/ns/$ns/bridgelist
done < $MYTMP/nslist
while read ns
do
echo "Starting namespace $ns"
read errline < $MYTMP/runtime-lines/$ns
error="starting namespace"
startup_ns $ns
while read route
do
errline=$($TR_CMD "\n" "/" < $MYTMP/runtime-lines/$ns-routes | $SED_CMD -e s:/$::)
error="adding route to $ns"
if is_ipv6 "$route"; then bc=""; v6="-6"; else bc="broadcast +"; v6=""; fi
$IP_CMD netns exec $ns $IP_CMD $v6 route add $route
done < $MYTMP/ns/$ns/routes
if [ -f $MYTMP/ns/$ns/exec ]
then
errline=$($TR_CMD "\n" "/" < $MYTMP/runtime-lines/$ns-exec | $SED_CMD -e s:/$::)
error="running exec for $ns"
$IP_CMD netns exec $ns $SH_CMD -e $MYTMP/ns/$ns/exec
fi
done < $MYTMP/nslist
error=""
fi
if [ "$mode" = "status" ]
then
while read ns
do
echo "---------------------- $ns --------------------"
if exists_ns $ns
then
# We remove the @whatever part of the device names since these
# appear to be highly unreliable when creating devices in different
# namespaces, at least under linux 4.3. Adding in two steps, like
# this did not help:
# $IP_CMD link add $dev netns $ns type veth peer name $odev
# $IP_CMD link set $odev netns $ons
$IP_CMD netns exec $ns $IP_CMD addr show | $SED_CMD 's/@[^:]*:/:/'
$IP_CMD netns exec $ns $IP_CMD route show
$IP_CMD netns exec $ns $BRIDGE_CMD link show
else
echo "Namespace not running"
fi
echo ""
done < $MYTMP/nslist
fi
if [ "$mode" = "graphviz" ]
then
gv=$MYTMP/gv
echo "/* process e.g.: $gvprog -Tps filename.gv -o filename.ps */" >$gv
echo "graph NET {" >>$gv
if [ "$format" != "png" ]
then
echo "size=7; /* Max size 7 inches */" >>$gv
fi
echo "overlap=prism;" >>$gv
echo "edge [color=blue,style=dashed];" >>$gv
while read ns
do
type="$($CAT_CMD $MYTMP/ns/$ns/type)"
if [ "$type" = "switch" ]
then
echo "switch_$ns [shape=polygon,sides=4,skew=.4,label=\"$ns\"];" >>$gv
else
echo -n "host_$ns [shape=record,label=\"$ns" >>$gv
while read route
do
echo -n "\\n$route" >>$gv
done < $MYTMP/ns/$ns/routes
while read bridge
do
echo -n "|{<$bridge> $bridge" >>$gv
while read ip
do
echo -n "\\n$ip" >>$gv
done < $MYTMP/ns/$ns/devices/$bridge
while read dev
do
echo -n "|{" >>$gv
echo -n "<$dev> $dev" >>$gv
while read ip
do
echo -n "\n$ip" >>$gv
done < $MYTMP/ns/$ns/devices/$dev
echo -n "}" >>$gv
echo "$bridge" > $MYTMP/ns/$ns/suppress-$dev
done < $MYTMP/ns/$ns/devices/$bridge-bridged
echo -n "}" >>$gv
done < $MYTMP/ns/$ns/bridgelist
while read dev
do
if [ ! -f $MYTMP/ns/$ns/suppress-$dev ]
then
echo -n "|<$dev> $dev" >>$gv
while read ip
do
echo -n "\\n$ip" >>$gv
done < $MYTMP/ns/$ns/devices/$dev
fi
done < $MYTMP/ns/$ns/devlist
echo "\"];" >>$gv
fi
done < $MYTMP/nslist
while read ns
do
type="$($CAT_CMD $MYTMP/ns/$ns/type)"
while read dev
do
read ons odev < $MYTMP/ns/$ns/devicepairs/$dev
if [ "$ons" != "n/a" ]
then
otype="$($CAT_CMD $MYTMP/ns/$ons/type)"
if [ "$type" = "switch" ]
then
from="switch_$ns"
else
from="host_$ns:$dev"
fi
if [ "$otype" = "switch" ]
then
to="switch_$ons"
else
to="host_$ons:$odev"
fi
echo "$from -- $to;" >>$gv
else
: # gets set up from the other end
fi
done < $MYTMP/ns/$ns/devlist
done < $MYTMP/nslist
echo "}" >>$gv
if [ "$outfile" = "" ]
then
$graphviz $gv
else
$graphviz $gv > "$outfile"
fi
fi
exit 0