-
-
Save dpino/6c0dca1742093346461e11aa8f608a99 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash | |
# set -x | |
if [[ $EUID -ne 0 ]]; then | |
echo "You must be root to run this script" | |
exit 1 | |
fi | |
# Returns all available interfaces, except "lo" and "veth*". | |
available_interfaces() | |
{ | |
local ret=() | |
local ifaces=$(ip li sh | cut -d " " -f 2 | tr "\n" " ") | |
read -a arr <<< "$ifaces" | |
for each in "${arr[@]}"; do | |
each=${each::-1} | |
if [[ ${each} != "lo" && ${each} != veth* ]]; then | |
ret+=( "$each" ) | |
fi | |
done | |
echo ${ret[@]} | |
} | |
IFACE="$1" | |
if [[ -z "$IFACE" ]]; then | |
ifaces=($(available_interfaces)) | |
if [[ ${#ifaces[@]} -gt 0 ]]; then | |
IFACE=${ifaces[0]} | |
echo "Using interface $IFACE" | |
else | |
echo "Usage: ./ns-inet <IFACE>" | |
exit 1 | |
fi | |
fi | |
NS="ns1" | |
VETH="veth1" | |
VPEER="vpeer1" | |
VETH_ADDR="10.200.1.1" | |
VPEER_ADDR="10.200.1.2" | |
trap cleanup EXIT | |
cleanup() | |
{ | |
ip li delete ${VETH} 2>/dev/null | |
} | |
# Remove namespace if it exists. | |
ip netns del $NS &>/dev/null | |
# Create namespace | |
ip netns add $NS | |
# Create veth link. | |
ip link add ${VETH} type veth peer name ${VPEER} | |
# Add peer-1 to NS. | |
ip link set ${VPEER} netns $NS | |
# Setup IP address of ${VETH}. | |
ip addr add ${VETH_ADDR}/24 dev ${VETH} | |
ip link set ${VETH} up | |
# Setup IP ${VPEER}. | |
ip netns exec $NS ip addr add ${VPEER_ADDR}/24 dev ${VPEER} | |
ip netns exec $NS ip link set ${VPEER} up | |
ip netns exec $NS ip link set lo up | |
ip netns exec $NS ip route add default via ${VETH_ADDR} | |
# Enable IP-forwarding. | |
echo 1 > /proc/sys/net/ipv4/ip_forward | |
# Flush forward rules. | |
iptables -P FORWARD DROP | |
iptables -F FORWARD | |
# Flush nat rules. | |
iptables -t nat -F | |
# Enable masquerading of 10.200.1.0. | |
iptables -t nat -A POSTROUTING -s ${VPEER_ADDR}/24 -o ${IFACE} -j MASQUERADE | |
iptables -A FORWARD -i ${IFACE} -o ${VETH} -j ACCEPT | |
iptables -A FORWARD -o ${IFACE} -i ${VETH} -j ACCEPT | |
# Get into namespace | |
ip netns exec ${NS} /bin/bash --rcfile <(echo "PS1=\"${NS}> \"") |
It works fine for me if change row 49:
iptables -t nat -A POSTROUTING -s ${VPEER_ADDR}/24 -o eth0 -j MASQUERADE
to
iptables -t nat -A POSTROUTING -s ${VETH_ADDR}/24 -o eth0 -j MASQUERADE
And it's logically true. In this row you set up the iptable routing. And try to connect eth0 with virtual interface.
But, VPEER_ADDR - locate in the ns1 network namespace while VETH_ADDR in global(default).
Am I right that it's a mistake in that row?
I don't think that row makes a difference, it's only looking at the prefix, right?
Any ideas why I would be able to ping from the namespace, but not wget or curl?
Hi, sorry for not replying comments before. This is the first time I noticed there were actually comments here.
@mcgovern612 Are you pinging to an IPv4 address or to a host name? Maybe that's the issue and what's not actually working is DNS resolution (I generally ping to an IPv4 address and wget or curl an URL resource). For name host resolution check the comment by @markoda (https://gist.github.com/dpino/6c0dca1742093346461e11aa8f608a99#gistcomment-1987705) or add an external nameserver to your /etc/resolv.conf
.
@EJohnF About line 49, I actually tried both VPEER_ADDR and VETH_ADDR and it works in both cases. That said, I think it's more correct to apply source natting in the host interface since the host interface should be reachable from the network namespace.
@andrew160 nice catch, the "eth0" string should be another variable in the script.
@dpino DNS is working as ping can resolve hostnames, and in wget calls I can see resolution but it just doesn't connect.
I must say though, thank you for the awesome script and blog description. It was very useful for someone who has never used network namespaces.
@Lu-Yi-Hsun I have modified the script to fix the "eth0" issue pointed out by other users before, as well as introducing other improvements like select a default interface (if only one is available) and remove veth interfaces on exit.
I run this new version of the script in my laptop and this is what I got when pinging 8.8.8.8 and www.google.com:
$ sudo ./ns-inet.sh
Using interface wlp59s0
ns1> ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=53 time=18.1 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=53 time=17.7 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=53 time=17.9 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 17.663/17.873/18.089/0.173 ms
ns1> ping www.google.com
PING www.google.com (216.58.206.228) 56(84) bytes of data.
64 bytes from par10s34-in-f4.1e100.net (216.58.206.228): icmp_seq=1 ttl=53 time=17.6 ms
64 bytes from par10s34-in-f4.1e100.net (216.58.206.228): icmp_seq=2 ttl=53 time=17.5 ms
64 bytes from par10s34-in-f4.1e100.net (216.58.206.228): icmp_seq=3 ttl=53 time=17.0 ms
^C
--- www.google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 17.027/17.370/17.602/0.247 ms
ns1>
So, I don't know what could be the reason why you can ping 8.8.8.8 but not www.google.com. It seems likely is due to a configuration problem in your settings or environment. Can you resolve www.google.com out of a networking namespace?
thank you!
I find the problem.
ns1 sent ping www.google.com
VPEER ----DNS(packet)------>VETH
VPEER<---------ICMP(Destination Unreachable, Communication Administratively Prohibited)--------VETH
Firewall block the packet sent from VETH to wlpXXs0
so this can fix my problem
sudo systemctl stop firewalld
Is it supposed to work with firefox as well? Hoped so but not working
Weird, executing the script (as well as following the corresponding blog post) only yields “Destination Host Unreachable” at the step of ping. All the outputs (routing tables, links and addresses) are as expected. I remember I tried this about half a year ago and it worked flawlessly. Anyone knows what might have happened? It's not the firewall. Tcpdump finds ARP who-has asking for “who's the gateway”.
Hi,
After executing the script ns-inet.sh on a server, I am able to ping the host IP as well as any IP configured on this server, but I am not able to ping any IP outside of the host. If I am not in the namespace prompt, I am able to ping the IP outside of the host. Any idea ?
I was getting error with interface name, also don't want to lose netns on exit, here is modified version
https://gist.github.com/programmer131/f1bf0d62249a2503bb7cdfaac6598d2d
@mcgovern612, I am facing the same problem. Did u get any solution for this ?
First thank you for the helpful script. These commands allow me to ping and communicate with systems on my local LAN and if using an exact IP connect out to the internet. However DNS doesn't work at all. If I do something like (from the namespace):
wget http://ftp.gnu.org/gnu/wget/wget-1.5.3.tar.gz
it fails to resolve. Looking at /etc/resolv.conf, it appears that it uses the systemd-resolver stub resolver at 127.0.0.53. As I understand it, this provides name resolution to applications via d-bus. A "systemd-resolve --status" gives some d-bus related errors about failing to get link data for unknown objects. /etc/resolv.conf is a symlink for:
lrwxrwxrwx 1 root root 27 Oct 22 2018 /etc/resolv.conf -> /run/resolvconf/resolv.conf
Not sure why but creating a /etc/netns/--my namespace--/resolv.conf and adding a nameserver (either an external one or internal LAN one) didn't work even w/ a reboot. The simplest way that I've been able to work around this is to just point /etc/resolv.conf to /run/systemd/resolve/resolv.conf which lists my LAN router as the nameserver.
I'm in a bit of a pinch right now so this is a good enough temporary workaround.
@neoavalon My experience tells me that any issue related with DNS resolution is likely due to a configuration problem. I run the script again and it is working fine. I've updated it however, because there was an issue with retrieving the available interfaces when the script was run without arguments. Here's the test I run:
$ sudo ./ns-inet.sh
Using interface wlp59s0
ns1> ping -c 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=54 time=46.8 ms
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 46.807/46.807/46.807/0.000 ms
ns1> ping -c 1 www.google.com
PING www.google.com (172.217.16.228) 56(84) bytes of data.
64 bytes from mad08s04-in-f4.1e100.net (172.217.16.228): icmp_seq=1 ttl=115 time=68.6 ms
--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 68.557/68.557/68.557/0.000 ms
ns1> wget www.google.com -O index.html
--2020-08-19 23:03:35-- http://www.google.com/
Resolving www.google.com (www.google.com)... 172.217.16.228, 2a00:1450:4003:805::2004
Connecting to www.google.com (www.google.com)|172.217.16.228|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘index.html’
index.html [ <=> ] 12.77K --.-KB/s in 0.001s
2020-08-19 23:03:35 (9.44 MB/s) - ‘index.html’ saved [13074]
My /etc/resolv.conf
is a proper file, not a link. What you mentioned about 127.0.0.53 sounds familiar but I think at some point I changed or tuned how systemD manages /etc/resolv.conf
. This is how '/etc/resolv.conf' looks like in my case:
# Generated by NetworkManager
# search wan
nameserver 1.1.1.1
nameserver 8.8.8.8
A minor improvement: local ifaces=$(ip li sh | cut -d " " -f 2 | tr "\n" " ")
on line 15 could be local ifaces=$(ip li sh | grep "state UP" | cut -d " " -f 2 | tr "\n" " ")
to only select interfaces that are actually up.
Had the same problem with DNS as others on Ubuntu Desktop 20.04. My /etc/resolv.conf also has nameserver 127.0.0.53
in it. I didn't have to change the /etc/resolv.conf, I only created a link. This is what I added this to the script:
mkdir -p "/etc/netns/$NS"
ln -s /run/systemd/resolve/resolv.conf /etc/netns/"$NS"/resolv.conf
And added a line to the cleanup function:
cleanup()
{
ip li delete ${VETH} 2>/dev/null
rm -rf "/etc/netns/$NS" # Removing the resov.conf and it's directory
}
Will ping work?
$ sudo ./ns-inet.sh
Using interface enp5s0
ns1> ping www.google.com
PING www.google.com (172.217.21.132) 56(84) bytes of data.
64 bytes from arn11s02-in-f4.1e100.net (172.217.21.132): icmp_seq=1 ttl=115 time=10.4 ms
Hurray!
Thanx for the script!
It would work ONLY if your local router has the allow policy to pass a packets with an appropriate addresses. Not all the routers have those policies. Your script does not work for me. In concrete I can not ping veth interface from it's paired one in the child ns and back. So I can not ping any external resources from the child ns
I tried this script, though I set VETH_ADDR
to 192.168.0.1
and VPEER_ADDR
to 192.168.0.2
.
inside the namespace, I can ping 192.168.0.1
, and I can ping my computer's actual ipv4: 10.0.0.199
, but I can't ping the gateway 10.0.0.1
from inside the namespace. And it goes without saying, but I also can't ping any internet addresses like 1.1.1.1
for example.
Maybe I have the issue that @oleshii mentioned, though I'm not sure. I have Xfinity, and we currently have their XB6 modem. If their default settings do allow this however, then I'm still not sure what the issue is.
I'm on Manjaro. I don't believe there was any firewall preinstalled.
It's weird too, because, I had it working last night. Not this script specifically, but I followed this guide:
https://blog.laxu.de/2020/04/02/how-use-zoom-sandbox/
Yet after restarting my computer (and running the commands again once I realized my changes hadn't been saved), it doesn't work, which is the main reason I tried this script.
Considering the feedback from the last comments, I run the script again on my laptop and I can tell it works for me.
$ sudo ./ns-inet.sh
Using interface wlp59s0
ns1> ping -c 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=61 time=2.04 ms
--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.044/2.044/2.044/0.000 ms
ns1> ping -c 1 www.google.com
PING www.google.com (172.217.24.36) 56(84) bytes of data.
64 bytes from hkg07s23-in-f36.1e100.net (172.217.24.36): icmp_seq=1 ttl=116 time=6.18 ms
--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.181/6.181/6.181/0.000 ms
@Magicrafter13 the script also works for me using VETH_ADDR and VPEER_ADDR as 192.168.0.1 and 192.168.0.2. Would you mind sharing your routing table (route -n
) in the network namespace as well as in the host?
I don't think using different private IP prefix block would cause trouble. @dpino
if the system uses a local dns server (like dnsmasqd on ubuntu ), a resolv.conf needs to be setup for the namespace, for example
mkdir -p /etc/netns/ns1/
echo 'nameserver 8.8.8.8' > /etc/netns/ns1/resolv.conf
For anyone else, this resolved my name resolution issue on Ubuntu 22.04. Thanks @markododa.
if the system uses a local dns server (like dnsmasqd on ubuntu ), a resolv.conf needs to be setup for the namespace, for example
mkdir -p /etc/netns/ns1/
echo 'nameserver 8.8.8.8' > /etc/netns/ns1/resolv.conf