* Added `DEBUG` option to enable verbose debugging output. master
authorUrban Wallasch <urban.wallasch@freenet.de>
Wed, 24 Apr 2019 11:02:58 +0000 (13:02 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Wed, 24 Apr 2019 11:02:58 +0000 (13:02 +0200)
* Only consider the first configured IP address in get_ip_pfl().
* Perform a simple sanity check before killing arbitrary PIDs.
* Use single combined $XCMD option (fallback to $XCMD1 plus $XCMD1_ARGS provided).
* Avoid gratuitous use of cat.
* Use absolute paths for basic utilities.
* Lots of small improvements.

nns.sh
nns_example.cfg

diff --git a/nns.sh b/nns.sh
index 271b6eb8e304c9c41ef4f6a743786019f91f7689..d195eab7f95684f68094f308185f9a5b9c28d103 100755 (executable)
--- a/nns.sh
+++ b/nns.sh
@@ -42,45 +42,45 @@ BRIDGE_MAC=
 NAMESRV=
 
 # Program with optional arguments to run in the new network namespace:
 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
 
 
 # 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.
 ######################################################################
 
 
 #
 # End of configuration.
 ######################################################################
 
 
-#set -x
-
-SHORTNAME=${0##*/}
-
 usemsg() {
 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."
 }
 
     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
 
 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
 # 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
 
 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."
 # Check and or sanitize some settings:
 if [ -z "$NNSNAME" ] ; then
     echo "No network namespace name provided."
@@ -95,43 +95,40 @@ fi
 [ -z "$BRIDGE_IF" ] && BRIDGE_IF="br0"
 [ -z "$VETH_HOST" ] && VETH_HOST="veth_host"
 [ -z "$VETH_GUEST" ] && VETH_GUEST="veth_guest"
 [ -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
         exit 1
     fi
+    eval "$1=$cmd"
+}
+
+# Become root if not already:
+if [ "$EUID" != "0" ] ; then
+    locate_cmd SUDO sudo
+    exec $SUDO -E $0 "$@"
 fi
 
 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
 
 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() {
 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:
 }
 
 # IPv4 math helpers; shamelessly ripped from:
@@ -175,7 +172,22 @@ network() {
 
 # Check whether a namespace with given name exists:
 exist_nns() {
 
 # 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"
 }
 
 
 }
 
 
@@ -187,7 +199,7 @@ start_nns() {
     fi
 
     # Enable IPv4 forwarding after saving the previous state:
     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 1 > /proc/sys/net/ipv4/ip_forward
 
     # Take care of DNS in the new namespace:
@@ -198,7 +210,7 @@ start_nns() {
             echo "nameserver $NS" >> /etc/netns/$1/resolv.conf
         done
     else
             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:
     fi
 
     # Create the new namespace, the veth cable and the bridge interface:
@@ -241,8 +253,7 @@ start_nns() {
 
     # Run the configured command, if any, inside the namespace:
     if [ -n "$XCMD" ] ; then
 
     # 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
 }
 
     fi
 }
 
@@ -258,13 +269,19 @@ stop_nns() {
     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!
     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:
         /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)
 
     # Burn the bridge:
     IP_PFL=$(get_ip_pfl $BRIDGE_IF)
@@ -293,19 +310,6 @@ stop_nns() {
 }
 
 
 }
 
 
-# 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
 case $2 in
     start)
         start_nns $NNSNAME
index 2b55d0f7009db76a17e1c4a2638b3408fa5f88e0..9cfa16a84e146fbb15e2149f3afefc9d8c88079f 100644 (file)
@@ -28,11 +28,12 @@ BRIDGE_MAC=FE:17:90:00:BA:BE
 # namespace. If empty, the host resolv.conf will be copied:
 NAMESRV="8.8.8.8 8.8.4.4"
 
 # namespace. If empty, the host resolv.conf will be copied:
 NAMESRV="8.8.8.8 8.8.4.4"
 
-# Program with optional arguments to run in the new network namespace:
-XCMD1="xterm"
-XCMD1_ARGS="-title $NNSNAME"
-
 # Virtual Ethernet to connect new network namespace to default namespace:
 VETH_HOST=veth_host
 VETH_GUEST=veth_guest
 
 # Virtual Ethernet to connect new network namespace to default namespace:
 VETH_HOST=veth_host
 VETH_GUEST=veth_guest
 
+# Program with optional arguments to run in the new network namespace:
+XCMD="xterm -title $NNSNAME"
+
+# Set to 'true' to enable debugging output:
+DEBUG=false