diff --git a/install.sh b/install.sh
index c5a45ed..9bdf6e0 100755
--- a/install.sh
+++ b/install.sh
@@ -126,4 +126,5 @@ install_data blink/51-blink.rules                        "$DESTDIR$udev_rulesdir
 install_data clusterd/clusterd.service                   "$DESTDIR$systemdunitdir/clusterd.service"
 install_exec clusterd/clusterd                           "$DESTDIR$usr_sbindir/clusterd"
 install_exec setuid/setuid                               "$DESTDIR$usr_sbindir/setuid"
+install_exec uvpn/uvpn                                   "$DESTDIR$usr_bindir/uvpn"
 exit
diff --git a/uvpn/dhclient-script b/uvpn/dhclient-script
new file mode 100755
index 0000000..52183e1
--- /dev/null
+++ b/uvpn/dhclient-script
@@ -0,0 +1,124 @@
+#!/bin/bash
+
+ip=/sbin/ip
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$new_broadcast_address != x ]; then
+  new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+  old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+  new_subnet_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+  old_subnet_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+  alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+if [ x$new_interface_mtu != x ]; then
+  mtu_arg="mtu $new_interface_mtu"
+fi
+if [ x$IF_METRIC != x ]; then
+  metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+   [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+
+  if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+		[ x$alias_ip_address != x$old_ip_address ]; then
+    # Possible new alias. Remove old alias.
+    ifconfig $interface:0- inet 0
+  fi
+  if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then
+    # IP address changed. Bringing down the interface will delete all routes,
+    # and clear the ARP cache.
+    ifconfig $interface inet 0 down
+
+  fi
+  if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+     [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+
+    ifconfig $interface inet $new_ip_address $new_subnet_arg \
+					$new_broadcast_arg $mtu_arg
+    # Add a network route to the computed network address.
+    for router in $new_routers; do
+      if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+        ip route add $router dev $interface
+      fi
+      ip route add default via $router dev $interface $metric_arg
+    done
+  else
+    # we haven't changed the address, have we changed other options
+    # that we wish to update?
+    if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+      # if we've changed routers delete the old and add the new.
+      for router in $old_routers; do
+        ip route del default via $router
+      done
+      for router in $new_routers; do
+        if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+          ip route add $router dev $interface
+        fi
+        ip route add default via $router dev $interface $metric_arg
+      done
+    fi
+  fi
+  if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+   then
+    ifconfig $interface:0- inet 0
+    ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+    ip route add $alias_ip_address dev $interface:0
+  fi
+  exit 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+   || [ x$reason = xSTOP ]; then
+  if [ x$alias_ip_address != x ]; then
+    # Turn off alias interface.
+    ifconfig $interface:0- inet 0
+  fi
+  if [ x$old_ip_address != x ]; then
+    # Shut down interface, which will delete routes and clear arp cache.
+    ifconfig $interface inet 0 down
+  fi
+  if [ x$alias_ip_address != x ]; then
+    ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+    ip route add $alias_ip_address dev $interface:0
+  fi
+  exit 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+  if [ x$alias_ip_address != x ]; then
+    ifconfig $interface:0- inet 0
+  fi
+  ifconfig $interface inet $new_ip_address $new_subnet_arg \
+					$new_broadcast_arg $mtu_arg
+  set $new_routers
+  if ping -q -c 1 $1; then
+    if [ x$new_ip_address != x$alias_ip_address ] && \
+			[ x$alias_ip_address != x ]; then
+      ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+      ip route add $alias_ip_address dev $interface:0
+    fi
+    for router in $new_routers; do
+      if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+        ip route add $router dev $interface
+      fi
+      ip route add default via $router dev $interface $metric_arg
+    done
+    exit 0
+  fi
+  ifconfig $interface inet 0 down
+  exit 1
+fi
+
+exit 0
diff --git a/uvpn/uvpn b/uvpn/uvpn
new file mode 100755
index 0000000..c678551
--- /dev/null
+++ b/uvpn/uvpn
@@ -0,0 +1,303 @@
+#! /bin/bash
+
+die() {
+	echo "$@">&2
+	exit 1
+}
+
+die_usage() {
+	cat <<EOF
+  usage:
+    $0 start           # start uvpn container
+    $0 stop            # stop uvpn container
+    $0 exec [cmd...]   # execute cmd (default bash) in the container
+    $0 show [user...]  # show processes
+
+    $0 start_as_root    # sudo callback - internal use
+    $0 stop_as_root     # sudo callback - internal use
+EOF
+	exit 1
+}
+
+create_dhclient-script() {
+	cat >/var/run/uvpn/$USER/dhclient-script << '_EOF_'
+#!/bin/bash
+
+ip=/sbin/ip
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$new_broadcast_address != x ]; then
+  new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+  old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+  new_subnet_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+  old_subnet_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+  alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+if [ x$new_interface_mtu != x ]; then
+  mtu_arg="mtu $new_interface_mtu"
+fi
+if [ x$IF_METRIC != x ]; then
+  metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+   [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+
+  if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+		[ x$alias_ip_address != x$old_ip_address ]; then
+    # Possible new alias. Remove old alias.
+    ifconfig $interface:0- inet 0
+  fi
+  if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then
+    # IP address changed. Bringing down the interface will delete all routes,
+    # and clear the ARP cache.
+    ifconfig $interface inet 0 down
+
+  fi
+  if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+     [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+
+    ifconfig $interface inet $new_ip_address $new_subnet_arg \
+					$new_broadcast_arg $mtu_arg
+    # Add a network route to the computed network address.
+    for router in $new_routers; do
+      if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+        ip route add $router dev $interface
+      fi
+      ip route add default via $router dev $interface $metric_arg
+    done
+  else
+    # we haven't changed the address, have we changed other options
+    # that we wish to update?
+    if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+      # if we've changed routers delete the old and add the new.
+      for router in $old_routers; do
+        ip route del default via $router
+      done
+      for router in $new_routers; do
+        if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+          ip route add $router dev $interface
+        fi
+        ip route add default via $router dev $interface $metric_arg
+      done
+    fi
+  fi
+  if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+   then
+    ifconfig $interface:0- inet 0
+    ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+    ip route add $alias_ip_address dev $interface:0
+  fi
+  exit 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+   || [ x$reason = xSTOP ]; then
+  if [ x$alias_ip_address != x ]; then
+    # Turn off alias interface.
+    ifconfig $interface:0- inet 0
+  fi
+  if [ x$old_ip_address != x ]; then
+    # Shut down interface, which will delete routes and clear arp cache.
+    ifconfig $interface inet 0 down
+  fi
+  if [ x$alias_ip_address != x ]; then
+    ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+    ip route add $alias_ip_address dev $interface:0
+  fi
+  exit 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+  if [ x$alias_ip_address != x ]; then
+    ifconfig $interface:0- inet 0
+  fi
+  ifconfig $interface inet $new_ip_address $new_subnet_arg \
+					$new_broadcast_arg $mtu_arg
+  set $new_routers
+  if ping -q -c 1 $1; then
+    if [ x$new_ip_address != x$alias_ip_address ] && \
+			[ x$alias_ip_address != x ]; then
+      ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+      ip route add $alias_ip_address dev $interface:0
+    fi
+    for router in $new_routers; do
+      if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+        ip route add $router dev $interface
+      fi
+      ip route add default via $router dev $interface $metric_arg
+    done
+    exit 0
+  fi
+  ifconfig $interface inet 0 down
+  exit 1
+fi
+
+exit 0
+_EOF_
+	chmod +x /var/run/uvpn/$USER/dhclient-script
+}
+
+have_interface() {
+	local if="$1"
+	ip link show "$1" >/dev/null 2>&1
+}
+
+cmd_start() {
+	test $# -eq 0 || die_usage
+
+	sudo $0 start_as_root || exit 1
+	create_dhclient-script
+	cat >/var/run/uvpn/$USER/resolv.conf<<_EOF_
+search molgen.mpg.de
+nameserver 141.14.16.1
+_EOF_
+	cmd_exec ip link set lo up
+	cmd_exec ip link set "uvpn.$USER.1" up
+	cmd_exec mount --bind /var/run/uvpn/$USER/resolv.conf /etc/resolv.conf
+	cmd_exec dhclient -4 -v \
+		-lf /var/run/uvpn/$USER/dhclient.leases \
+		-pf /var/run/uvpn/$USER/dhclient.pid \
+		-sf /var/run/uvpn/$USER/dhclient-script \
+		uvpn.$USER.1
+	exit
+}
+
+cmd_start_as_root() {
+	test $# -eq 0 || die_usage
+
+	test $(id -u) -eq 0 || die "must be root"
+	user="$SUDO_USER"
+	test -n "$user" || die "must be called via sudo"
+	uid=$(id -u "$user") 2>/dev/null || die "$user: no such user"
+	umask 022
+	have_interface vlan.guest0 || die "vlan.guest0 not available. This system is not prepared to run $0. Please contact helpdesk@molgen.mpg.de"
+
+	findmnt "/run/uvpn/$user/ns" >/dev/null 2>&1 && die "container already started"
+
+	if ! have_interface "uvpn.$user.0"; then
+		ip link add "uvpn.$user.0" type veth peer name "uvpn.$user.1"
+	fi
+	if ! have_interface br.guest0; then
+		brctl addbr br.guest0
+		brctl addif br.guest0 vlan.guest0
+	fi
+	brctl addif br.guest0 "uvpn.$user.0"
+	ip link set "uvpn.$user.0" up
+	ip link set vlan.guest0 up
+	ip link set br.guest0 up
+
+	mkdir -p "/run/uvpn/$user/ns"
+	chown "$user" "/run/uvpn/$user"
+	for ns in user net mnt;do
+		touch "/run/uvpn/$user/ns/$ns"
+	done
+	mount --bind "/run/uvpn/$user/ns" "/run/uvpn/$user/ns"
+	mount --make-private "/run/uvpn/$user/ns"
+
+	pid=$(setuid $uid unshare --mount --user --net bash -c 'sleep 30 > /dev/null&echo $!')
+	for ns in user net mnt;do
+		mount --bind /proc/$pid/ns/$ns "/run/uvpn/$user/ns/$ns"
+	done
+	echo "0 $uid 1" > /proc/$pid/uid_map
+	echo "0 $(id -g "$user") 1" > /proc/$pid/gid_map
+	ip link set "uvpn.$user.1" netns $pid
+	kill $pid
+}
+
+cmd_stop() {
+	test $# -eq 0 || die_usage
+	user=$USER
+	uid=$(id -u "$user")
+	if findmnt "/run/uvpn/$user/ns/net" > /dev/null; then
+		inode_net=$(stat --format %i "/run/uvpn/$user/ns/net")
+		for pid in $(cd /proc;ls -1d [0-9]*|sort -n); do
+			inode="$(stat -L --format %i /proc/$pid/ns/net 2>/dev/null)" || continue
+			test $inode == $inode_net || continue
+			cmd=$(cat /proc/$pid/cmdline|sed 's/\x0/ /g')
+			echo "kill --> $pid $cmd"
+			kill -9 $pid
+			found_processes=1
+		done
+		test -n "$found_processes" && sleep 1
+	fi
+	sudo $0 stop_as_root
+}
+
+cmd_stop_as_root() {
+	test $# -eq 0 || die_usage
+	test $(id -u) -eq 0 || die "must be root"
+	user="$SUDO_USER"
+	test -n "$user" || die "must be called via sudo"
+	uid=$(id -u "$user") 2>/dev/null || die "$user: no such user"
+
+	for ns in user net mnt;do
+		while umount "/run/uvpn/$user/ns/$ns" 2>/dev/null; do true; done
+	done
+	while umount "/run/uvpn/$user/ns" 2>/dev/null; do true; done
+	ip link del "uvpn.$user.0" 2>/dev/null
+}
+
+cmd_exec() {
+	findmnt "/run/uvpn/$USER/ns" >/dev/null 2>&1 || die "container not started"
+	nsenter \
+		--wd=$(pwd) \
+		--net="/run/uvpn/$USER/ns/net" \
+		--user="/run/uvpn/$USER/ns/user" \
+		--mount="/run/uvpn/$USER/ns/mnt" \
+		"$@"
+}
+
+cmd_show() {
+	user="$1";shift
+	test $# -eq 0 || die_usage
+	test -z "$user" && user=$USER
+	uid=$(id -u "$user") 2>/dev/null || die "$user: no such user"
+	findmnt "/run/uvpn/$user/ns/net" >/dev/null 2>/dev/null || { echo "Container not started"; exit; }
+	inode_net=$(stat --format %i "/run/uvpn/$user/ns/net")
+	echo "Processes running in this container:"
+	for pid in $(cd /proc;ls -1d [0-9]*|sort -n); do
+		inode="$(stat -L --format %i /proc/$pid/ns/net 2>/dev/null)" || continue
+		test $inode == $inode_net || continue
+		cmd=$(cat /proc/$pid/cmdline|sed 's/\x0/ /g')
+		echo "    $pid $cmd"
+		found_processes=1
+	done
+	test -n "$found_processes" || echo "    (none)"
+}
+
+cmd="$1";shift
+case "$cmd" in
+	start)
+		cmd_start "$@"
+		;;
+	start_as_root)
+		cmd_start_as_root "$@"
+		;;
+	stop)
+		cmd_stop "$@"
+		;;
+	stop_as_root)
+		cmd_stop_as_root "$@"
+		;;
+	exec)
+		cmd_exec "$@"
+		;;
+	show)
+		cmd_show "$@"
+		;;
+	*)
+		die_usage
+		;;
+esac