NAMESRV=
# Program with optional arguments to run in the new network namespace:
-XCMD1=
-XCMD1_ARGS=
+XCMD=
# Virtual Ethernet to connect new network namespace to default namespace:
VETH_HOST=veth_host
VETH_GUEST=veth_guest
+# Set to 'true' to enable debugging output:
+DEBUG=false
+
#
# End of configuration.
######################################################################
-#set -x
-
-SHORTNAME=${0##*/}
-
usemsg() {
- echo "Usage: $1 name start|stop|restart|exec cmd [arg ...]"
+ echo "Usage: $1 name start|stop|restart"
+ echo " $1 name exec cmd [arg ...]"
echo " where name is the name of a config file or a network namespace name,"
echo " and cmd is a command to execute in the context of an existing namespace."
}
+SHORTNAME=${0##*/}
if [ $# -lt 2 ]; then
usemsg $SHORTNAME
exit 1
fi
-# Become root if not already:
-[ "root" != "$USER" ] && exec sudo -E $0 "$@"
-
# Check, if first argument looks like a config file name and
# if so source it. Otherwise $1 is interpreted as namespace name.
if [ -f "$1" ] && [ -r "$1" ] ; then
. "$1"
else
- # Clear the proposed namespace name of blank characters:
- NNSNAME=${1//[[:space:]]}
+ NNSNAME="$1"
fi
+[ "$DEBUG" == "true" ] && set -x
+
+# Clear the proposed namespace name of blank characters:
+NNSNAME=${NNSNAME//[[:space:]]}
# Check and or sanitize some settings:
if [ -z "$NNSNAME" ] ; then
echo "No network namespace name provided."
[ -z "$BRIDGE_IF" ] && BRIDGE_IF="br0"
[ -z "$VETH_HOST" ] && VETH_HOST="veth_host"
[ -z "$VETH_GUEST" ] && VETH_GUEST="veth_guest"
+[ -z "$XCMD" ] && XCMD="$XCMD1 $XCMD1_ARGS"
-
-# Locate essential commands:
-IP=$(command -v ip)
-if [ -z $IP ] ; then
- echo "please install the iproute2 package"
- exit 1
-fi
-IPTABLES=$(command -v iptables)
-if [ -z $IPTABLES ] ; then
- echo "please install the iptables package"
- exit 1
-fi
-if [ -z "$BRIDGE_ADDR" ] || [ -z "$GUEST_ADDR" ] ; then
- DHCLIENT=$(command -v dhclient)
- if [ -z $DHCLIENT ] ; then
- echo "please install the dhclient package"
+locate_cmd() {
+ local cmd=$(command -v "$2")
+ if [ -z "$cmd" ] ; then
+ local pkg="$2" ; [ -n "$3" ] && pkg="$3"
+ echo "please install the $pkg package"
exit 1
fi
+ eval "$1=$cmd"
+}
+
+# Become root if not already:
+if [ "$EUID" != "0" ] ; then
+ locate_cmd SUDO sudo
+ exec $SUDO -E $0 "$@"
fi
-# Locate user provided command:
-if [ -n "$XCMD1" ] ; then
- XCMD=$(command -v $XCMD1)
- if [ -z $XCMD ] ; then
- echo "command not found: $XCMD1"
- exit 1
- fi
+# Locate essential commands:
+locate_cmd GREP grep
+locate_cmd AWK awk
+locate_cmd IP ip iproute2
+locate_cmd IPTABLES iptables
+if [ -z "$BRIDGE_ADDR" ] || [ -z "$GUEST_ADDR" ] ; then
+ locate_cmd DHCLIENT dhclient
fi
# Assign and create directory to keep runtime information:
RUNDIR=/var/run/newns/$NNSNAME
/bin/mkdir -p $RUNDIR
-# Get IPv4 address/prefix_length of an interface:
+# Get first IPv4 address/prefix_length of an interface:
get_ip_pfl() {
- $IP addr show $1 | grep -F 'inet ' | awk -F ' ' '{print $2}'
+ $IP addr show $1 | $GREP -m 1 -F 'inet ' | $AWK -F ' ' '{print $2}'
}
# IPv4 math helpers; shamelessly ripped from:
# Check whether a namespace with given name exists:
exist_nns() {
- $IP netns list | /bin/grep -F $1 1>/dev/null 2>&1
+ $IP netns list | $GREP -F $1 1>/dev/null 2>&1
+}
+
+
+# Run specified command inside a network namespace:
+run_nns() {
+ if ! exist_nns $1 ; then
+ echo "Network namespace $1 does not exist, please choose another name."
+ exit 1
+ fi
+ locate_cmd SU su "su (util-linux?)"
+ local NNS=$1
+ shift
+ local bname=${1##*/}
+ $IP netns exec $NNS $SU -p -c "$*" $SUDO_USER &
+ $IP netns exec $NNS echo "$!" > "$RUNDIR/$bname.$!.pid"
}
fi
# Enable IPv4 forwarding after saving the previous state:
- cat /proc/sys/net/ipv4/ip_forward > "$RUNDIR/ipv4fwd.sav"
+ echo $(</proc/sys/net/ipv4/ip_forward) > "$RUNDIR/ipv4fwd.sav"
echo 1 > /proc/sys/net/ipv4/ip_forward
# Take care of DNS in the new namespace:
echo "nameserver $NS" >> /etc/netns/$1/resolv.conf
done
else
- cp /etc/resolv.conf /etc/netns/$1/resolv.conf
+ /bin/cp /etc/resolv.conf /etc/netns/$1/resolv.conf
fi
# Create the new namespace, the veth cable and the bridge interface:
# Run the configured command, if any, inside the namespace:
if [ -n "$XCMD" ] ; then
- $IP netns exec $1 su -p -c "$XCMD $XCMD1_ARGS" $SUDO_USER &
- $IP netns exec $1 echo "$!" > "$RUNDIR/xcmd_.pid"
+ run_nns $1 $XCMD
fi
}
for PIDF in $(find $RUNDIR -maxdepth 1 -name '*.pid' 2> /dev/null) ; do
# Note: This can lead to nasty surprises in case a process
# already terminated and its PID has been reused!
- /bin/kill -TERM $(cat "$PIDF")
+ # The check below is insufficient and by no means safe:
+ local cmd=${PIDF##*/}
+ if $GREP -F "${cmd%%.*}" "/proc/$(<"$PIDF")/cmdline" 1>/dev/null 2>&1 ; then
+ /bin/kill -TERM $(<"$PIDF")
+ else
+ echo "Not killing $(<"$PIDF") [$cmd]: no matching process found."
+ fi
/bin/rm "$PIDF"
done
# Restore saved state of IPv4 forwarding:
- cat $RUNDIR/ipv4fwd.sav > /proc/sys/net/ipv4/ip_forward
- rm $RUNDIR/ipv4fwd.sav
+ echo $(<"$RUNDIR/ipv4fwd.sav") > /proc/sys/net/ipv4/ip_forward
+ /bin/rm $RUNDIR/ipv4fwd.sav
# Burn the bridge:
IP_PFL=$(get_ip_pfl $BRIDGE_IF)
}
-# Run a specified command inside a network namespace:
-run_nns() {
- if ! exist_nns $1 ; then
- echo "Network namespace $1 does not exist, please choose another name."
- exit 1
- fi
- NNS=$1
- shift
- # Run the specified command inside the namespace:
- $IP netns exec $NNS su -p -c "$*" $SUDO_USER &
-}
-
-
case $2 in
start)
start_nns $NNSNAME