working version for use in systemd
[newns] / newns
1 #!/bin/bash
2 # Copyright (C) 2017 Ian Kelling
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16
17 # Create a network namespace. Designed for use from systemd.
18
19 [[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
20
21 cd "${BASH_SOURCE%/*}"
22 source ../errhandle/errcatch-function
23 source ../errhandle/bash-trace-function
24 errcatch
25
26
27 ## begin arg parsing ##
28 action=$1
29 nn=$2 # network namespace / namespace name
30 ## end arg parsing ##
31
32 ## begin sanity checking ##
33
34 install_error=false
35 if ! type -p ip &>/dev/null; then
36 echo "please install the iproute2 package"
37 install_error=true
38 fi
39 if ! type -p iptables &>/dev/null; then
40 echo "please install the iptables package"
41 install_error=true
42 fi
43 if $install_error; then
44 exit 1
45 fi
46
47 ## end sanity checking ##
48
49
50 v0=veth0-$nn
51 v1=veth1-$nn
52 ip_base=10.173
53
54
55 target=/run/netns/default
56 if [[ ! -e $target && ! -L $target ]]; then
57 mkdir -p /run/netns
58 # make the default network namespace be named
59 ln -s /proc/1/ns/net $target
60 fi
61
62
63 ipd() { ip -n default "$@"; }
64 dexec() { ip netns exec default "$@"; }
65
66 # note: this script could be easily adapted to create a
67 # netns instead of using the systemd created one.
68 # ip netns add NAME
69 # ip -n NAME link set dev lo up
70
71 # random note, ip netns exec creates a mount namespace and
72 # remaps /etc to /etc/netns/NAME.
73
74 # head -n1 is defensive. I don't know if it's possible to have more
75 # than one default route.
76 gateway_if=$(ipd route list exact 0/0 | head -n1| sed -r 's/.*\s(\S+)\s*$/\1/')
77 nat() { dexec iptables -t nat $1 POSTROUTING -o $gateway_if -j MASQUERADE \
78 -m comment --comment "systemd network namespace nat"; }
79
80 find_network() {
81 found=false
82 existing=false
83 ips="$(ipd addr show | awk '$1 == "inet" {print $2}')"
84 for ((i=0; i <= 254; i++)); do
85 network=$ip_base.$i
86 if printf "%s\n" "$ips" | grep "^${network//./\\.}"; then
87 existing=true
88 else
89 found=true
90 break
91 fi
92 done
93 }
94
95 start() {
96
97 mkdir -p /root/mount_namespaces
98 if ! mountpoint /root/mount_namespaces >/dev/null; then
99 mount --bind /root/mount_namespaces /root/mount_namespaces
100 mount --make-private /root/mount_namespaces
101 fi
102 if [[ ! -e /root/mount_namespaces/$nn ]]; then
103 touch /root/mount_namespaces/$nn
104 fi
105 if ! mountpoint /root/mount_namespaces/$nn >/dev/null; then
106 unshare --mount=/root/mount_namespaces/$nn
107 fi
108
109
110
111 find_network
112 if ! $found; then
113 echo "$0: error: no open network found"
114 exit 1
115 fi
116
117 echo 1 | dexec dd of=/proc/sys/net/ipv4/ip_forward 2>/dev/null
118
119 _errcatch_cleanup=stop
120 ip link add $v0 type veth peer name $v1
121 ip link set $v0 netns default
122 ipd addr add $network.1/24 dev $v0
123 ipd link set $v0 up
124 nat -C &>/dev/null || nat -A
125 ip addr add $network.2/24 dev $v1
126 ip link set $v1 up
127 ip route add default via $network.1
128
129 }
130
131 stop() {
132 if ipd link list $v0 &>/dev/null; then
133 # this also deletes $v1 and the route we added.
134 ipd link del $v0
135 fi
136 find_network
137 if ! $existing; then
138 if nat -C &>/dev/null; then nat -D; fi
139 fi
140 }
141
142 case $action in
143 start|stop)
144 $action
145 ;;
146 *)
147 echo "$0: error: unsupported action"
148 exit 1
149 ;;
150 esac