SLIRP4NETNS(1) User Commands SLIRP4NETNS(1)

slirp4netns - شبکه‌سازی در حالت کاربر برای فضاهای نام شبکه بدون دسترسی ریشه

slirp4netns [OPTION]... PID|PATH|FD [TAPNAME]

slirp4netns شبکه‌سازی در حالت کاربر (“slirp”) را برای فضاهای نام شبکه فراهم می‌کند.

برخلاف veth(4)، slirp4netns نیازی به دسترسی‌های ریشه (root) بر روی میزبان ندارد.

پیکربندی پیش‌فرض:

  • MTU: 1500
  • CIDR: 10.0.2.0/24
  • Gateway/Host: 10.0.2.2 (آدرس شبکه + 2)
  • DNS: 10.0.2.3 (آدرس شبکه + 3)
  • شروع DHCP: 10.0.2.15 (آدرس شبکه + 15)
  • پایان DHCP: 10.0.2.30 (آدرس شبکه + 30)
  • IPv6 CIDR: fd00::/64
  • IPv6 Gateway/Host: fd00::2
  • IPv6 DNS: fd00::3

-c, --configure رابط TAP را بالا می‌آورد. به‌طور پیش‌فرض، IP روی 10.0.2.100 (آدرس شبکه + 100) تنظیم خواهد شد. IPv6 روی یک آدرس تصادفی تنظیم می‌شود. از نسخهٔ v0.4.0، رابط loopback (lo) نیز بالا آورده می‌شود.

-e, --exit-fd=FD مشخص کردن FD برای خاتمه دادن به slirp4netns. هنگامی که FD مشخص شود، slirp4netns با رخ دادن یک رویداد poll(2) روی FD خارج می‌شود.

-r, --ready-fd=FD مشخص کردن FD جهت نوشتن در آن پس از اتمام مراحل مقداردهی اولیه. هنگامی که FD مشخص شود، slirp4netns مقدار "1" را روی FD نوشته و FD را می‌بندد. پیش از نسخهٔ v0.4.0، نوشتن روی FD پس از پیکربندی شبکه (-c) اما پیش از پیکربندی سوکت API (-a) انجام می‌شد.

-m, --mtu=MTU (از نسخهٔ v0.2.0) مشخص کردن MTU (حداکثر=65521).

-6, --enable-ipv6 (از نسخهٔ v0.2.0، آزمایشی) فعال‌سازی IPv6

-a, --api-socket (از نسخهٔ v0.3.0) مسیر سوکت API

--cidr (از نسخهٔ v0.3.0) مشخص کردن CIDR، به عنوان مثال 10.0.2.0/24

--disable-host-loopback (از نسخهٔ v0.3.0) ممنوع کردن اتصال به 127.0.0.1:* روی فضای نام میزبان

--netns-type=TYPE (از نسخهٔ v0.4.0) مشخص کردن نوع فضای نام شبکه ([path|pid|tapfd]، پیش‌فرض=pid)

اگر نوع فضای نام tapfd باشد، انتظار می‌رود نخستین آرگومان مکانی، یک توصیف‌کننده فایل موروثی باشد که به یک اتصال /dev/net/tun مربوط می‌شود. این با --configure تداخل دارد و TAPNAME نادیده گرفته می‌شود.

--userns-path=PATH (از نسخهٔ v0.4.0) مشخص کردن مسیر فضای نام کاربر

--enable-sandbox (از نسخهٔ v0.4.0) ورود به فضای نام کاربر و ایجاد یک فضای نام سوارکردن (mount namespace) جدید که در آن تنها /etc و /run از میزبان سوار شده‌اند.

نیازمند آن است که /etc/resolv.conf یک پیوند نمادین به فایلی خارج از /etc و /run نباشد.

هنگام اجرا به عنوان کاربر ریشه (root)، فرآیند وارد فضای نام کاربر نمی‌شود اما تمامی مجوزها (capabilities) به جز CAP_NET_BIND_SERVICE لغو می‌شوند.

--enable-seccomp (از نسخهٔ v0.4.0، آزمایشی) فعال‌سازی seccomp(2) برای محدود کردن فراخوان‌های سیستمی. معمولاً در ترکیب با --enable-sandbox استفاده می‌شود.

--outbound-addr=IPv4 (از نسخهٔ v1.1.0، آزمایشی) مشخص کردن آدرس ipv4 خروجی که slirp باید به آن مقید (bind) شود

--outbound-addr=INTERFACE (از نسخهٔ v1.1.0، آزمایشی) مشخص کردن رابط خروجی که slirp باید به آن مقید شود (تنها ترافیک ipv4)

--outbound-addr=IPv6 (از نسخهٔ v1.1.0، آزمایشی) مشخص کردن آدرس ipv6 خروجی که slirp باید به آن مقید شود

--outbound-addr6=INTERFACE (از نسخهٔ v1.1.0، آزمایشی) مشخص کردن رابط خروجی که slirp باید به آن مقید شود (تنها ترافیک ipv6)

--disable-dns (از نسخهٔ v1.1.0) غیرفعال کردن DNS داخلی (به‌طور پیش‌فرض 10.0.2.3)

--macaddress (از نسخهٔ v1.1.9) مشخص کردن آدرس MAC رابط TAP (فقط با -c معتبر است)

--target-type=TYPE (از نسخهٔ v1.2.0) مشخص کردن نوع هدف ([netns|bess]، پیش‌فرض=netns).

حالت bess (از نسخهٔ v1.2.0، آزمایشی) انتظار می‌رود با User Mode Linux استفاده شود. حالت bess با --configure، --netns-type، و --userns-path تداخل دارد.

-h, --help (از نسخهٔ v0.2.0) نمایش راهنما و خروج

-v, --version (از نسخهٔ v0.2.0) نمایش نسخه و خروج.SH "مثال (EXAMPLE)"

ترمینال ۱: ایجاد فضاهای نام user/network/mount

(host)$ unshare --user --map-root-user --net --mount
(namespace)$ echo $$ > /tmp/pid

در این مستندات، از (host)$ به‌عنوان اعلان (prompt) پوستهٔ میزبان و از (namespace)$ به‌عنوان اعلان پوستهٔ در حال اجرا در فضاهای نام استفاده شده است.

اگر unshare با شکست مواجه شد، دستورهای زیر را امتحان کنید (مشخص شده است که در Debian، Arch و نسخهٔ قدیمی CentOS 7.X مورد نیاز است):

(host)$ sudo sh -c 'echo "user.max_user_namespaces=28633" >> /etc/sysctl.d/userns.conf'
(host)$ if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then sudo sh -c 'echo "kernel.unprivileged_userns_clone=1" >> /etc/sysctl.d/userns.conf'; fi
(host)$ sudo sysctl --system

ترمینال ۲: راه‌اندازی slirp4netns

(host)$ slirp4netns --configure --mtu=65520 $(cat /tmp/pid) tap0
starting slirp, MTU=65520

ترمینال ۱: مطمئن شوید که tap0 پیکربندی شده و به اینترنت متصل است

(namespace)$ ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: tap0: <BROADCAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN group default qlen 1000
    link/ether c2:28:0c:0e:29:06 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
       valid_lft forever preferred_lft forever
    inet6 fe80::c028:cff:fe0e:2906/64 scope link 
       valid_lft forever preferred_lft forever
(namespace)$ echo "nameserver 10.0.2.3" > /tmp/resolv.conf
(namespace)$ mount --bind /tmp/resolv.conf /etc/resolv.conf
(namespace)$ curl https://example.com

عملیات bind-mount برای /etc/resolv.conf تنها زمانی لازم است که /etc/resolv.conf در میزبان به آدرس‌های loopback اشاره کند (127.0.0.X، معمولاً dnsmasq(8) یا systemd-resolved.service(8)) که از داخل فضای نام قابل دسترس نیستند.

اگر /etc/resolv.conf شما در میزبان توسط networkmanager(8) یا systemd-resolved.service(8) مدیریت می‌شود، ممکن است در عوض نیاز داشته باشید یک سیستم‌فایل جدید را روی /etc سوار (mount) کنید تا هنگام بازتولید /etc/resolv.conf در میزبان، از پیاده شدن (unmount) غیرمنتظرهٔ /etc/resolv.conf جدید جلوگیری شود.

(namespace)$ mkdir /tmp/a /tmp/b
(namespace)$ mount --rbind /etc /tmp/a
(namespace)$ mount --rbind /tmp/b /etc
(namespace)$ mkdir /etc/.ro
(namespace)$ mount --move /tmp/a /etc/.ro
(namespace)$ cd /etc
(namespace)$ for f in .ro/*; do ln -s $f $(basename $f); done
(namespace)$ rm resolv.conf
(namespace)$ echo "nameserver 10.0.2.3" > resolv.conf
(namespace)$ curl https://example.com

در صورت نصب بودن rootlesskit، این مراحل را می‌توان با rootlesskit --copy-up=/etc ساده‌تر کرد:

(host)$ rootlesskit --net=slirp4netns --copy-up=/etc bash
(namespace)$ cat /etc/resolv.conf
nameserver 10.0.2.3

برای مسیریابی بسته‌های ping، ممکن است نیاز باشد net.ipv4.ping_group_range را با دسترسی ریشه (root) به‌درستی تنظیم کنید.

برای نمونه:

(host)$ sudo sh -c 'echo "net.ipv4.ping_group_range=0   2147483647" > /etc/sysctl.d/ping_group_range.conf'
(host)$ sudo sysctl --system

به‌طور پیش‌فرض، درگاه‌های شنوا روی INADDR_LOOPBACK (127.0.0.1) در میزبان، از طریق دروازه (پیش‌فرض: 10.0.2.2) از فضای نام فرزند قابل دسترسی هستند. می‌توان از --disable-host-loopback برای منع اتصال به INADDR_LOOPBACK در میزبان استفاده کرد.

با این حال، اگر /etc/resolv.conf روی میزبان به یک نشانی لوپ‌بک اشاره کند، نشانی لوپ‌بک میزبان ممکن است همچنان از طریق DNS داخلی (پیش‌فرض: 10.0.2.3) در دسترس باشد. در چنین مواردی ممکن است بخواهید iptables را برای محدود کردن دسترسی به DNS داخلی برپا کنید.

(host)$ nsenter -t $(cat /tmp/pid) -U --preserve-credentials -n
(namespace)$ iptables -A OUTPUT -d 10.0.2.3 -p udp --dport 53 -j ACCEPT
(namespace)$ iptables -A OUTPUT -d 10.0.2.3 -j DROP

برنامه slirp4netns می‌تواند یک سرور API شبیه به QMP را روی یک فایل سوکت UNIX ارائه دهد:

(host)$ slirp4netns --api-socket /tmp/slirp4netns.sock ...

add_hostfwd: در دسترس قرار دادن یک درگاه (فقط IPv4)

(namespace)$ json='{"execute": "add_hostfwd", "arguments": {"proto": "tcp", "host_addr": "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}}'
(namespace)$ echo -n $json | nc -U /tmp/slirp4netns.sock
{"return": {"id": 42}}

اگر host_addr مشخص نشود، مقدار پیش‌فرض آن "0.0.0.0" خواهد بود.

اگر guest_addr مشخص نشود، روی نشانی پیش‌فرضی که متناظر با --configure است تنظیم می‌شود.

list_hostfwd: فهرست کردن درگاه‌های در دسترس قرار گرفته

(namespace)$ json='{"execute": "list_hostfwd"}'
(namespace)$ echo -n $json | nc -U /tmp/slirp4netns.sock
{"return": {"entries": [{"id": 42, "proto": "tcp", "host_addr": "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}]}}

remove_hostfwd: حذف یک درگاه در دسترس قرار گرفته

(namespace)$ json='{"execute": "remove_hostfwd", "arguments": {"id": 42}}'
(namespace)$ echo -n $json | nc -U /tmp/slirp4netns.sock
{"return": {}}

نکات:

  • کلاینت باید پس از ارسال هر درخواست، سوکت را با SHUT_WR توسط shutdown(2) ببندد. به این معنی که هیچ پشتیبانی‌ای از keep-alive و timeout وجود ندارد.
  • برنامه slirp4netns در حین پردازش درخواست‌های API سامانه را متوقف می‌کند ("stops the world").
  • طول هر درخواست باید کمتر از ۴۰۹۶ بایت باشد.
  • پاسخ‌های JSON ممکن است به جای return شامل error باشند.

کاربر می‌تواند بر خلاف شناسهٔ فرایند پیش‌فرض، یک مسیر فضای نام شبکه تعریف کند:

(host)$ slirp4netns --netns-type=path ... /path/to/netns tap0

در حال حاضر آرگومان netns-type=TYPE از مقادیر path یا pid پشتیبانی می‌کند که پیش‌فرض آن pid است.

علاوه بر این، می‌توان آرگومان --userns-path=PATH را برای بازنویسی مقادیر پیش‌فرض مسیر فضای نام کاربری اضافه کرد

(host)$ slirp4netns --netns-type=path --userns-path=/path/to/userns /path/to/netns tap0

کاربر می‌تواند در سناریوهای دارای چند IP، نشانی‌های IPv4 و IPv6 خروجی ترجیحی را تعیین کند.

(host)$ slirp4netns --outbound-addr=10.2.2.10 --outbound-addr6=fe80::10 ...

همچنین می‌توانید به جای نشانی‌های IP، از نام رابط‌ها (interface) استفاده کنید.

(host)$ slirp4netns --outbound-addr=eth0 --outbound-addr6=eth0 ...

ساده‌ترین راه برای برقراری ارتباط میان فضاهای نام، تودرتو کردن (nesting) فضاهای نام شبکه درون فضای نام شبکه slirp4netns است.

(host)$ nsenter -t $(cat /tmp/pid) -U --preserve-credentials -n -m
(namespace)$ mount -t tmpfs none /run
(namespace)$ ip netns add foo
(namespace)$ ip netns add bar
(namespace)$ ip link add veth-foo type veth peer name veth-bar
(namespace)$ ip link set veth-foo netns foo
(namespace)$ ip link set veth-bar netns bar
(namespace)$ ip netns exec foo ip link set veth-foo name eth0
(namespace)$ ip netns exec bar ip link set veth-bar name eth0
(namespace)$ ip netns exec foo ip link set lo up
(namespace)$ ip netns exec bar ip link set lo up
(namespace)$ ip netns exec foo ip link set eth0 up
(namespace)$ ip netns exec bar ip link set eth0 up
(namespace)$ ip netns exec foo ip addr add 192.168.42.100/24 dev eth0
(namespace)$ ip netns exec bar ip addr add 192.168.42.101/24 dev eth0
(namespace)$ ip netns exec bar ping 192.168.42.100

با این حال، این روش زمانی که بخواهید ارتباط میان چندین نمونه slirp4netns را برقرار کنید کارایی ندارد. برای ایجاد ارتباط میان چندین نمونه slirp4netns، باید یک پشته شبکه دیگر مانند vde_plug(1) را با slirp4netns ترکیب کنید.

(host)$ vde_plug --daemon switch:///tmp/switch null://
(host)$ nsenter -t $(cat /tmp/pid-instance0) -U --preserve-credentials -n
(namespace-instance0)$ vde_plug --daemon vde:///tmp/switch tap://vde
(namespace-instance0)$ ip link set vde up
(namespace-instance0)$ ip addr add 192.168.42.100/24 dev vde
(namespace-instance0)$ exit
(host)$ nsenter -t $(cat /tmp/pid-instance1) -U --preserve-credentials -n
(namespace-instance1)$ vde_plug --daemon vde:///tmp/switch tap://vde
(namespace-instance1)$ ip link set vde up
(namespace-instance1)$ ip addr add 192.168.42.101/24 dev vde
(namespace-instance1)$ ping 192.168.42.100

استفاده از VXLAN کارآمد و تأییدشده است. برای مشاهده مثالی از یک کلاستر چندگره‌ای Kubernetes بدون ریشه (rootless) به همراه VXLAN، پروژه Usernetes را ببینید: https://github.com/rootless-containers/usernetes

برنامه slirp4netns (از نسخه v1.2.0 به بعد) می‌تواند به عنوان یک سرور سازگار با BESS برای فراهم کردن اتصال شبکه به User Mode Linux نیز مورد استفاده قرار گیرد.

ترمینال ۱: آغاز slirp4netns

(host)$ slirp4netns --target-type=bess /tmp/bess.sock

ترمینال ۲: آغاز User Mode Linux

(host)$ linux.uml vec0:transport=bess,dst=/tmp/bess.sock,depth=128,gro=1 root=/dev/root rootfstype=hostfs init=/bin/bash mem=2G
(UML)$ ip addr add 10.0.2.100/24 dev vec0
(UML)$ ip link set vec0 up
(UML)$ ip route add default via 10.0.2.2

در حال حاضر، تنها یک نمونه واحد از User Mode Linux می‌تواند به سرور BESS متعلق به slirp4netns متصل شود.

همچنین مستندات User Mode Linux را ببینید: https://www.kernel.org/doc/html/latest/virt/uml/user_mode_linux_howto_v2.html#bess-socket-transport.SH "گزارش باگ‌ها (BUGS)"

در هستهٔ 4.20 مقدار پیش‌فرض /proc/sys/net/ipv4/tcp_rmem از 87380 به 131072 افزایش یافت. مشخص شده است که این تغییر موجب کاهش سرعت فوروارد پورت (port forwarding) در slirp4netns می‌شود: https://github.com/rootless-containers/slirp4netns/issues/128.

به‌عنوان یک راهکار موقت، می‌توانید مقدار /proc/sys/net/ipv4/tcp_rmem را در داخل فضای نام (namespace) تنظیم کنید. از هستهٔ 4.15 به بعد، برای تغییر این فایل نیازی به دسترسی واقعی ریشه (root) نیست.

(host)$ nsenter -t $(cat /tmp/pid) -U --preserve-credentials -n -m
(namespace)$ c=$(cat /proc/sys/net/ipv4/tcp_rmem); echo $c | sed -e s/131072/87380/g > /proc/sys/net/ipv4/tcp_rmem

network_namespaces(7), user_namespaces(7), veth(4)

دستور slirp4netns تحت «اجازه‌نامهٔ عمومی همگانی گنو» نسخهٔ 2 (یا نسخه‌های بالاتر) از نشانی https://github.com/rootless-containers/slirp4netns در دسترس است.

January 2022 Rootless Containers