|
#!/bin/bash |
|
|
|
PREFIX=/run/netns |
|
ACTION=${1} |
|
VLAN=100 |
|
SELF=$$ |
|
|
|
log() { |
|
# log(): print and log all options to /tmp/portal/$VLAN.log |
|
echo "[PRE] $@" | tee -a /tmp/portal.$VLAN.log |
|
} |
|
|
|
fail() { |
|
log $@ |
|
exit 1 |
|
} |
|
|
|
default_exec() { |
|
# default_exec(): execute commands in the default namespace |
|
# there is a nsenter from pypi, we won't want that one |
|
/usr/bin/nsenter --net --mount -t 1 -- $@ |
|
} |
|
|
|
ipn() { |
|
# ipn(): execute /sbin/ip commands in a specific namespace |
|
ns=$1 |
|
shift |
|
/sbin/ip -n $ns $@ |
|
} |
|
|
|
capture() { |
|
# capture(): conditionally capture device from one namespace to another |
|
# params: |
|
# $1 => interface name |
|
# $2 => source namespace name (default: `default`) |
|
# $3 => destination namespace name or pid (default: $SELF) |
|
intf=$1 |
|
src=${2:-default} |
|
dest=${3:-$SELF} |
|
|
|
# important: grep -w to match vlan226@ and not vlan2265@ |
|
ipn $dest -o link | grep -wq $intf |
|
if [ $? -eq 0 ]; then |
|
log "Device $intf exists in namespace, skipping capture" |
|
return 0 |
|
fi |
|
|
|
ipn $src -o link | grep -wq $intf |
|
if [ $? -gt 0 ]; then |
|
fail "BUG: Device $intf doesn't exist in source namespace: $src" |
|
fi |
|
|
|
log "Device $intf not found in namespace $dest, capturing from $src" |
|
move_intf_netns $intf $src $dest |
|
} |
|
|
|
move_intf_netns() { |
|
# move_intf_netns(): move interface from one namespace to another |
|
# params: |
|
# $1 => interface name |
|
# $2 => source namespace name (default: `default`) |
|
# $3 => destination namespace name or pid (default: $SELF) |
|
|
|
intf=$1 |
|
src=${2:-default} |
|
dest=${3:-$SELF} |
|
log "moving $intf from $src to $3" |
|
ipn $src link set $intf netns $dest || fail "failed to capture $intf" |
|
} |
|
|
|
ns_add() { |
|
# ns_add(): add network namespace bind that iproute2 can manage |
|
# params: |
|
# $1 => network namespace name |
|
# $2 => pid containing network namespace (default: $SELF) |
|
|
|
nn=$PREFIX/$1 |
|
netns=${2:-$SELF} |
|
log "trying to preserve namespace pid:$netns to $nn" |
|
|
|
if ! mountpoint $PREFIX; then |
|
fail "$PREFIX is not a mountpoint" |
|
fi |
|
|
|
# checking to see if this mount is the same as the namespace |
|
# we want to bind. We don't want to clobber what's there as |
|
# it is indicitive of a bug |
|
if mountpoint $nn; then |
|
current_ns=$(awk "\$5==\"$PREFIX\" { print \$4 }" /proc/$SELF/mountinfo) |
|
target_ns=$(readlink /proc/$SELF/ns/net) |
|
|
|
if [ "$current_ns" != "$target_ns"]; then |
|
log "!!! attempted to add a namespace over an existing namespace" |
|
fail "BUG: existing namespace $current_ns != $target_ns" |
|
else |
|
log $(default_exec mountpoint $nn) |
|
return 0 |
|
fi |
|
fi |
|
|
|
log "attempting create bind mount $mnt" |
|
touch $nn || fail "failed to touch $nn" |
|
default_exec mount --bind /proc/$netns/ns/net $nn || fail "failed to bind /proc/$SELF/ns/net to $nn" |
|
default_exec mount --make-slave $nn |
|
log $(default_exec mountpoint $nn) |
|
} |
|
|
|
ns_rm() { |
|
# ns_rm(): remove namespace |
|
|
|
nn=$PREFIX/$1 |
|
if ! mountpoint $nn; then |
|
log "namespace mount, $nn is not mounted" |
|
default_exec rm -f $nn |
|
fi |
|
|
|
default_exec umount $nn || fail "failed to umount $nn" |
|
default_exec rm -f $nn || fail "failed to remove $nn" |
|
} |
|
|
|
setup_default_namespace() { |
|
# setup_default_namespace(): add network namespace called `default` that iproute2 can manage |
|
# params: none |
|
if [[ ! -e $PREFIX/default ]]; then |
|
log "Missing default netns, adding" |
|
ns_add "default" 1 |
|
fi |
|
} |
|
|
|
# main() |
|
interface="vlan${VLAN}" |
|
current_namespace=$(readlink /proc/$SELF/ns/net) |
|
default_namespace=$(readlink /proc/1/ns/net) |
|
netns="portal" |
|
|
|
log "Current namespace: $current_namespace" |
|
log "Default namespace: $default_namespace" |
|
|
|
if [ "$current_namespace" == "$default_namespace" ]; then |
|
# service definitions are incorrect! |
|
fail "BUG: Running in parent namespace" |
|
fi |
|
|
|
if [[ $EUID -ne 0 ]]; then |
|
fail "BUG: not running as root" |
|
fi |
|
|
|
case $ACTION in |
|
start) |
|
# create namespace bind so it's preserved |
|
ns_add $netns |
|
capture $interface default $netns |
|
;; |
|
stop) |
|
capture $interface $netns default |
|
ns_rm $netns |
|
;; |
|
*) |
|
fail "unknown action: $ACTION" |
|
;; |
|
esac |
|
|
|
exit 0 |