From c5aad5183e1d252bb8ca1e8da35ed9880bc61060 Mon Sep 17 00:00:00 2001 From: Urban Wallasch Date: Mon, 22 Apr 2019 12:53:41 +0200 Subject: [PATCH] * Moved runtime info directory from /var/log/ to /var/run/. * Save IPv4 forwarding state on start and restore it on stop. * Replaced `ifconfig` by equivalent `ip` invocations. * Renamed some variables. * Removed more clutter. --- nns.sh | 178 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 90 insertions(+), 88 deletions(-) diff --git a/nns.sh b/nns.sh index 9de197a..97f5f0e 100755 --- a/nns.sh +++ b/nns.sh @@ -1,8 +1,8 @@ #!/bin/bash # -# This script will setup a network namespace with a macvlan -# which obtains its IP address via dhclient from the LAN on which the host is -# placed +# This script will setup a network namespace around a specified base +# interface, connect it with a veth virtual ethernet to a bridge in +# the host (default) network namespace. # ############################### @@ -16,8 +16,6 @@ # # * (Run dhclient to obtain bridge IP, if none was specified?) # -# * Add config option to disable IP forwarding on stop? -# # * Add "run" command to start a process in already existing namespace # via `ip netns exec`. # @@ -26,43 +24,43 @@ ############################### -set -x - -# The script will temporarily activate ip forwarding for you. If you -# do not wish to retain this feature, you will have to issue, at the -# end of this session, the command -# echo 0 > /proc/sys/net/ipv4/ip_forward -# yourself. - -############################################################################### - ############################### -# Configuration variables +# Configuration variables: BASE_IF=vpn_tap1 -BASE_NET=192.168.30.0/24 -BR_NAME=br0 -BR_IP=192.168.30.3 + +GUEST_ADDR=dhcp +GUEST_NET=192.168.30.0/24 + +BRIDGE_IF=br0 +BRIDGE_ADDR=192.168.30.3 # List of nameservers to provide DNS in the new network namespace: -NAMESRV="8.8.8.8 8.8.4.4" +NAMESRV="208.67.222.222 204.152.184.76 8.8.8.8" # Program to run in the new network namespace: -XTERM1=xterm +XCMD1=xterm # Virtual Ethernet to connect new network namespace to default namespace: VETH_HOST=veth_host VETH_GUEST=veth_guest -# +# Configuration end. ############################### + +set -x + SHORTNAME=${0##*/} +usemsg() { + echo "Usage: $1 name action" + echo "where name is the network namespace name" + echo "and action is one of start| stop| reload." +} + if [ $# -lt 2 ]; then - echo "Usage $SHORTNAME name action" - echo "where name is the network namespace name," - echo " and action is one of start| stop| reload." + usemsg $SHORTNAME exit 1 fi @@ -80,39 +78,32 @@ if [ -z $IPTABLES ] ; then echo "please install the iptables package" exit 1 fi -XTERM=$(command -v $XTERM1) -if [ -z $XTERM ] ; then - echo "please install the $XTERM1 package" +DHCLIENT=$(command -v dhclient) +if [ -z $DHCLIENT ] ; then + echo "please install the dhclient package" + exit 1 +fi +XCMD=$(command -v $XCMD1) +if [ -z $XCMD ] ; then + echo "please install the $XCMD1 package" exit 1 fi -prelim() { -# Perform some preliminary setup. First, clear the proposed -# namespace name of blank characters; then create a directory -# for logging info, and a pid file in it; lastly, enable IPv4 -# forwarding. - - VAR=$1 - export NNSNAME=${VAR//[[:space:]]} - - export OUTDIR=/var/log/newns/$NNSNAME - - if [ ! -d $OUTDIR ]; then - /bin/mkdir -p $OUTDIR - fi - export PID=$OUTDIR/pid$NNSNAME - - echo 1 > /proc/sys/net/ipv4/ip_forward +# Clear the proposed namespace name of blank characters: +NNSNAME=${1//[[:space:]]} -} +# Assign and create directory to keep runtime information: +RUNDIR=/var/run/newns/$NNSNAME +/bin/mkdir -p $RUNDIR # Check whether a namespace with given name exists: exist_nns() { $IP netns list | /bin/grep $1 2> /dev/null } -# Create and configure a new network namespace: + +# Set up a new network namespace: start_nns() { if exist_nns $1 ; then echo "Network namespace $1 already exists," @@ -120,38 +111,50 @@ start_nns() { exit 1 fi - # Take care of DNS: + # Enable IPv4 forwarding after saving the previous state: + cat /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: /bin/mkdir -p /etc/netns/$1 /bin/rm /etc/netns/$1/resolv.conf 2> /dev/null for NS in $NAMESRV ; do echo "nameserver $NS" >> /etc/netns/$1/resolv.conf done - # Create the new namespace, the veth interface and the bridge: + # Create the new namespace, the veth cable and the bridge interface: $IP netns add $1 $IP link add $VETH_HOST type veth peer name $VETH_GUEST - brctl addbr $BR_NAME - brctl addif $BR_NAME $BASE_IF - brctl addif $BR_NAME $VETH_HOST - ifconfig $VETH_HOST up - ifconfig $BR_NAME $BR_IP up - -# This assigns the macvlan interface, mac$1, to the new -# namespace, asks for an IP address via a call to dhclient, -# brings up this and the (essential) lo interface, -# creates a new terminal in the new namespace and -# stores its pid for the purpose of tearing it cleanly, later. - + brctl addbr $BRIDGE_IF + brctl addif $BRIDGE_IF $BASE_IF + brctl addif $BRIDGE_IF $VETH_HOST + $IP link set dev $VETH_HOST up + $IP addr add $BRIDGE_ADDR dev $BRIDGE_IF + $IP link set dev $BRIDGE_IF up + + # Assign the veth guest interface to the new network namespace, + # set an IP address (either static or via dhclient) to it, bring + # up this and the lo interface: $IP link set $VETH_GUEST netns $1 - $IP netns exec $1 /sbin/dhclient -pf /var/run/dhclient_$1.pid -v $VETH_GUEST 1> /dev/null 2>&1 + if [ "$GUEST_ADDR" == "dhcp" ] ; then + $IP netns exec $1 $DHCLIENT -pf "$RUNDIR/dhclient.pid" -v $VETH_GUEST 1> /dev/null 2>&1 + else + $IP netns exec $1 $IP addr add $GUEST_ADDR dev $VETH_GUEST + $IP netns exec $1 $IP link set dev $VETH_GUEST up + fi $IP netns exec $1 $IP link set dev lo up - $IP netns exec $1 su -p -c $XTERM $SUDO_USER & - $IP netns exec $1 echo "$!" > $PID - $IPTABLES -t nat -A POSTROUTING --source $BASE_NET --jump MASQUERADE - $IP netns exec $1 /sbin/route add default gw $BR_IP + # Set up routing: + $IPTABLES -t nat -A POSTROUTING --source $GUEST_NET --jump MASQUERADE + $IP netns exec $1 /sbin/route add default gw $BRIDGE_ADDR + + # Run the configured user process inside the namespace: + $IP netns exec $1 su -p -c "$XCMD" $SUDO_USER & + $IP netns exec $1 echo "$!" > "$RUNDIR/xcmd_.pid" } + +# Tear down a previously created network namespace and clean up the mess: stop_nns() { if ! exist_nns $1 ; then echo "Network namespace $1 does not exist," @@ -159,53 +162,52 @@ stop_nns() { exit 1 fi -# This kills the terminal in the separate namespace and -# removes the file and the directory where it is stored. + # Kill processes started in the separate namespace: + for PIDF in $RUNDIR/*.pid ; do + /bin/kill -TERM $(cat "$PIDF") + /bin/rm "$PIDF" + done - /bin/kill -TERM $(cat $PID) 2> /dev/null 1> /dev/null - /bin/kill -TERM $(cat /var/run/dhclient_$1.pid) 2> /dev/null 1> /dev/null - /bin/rm $PID - /bin/rmdir $OUTDIR + # Restore saved state of IPv4 forwarding: + cat $RUNDIR/ipv4fwd.sav > /proc/sys/net/ipv4/ip_forward + rm $RUNDIR/ipv4fwd.sav - ifconfig $BR_NAME down - brctl delif $BR_NAME $BASE_IF - brctl delif $BR_NAME $VETH_HOST - brctl delbr $BR_NAME + # Burn the bridge: + $IP link set dev $BRIDGE_IF down + brctl delif $BRIDGE_IF $BASE_IF + brctl delif $BRIDGE_IF $VETH_HOST + brctl delbr $BRIDGE_IF + # Unplug the virtual cable and remove network namespace: $IP netns exec $1 $IP link del $VETH_GUEST - $IP netns del $1 $IP link del $VETH_HOST - $IPTABLES -t nat -D POSTROUTING --source $BASE_NET --jump MASQUERADE - -# This deletes the file and direcotory connected with the DNSes. + $IP netns del $1 + $IPTABLES -t nat -D POSTROUTING --source $GUEST_NET --jump MASQUERADE + # Cleanup namespace DNS configuration: /bin/rm /etc/netns/$1/resolv.conf /bin/rmdir /etc/netns/$1 + # Remove runtime dir: + /bin/rmdir $RUNDIR } case $2 in start) - prelim "$1" start_nns $NNSNAME ;; stop) - prelim "$1" stop_nns $NNSNAME ;; reload|restart) - prelim "$1" stop_nns $NNSNAME - prelim "$1" start_nns $NNSNAME ;; - run) + run|exec) echo "TO-DO: implement '$SHORTNAME run ' action." ;; *) - echo "Usage: $SHORTNAME name action," - echo "where name is the name of the network namespace," - echo "and action is one of start|stop|reload" + usemsg $SHORTNAME ;; esac -- 2.30.2