fix error handle path, add doc and cteate options
[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 [[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
18
19 if [[ ! $ERRHANDLE_PATH ]]; then
20 ERRHANDLE_PATH=$(readlink -f "${BASH_SOURCE}")
21 ERRHANDLE_PATH=$(readlink -f ${ERRHANDLE_PATH%/*}/../errhandle)
22 fi
23 err_sourced=true
24 for p in $ERRHANDLE_PATH/{errcatch-function,bash-trace-function}; do
25 if [[ -e $p ]]; then
26 source $p
27 else
28 err_sourced=false
29 fi
30 done
31 if $err_sourced; then
32 errcatch
33 else
34 set -eE -o pipefail
35 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
36 fi
37
38 usage() {
39 cat <<EOF
40 usage: ${0##*/} [OPTS] start|stop NETNS_NAME
41
42 -c, --create Create network namespace. For running outside systemd private net.
43 -h, --help Show this help and exit.
44
45 From within systemd network namespace, nat it to the outside. If given
46 -c, or if in the default network namespace, create a named network
47 namepace natted to the current netns.
48
49 Also create a named mount namespace under /root/mount_namespaces, so we
50 can alter some system config for this namespace. Subsequent systemd
51 command lines would be prefixed with:
52
53 /usr/bin/nsenter --mount=/root/mount_namespaces/NETNS_NAME
54
55
56 "ip netns new ..." also does a mount namespace, then bind mounts each
57 thing in /etc/netns/NETNS_NAME to /etc/NETNS_NAME. Note, for openvpn having it's own
58 resolv.conf, this doesn't help much. What we actually want to do is copy
59 /run/resolvconf somehwere, then bind mount it on top of /run/resolvconf.
60
61 Once systemd 233 comes out, it will have a bind mount option from within
62 unit files, so the mount namespace won't be needed for this use case.
63
64 Recommmended dependency of errhandle to print stack trace on error:
65 https://iankelling.org/git/?p=errhandle, set ERRHANDLE_PATH, or put it
66 in a directory adjacent to the absolute, resolved directory this file is
67 in.
68
69 EOF
70 exit ${1:-0}
71 }
72
73
74 ## begin arg parsing ##
75 create=false
76 temp=$(getopt -l help,create hc "$@") || usage 1
77 eval set -- "$temp"
78 while true; do
79 case $1 in
80 -c|--create) create=true; shift ;;
81 -h|--help) usage ;;
82 --) shift; break ;;
83 *) echo "$0: Internal error!" ; exit 1 ;;
84 esac
85 done
86 if (( $# != 2 )); then
87 usage 1
88 fi
89
90 action=$1
91 nn=$2 # network namespace / namespace name
92 ## end arg parsing ##
93
94 ## begin sanity checking ##
95
96 install_error=false
97 if ! type -p ip &>/dev/null; then
98 echo "please install the iproute2 package"
99 install_error=true
100 fi
101 if ! type -p iptables &>/dev/null; then
102 echo "please install the iptables package"
103 install_error=true
104 fi
105 if $install_error; then
106 exit 1
107 fi
108
109 ## end sanity checking ##
110
111
112 v0=veth0-$nn
113 v1=veth1-$nn
114 ip_base=10.173
115
116 if ! $create && [[ $(readlink /proc/self/ns/net) == "$(readlink /proc/1/ns/net)" ]]; then
117 create=true
118 fi
119
120 target=/run/netns/default
121 if [[ ! -e $target && ! -L $target ]]; then
122 mkdir -p /run/netns
123 # make the default network namespace be named
124 ln -s /proc/1/ns/net $target
125 fi
126
127
128 ipd() { ip -n default "$@"; }
129 if $create; then
130 ipnn() { ip -n $nn "$@"; }
131 else
132 # we are already in the network namespace and it's unnamed.
133 ipnn() { ip "$@"; }
134 fi
135 dexec() { ip netns exec default "$@"; }
136
137
138 # head -n1 is defensive. Not sure if there is some weird feature
139 # for 2 routes to be 0/0.
140 gateway_if=$(ipd route list exact 0/0 | head -n1| sed -r 's/.*\s(\S+)\s*$/\1/')
141 nat() { dexec iptables -t nat $1 POSTROUTING -o $gateway_if -j MASQUERADE \
142 -m comment --comment "systemd network namespace nat"; }
143
144 find_network() {
145 found=false
146 existing=false
147 ips="$(ipd addr show | awk '$1 == "inet" {print $2}')"
148 for ((i=0; i <= 254; i++)); do
149 network=$ip_base.$i
150 if printf "%s\n" "$ips" | grep "^${network//./\\.}" >/dev/null; then
151 existing=true
152 else
153 found=true
154 break
155 fi
156 done
157 }
158
159 start() {
160
161 find_network
162 if ! $found; then
163 echo "$0: error: no open network found"
164 exit 1
165 fi
166
167 mkdir -p /root/mount_namespaces
168 if ! mountpoint /root/mount_namespaces >/dev/null; then
169 mount --bind /root/mount_namespaces /root/mount_namespaces
170 mount --make-private /root/mount_namespaces
171 fi
172 if [[ ! -e /root/mount_namespaces/$nn ]]; then
173 touch /root/mount_namespaces/$nn
174 fi
175 if ! mountpoint /root/mount_namespaces/$nn >/dev/null; then
176 unshare --mount=/root/mount_namespaces/$nn
177 fi
178
179
180 if $create; then
181 ip netns add $nn
182 ip -n $nn link set dev lo up
183 fi
184
185
186
187 echo 1 | dexec dd of=/proc/sys/net/ipv4/ip_forward 2>/dev/null
188
189 _errcatch_cleanup=stop
190 ipnn link add $v0 type veth peer name $v1
191 ipnn link set $v0 netns default
192 ipd addr add $network.1/24 dev $v0
193 ipd link set $v0 up
194 nat -C &>/dev/null || nat -A
195 ipnn addr add $network.2/24 dev $v1
196 ipnn link set $v1 up
197 ipnn route add default via $network.1
198
199 }
200
201 stop() {
202 if ipd link list $v0 &>/dev/null; then
203 # this also deletes $v1 and the route we added.
204 ipd link del $v0
205 fi
206 find_network
207 if ! $existing; then
208 if nat -C &>/dev/null; then nat -D; fi
209 fi
210 if $create; then
211 ip netns del $nn
212 fi
213 }
214
215 case $action in
216 start|stop)
217 $action
218 ;;
219 *)
220 echo "$0: error: unsupported action"
221 exit 1
222 ;;
223 esac