From 2e6e29a7577a45c2723080d2acd1621df6402831 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 14 Oct 2021 13:07:32 +0200 Subject: [PATCH] slirp4netns.sh: Introduce compatibility wrapper behaving like slirp4netns(1) Warning: draft quality, not really tested, --enable-sandbox not supported yet. Example: $ unshare -rUn # echo $$ 3130879 $ ./slirp4netns.sh -m 65520 -c 3130879 tap0 sent tapfd=5 for tap0 received tapfd=5 Starting slirp * MTU: 65520 * Network: 10.0.2.0 * Netmask: 255.255.255.0 * Gateway: 10.0.2.2 * DNS: 10.0.2.3 * Recommended IP: 10.0.2.100 WARNING: 127.0.0.1:* on the host is accessible as 10.0.2.2 (set --disable-host-loopback to prohibit connecting to 127.0.0.1:*) # ip li sh 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 33: tap0: mtu 65520 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 1000 link/ether 5e:9d:a0:c5:cf:67 brd ff:ff:ff:ff:ff:ff # ip ad sh 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 33: tap0: mtu 65520 qdisc pfifo_fast state UNKNOWN group default qlen 1000 link/ether 5e:9d:a0:c5:cf:67 brd ff:ff:ff:ff:ff:ff inet 10.0.2.0/24 scope global tap0 valid_lft forever preferred_lft forever inet6 fe80::5c9d:a0ff:fec5:cf67/64 scope link valid_lft forever preferred_lft forever # ip ro sh default via 10.0.2.2 dev tap0 10.0.2.0/24 dev tap0 proto kernel scope link src 10.0.2.0 root@epycfail:~# ip -6 ro sh fe80::/64 dev tap0 proto kernel metric 256 pref medium # iperf3 -c 10.0.2.2 -l1M Connecting to host 10.0.2.2, port 5201 [ 5] local 10.0.2.0 port 43014 connected to 10.0.2.2 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 1.38 GBytes 11.8 Gbits/sec 0 9.96 MBytes [ 5] 1.00-2.00 sec 1.59 GBytes 13.6 Gbits/sec 0 13.3 MBytes [ 5] 2.00-3.00 sec 1.63 GBytes 14.0 Gbits/sec 0 13.3 MBytes [ 5] 3.00-4.00 sec 1.78 GBytes 15.3 Gbits/sec 0 13.3 MBytes [ 5] 4.00-5.00 sec 1.80 GBytes 15.5 Gbits/sec 0 15.8 MBytes [ 5] 5.00-6.00 sec 1.69 GBytes 14.5 Gbits/sec 0 15.8 MBytes [ 5] 6.00-7.00 sec 1.65 GBytes 14.2 Gbits/sec 0 15.8 MBytes [ 5] 7.00-8.00 sec 1.68 GBytes 14.4 Gbits/sec 0 15.8 MBytes [ 5] 8.00-9.00 sec 1.60 GBytes 13.7 Gbits/sec 0 15.8 MBytes [ 5] 9.00-10.00 sec 1.66 GBytes 14.3 Gbits/sec 0 15.8 MBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 16.5 GBytes 14.1 Gbits/sec 0 sender [ 5] 0.00-10.01 sec 16.4 GBytes 14.1 Gbits/sec receiver iperf Done. Signed-off-by: Stefano Brivio --- slirp4netns.sh | 227 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100755 slirp4netns.sh diff --git a/slirp4netns.sh b/slirp4netns.sh new file mode 100755 index 0000000..f543e9b --- /dev/null +++ b/slirp4netns.sh @@ -0,0 +1,227 @@ +#!/bin/sh -euf +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# PASST - Plug A Simple Socket Transport +# for qemu/UNIX domain socket mode +# +# PASTA - Pack A Subtle Tap Abstraction +# for network namespace/tap device mode +# +# slirp4netns.sh - Compatibility wrapper for pasta, behaving like slirp4netns(1) +# +# WARNING: Draft quality, not really tested, --enable-sandbox not supported yet +# +# Copyright (c) 2021 Red Hat GmbH +# Author: Stefano Brivio + +PASTA_PID="$(mktemp)" +PASTA_OPTS="-q --ipv4-only -a 10.0.2.0 -n 24 -g 10.0.2.2 -m 1500 --no-ndp --no-dhcpv6 --no-dhcp -P ${PASTA_PID}" + +# add() - Add single option to $PASTA_OPTS +# $1: Option name, with or without argument +add() { + PASTA_OPTS="${PASTA_OPTS} ${1}" +} + +# drop() - Drop one option (without argument) from $PASTA_OPTS +# $1: Option name +drop() { + old_opts="${PASTA_OPTS}"; PASTA_OPTS= + for o in ${old_opts}; do [ "${o}" != "${1}" ] && add "${o}"; done +} + +# sub() - Substitute option in $PASTA_OPTS, with or without argument +# $1: Option name +# $2: Option argument, can be empty +sub() { + old_opts="${PASTA_OPTS}"; PASTA_OPTS= + next=0 + for o in ${old_opts}; do + if [ ${next} -eq 1 ]; then + next=0; add "${1} ${2}"; shift; shift; continue + fi + + for r in ${@}; do [ "${o}" = "${r}" ] && next=1 && break; done + [ "${next}" -eq 0 ] && add "${o}" + done +} + +# xorshift() - pseudorandom permutation of 16-bit group +# $1: 16-bit value to shuffle +xorshift() { + # Adaptation of Xorshift algorithm from: + # Marsaglia, G. (2003). Xorshift RNGs. + # Journal of Statistical Software, 8(14), 1 - 6. + # doi:http://dx.doi.org/10.18637/jss.v008.i14 + # with triplet (5, 3, 1), suitable for 16-bit ranges. + n=${1} + : $((n ^= n << 5)) + : $((n ^= n >> 3)) + : $((n ^= n << 1)) + echo ${n} +} + +# opt() - Validate single option from getopts +# $1: Option type +# $@: Variable names to assign to +opt() { + case "${1}" in + u32) + if ! printf "%i" "${OPTARG}" >/dev/null 2>&1 || \ + [ "${OPTARG}" -lt 0 ]; then + echo "${OPT} must be a non-negative integer" + usage + fi + eval ${2}="${OPTARG}" + ;; + mtu) + if ! printf "%i" "${OPTARG}" >/dev/null 2>&1 || \ + [ "${OPTARG}" -lt 0 ] || [ "${OPTARG}" -ge 65522 ]; then + echo "MTU must be a positive integer (< 65522)" + usage + fi + eval ${2}="${OPTARG}" + ;; + str) + eval ${2}="${OPTARG}" + ;; + net4) + addr="${OPTARG%/*}" + mask="${OPTARG##*/}" + + { [ -z "${mask}" ] || !printf "%i" "${mask}" >/dev/null 2>&1 \ + || [ ${mask} -gt 32 ] || ${mask} -le 0 ]; } && usage + + expr "${addr}" : \ + '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' \ + >/dev/null + [ $? -ne 0 ] && usage + + ifs="${IFS}"; IFS='.' + for q in ${addr}; do [ ${q} -gt 255 ] && usage; done + IFS="${ifs}" + + eval ${2}="${addr}" + eval ${3}="${mask}" + ;; + esac +} + +# usage() - Print slirpnetns(1) usage and exit indicating failure +# $1: Invalid option name, if any +usage() { + [ ${#} -eq 1 ] && printf "s: invalid option -- '%s'\n" "${0}" "${1}" + cat << EOF +Usage: ${0} [OPTION]... PID|PATH TAPNAME +User-mode networking for unprivileged network namespaces. + +-c, --configure bring up the interface +-e, --exit-fd=FD specify the FD for terminating slirp4netns +-r, --ready-fd=FD specify the FD to write to when the network is configured +-m, --mtu=MTU specify MTU (default=1500, max=65521) +-6, --enable-ipv6 enable IPv6 (experimental) +-a, --api-socket=PATH specify API socket path +--cidr=CIDR specify network address CIDR (default=10.0.2.0/24) +--disable-host-loopback prohibit connecting to 127.0.0.1:* on the host namespace +--netns-type=TYPE specify network namespace type ([path|pid], default=pid) +--userns-path=PATH specify user namespace path +--enable-sandbox create a new mount namespace (and drop all caps except CAP_NET_BIND_SERVICE if running as the root) +--enable-seccomp enable seccomp to limit syscalls (experimental) +-h, --help show this help and exit +-v, --version show version and exit +EOF + exit 1 +} + +# version() - Print version +version() { + echo "slirp4netns-like wrapper for pasta" + exit 0 +} + +# gen_addr6() - Generate pseudorandom IPv6 address, changes every second +gen_addr6() { + printf "fd00" + n=$(($(xorshift $(date +%S)) % 65536)) + for i in $(seq 2 8); do + printf ":%04x" ${n} + n=$(($(xorshift ${n}) % 65536)) + done +} + +# Default options +v6=0 +get_pid=0 +MTU=1500 +A4="10.0.2.0" +M4="255.255.255.0" +no_map_gw=0 +EFD=0 +RFD=0 + +while getopts ce:r:m:6a:hv-: OPT 2>/dev/null; do + if [ "${OPT}" = "-" ]; then + OPT="${OPTARG%%[= ]*}" + OPTARG="${OPTARG#${OPT}[= ]}" + fi + case "${OPT}" in + c | configure) add "--config-net" ;; + e | exit-fd) opt u32 EFD ;; + r | ready-fd) opt u32 RFD ;; + m | mtu) opt mtu MTU && sub -m ${MTU} ;; + 6 | enable-ipv6) V6=1 ;; + a | api-socket) opt str API ;; + cidr) opt net4 A4 M4 && sub -a ${A4} -n ${M4} ;; + disable-host-loopback) add "--no-map-gw" && no_map_gw=1 ;; + netns-type) : Autodetected ;; + userns-path) opt_str USERNS_NAME "${OPTARG}" ;; + enable-sandbox) : Not supported yet ;; + enable-seccomp) : Cannot be disabled ;; + h | help) usage "${0}" ;; + v | version) version ;; + ??*) usage "${0}" "${OPT}" ;; + ?) usage "${0}" ;; + esac +done + +shift $((OPTIND - 1)) +[ ${#} -ne 2 ] && usage +ns_spec="${1}" + +ifname="${2}" +add "-I ${ifname}" + +if [ ${v6} -eq 1 ]; then + drop "--ipv4-only" + add "-a $(gen_addr6) -g fd00::2 -D fd00::3" +fi + +./pasta ${PASTA_OPTS} ${ns_spec} 2>/dev/null && \ + [ ${RFD} -ne 0 ] && echo "1" >&${RFD} + +trap "kill $(cat ${PASTA_PID}); rm ${PASTA_PID}" INT TERM + +cat << EOF +sent tapfd=5 for ${ifname} +received tapfd=5 +Starting slirp +* MTU: ${MTU} +* Network: ${A4} +* Netmask: ${M4} +* Gateway: 10.0.2.2 +* DNS: 10.0.2.3 +* Recommended IP: 10.0.2.100 +EOF + +if [ ${no_map_gw} -eq 0 ]; then + echo "WARNING: 127.0.0.1:* on the host is accessible as 10.0.2.2 (set --disable-host-loopback to prohibit connecting to 127.0.0.1:*)" +fi + +if [ ${EFD} -ne 0 ]; then + dd count=1 of=/dev/null 2>/dev/null <&${EFD} +else + while read a; do :; done +fi + +exit 0