I've been running into an issue where a Linux networking bridge I create on Ubuntu 18.04 cannot access the Internet. I have a network namespace in Linux that I want to run an application in. I want this application to be able to send outbound packets to the Internet. Therefore, I setup a veth pair and put the peer inside of the network namespace. Veth1
is the veth on the host machine/default network namespace and veth2
is the veth inside the custom network namespace (test). I then setup a Linux bridge on the host and added veth1
to it. Here are commands I've ran to achieve this:
# Create namespace.
ip netns add test
# Put up loopback interface.
ip netns exec test ip link set lo up
# Create veth pair.
ip link add veth1 type veth peer name veth2
# Put veth2 inside namespace.
ip link set veth2 netns test
# Add IP address to veth2 inside namespace.
ip netns exec test ip addr add 172.20.0.2/16 dev veth2
# Put veth2 up.
ip netns exec test ip link set veth2 up
# Delete default route in namespace.
ip netns exec test ip route delete default
# Add veth2 to default route in namespace.
ip netns exec test ip route add default dev veth2
# Create bridge br0.
ip link add br0 type bridge
# Add veth1 to bridge (I've also tried 'brctl addif br0 veth1').
ip link set veth1 master br0
# Add IP to br0.
ip addr add 172.20.0.1/16 dev br0
# Put br0 up.
ip link set br0 up
Initially, I was trying to get this to work for an application I didn't create. The application was sending outbound packets through the veth2
interface inside of the network namespace since that's the default route. However, all it sent was ARP requests (who-has) and it never received any sort of response. Therefore, I decided to create my own C program that uses AF_PACKET
sockets. Here is the code for anyone wondering. All it does is bind to a specific interface and sends an empty UDP packet to a destination specified in the command line. I also made it so you can set the source IP in the command line. One other thing I'd like to note is the program retrieves the MAC address of the gateway and uses that as the destination MAC for the Ethernet header (I wasn't sure what to set the destination MAC to and read setting it to the gateway MAC address should work since ARP requests shouldn't go to IPs outside of the network).
When executing the program inside the network namespace like this:
ip netns exec test ./test_veth veth2 10.50.0.11 10.50.0.3
Traffic never reaches 10.50.0.3
. I can see the traffic on veth1
and br0
via tcpdump
. Here's an example of br0
:
root@netvm02:/home/roy# tcpdump -i br0 -nne
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:29:13.928570 42:7d:2a:5e:8c:78 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:29:14.928741 42:7d:2a:5e:8c:78 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:29:15.928957 42:7d:2a:5e:8c:78 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:29:16.929181 42:7d:2a:5e:8c:78 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:29:17.929412 42:7d:2a:5e:8c:78 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
When I run the program inside the default network namespace and attached to veth
, I never end up seeing the traffic on br0
. This might be because of my program setting the destination MAC to the gateway, though:
root@netvm02:/home/roy# tcpdump -i veth1 -nne
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes
14:30:58.397476 02:a2:0f:2a:7b:bf > 78:8a:20:ba:e1:f9, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:30:59.397707 02:a2:0f:2a:7b:bf > 78:8a:20:ba:e1:f9, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:31:00.398022 02:a2:0f:2a:7b:bf > 78:8a:20:ba:e1:f9, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:31:01.398295 02:a2:0f:2a:7b:bf > 78:8a:20:ba:e1:f9, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
14:31:02.398544 02:a2:0f:2a:7b:bf > 78:8a:20:ba:e1:f9, ethertype IPv4 (0x0800), length 42: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
I tried also attaching the program to br0
and 10.50.0.3
still doesn't see the traffic. Therefore, I'm assuming there's something wrong with the bridge.
If I attach it to the main interface (ens18
in this case), I can see traffic on 10.50.0.3
:
root@test02:/home/roy# tcpdump -i any host 10.50.0.11 and udp -nne
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
22:17:59.964569 In 78:8a:20:ba:e1:f9 ethertype IPv4 (0x0800), length 58: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
22:18:00.964726 In 78:8a:20:ba:e1:f9 ethertype IPv4 (0x0800), length 58: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
22:18:01.965059 In 78:8a:20:ba:e1:f9 ethertype IPv4 (0x0800), length 58: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
22:18:02.965271 In 78:8a:20:ba:e1:f9 ethertype IPv4 (0x0800), length 58: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
22:18:03.965544 In 78:8a:20:ba:e1:f9 ethertype IPv4 (0x0800), length 58: 10.50.0.11.15000 > 10.50.0.3.25000: UDP, length 0
I've also tried adding the physical interface (ens18
) to the bridge via brctl
(bridge-utils):
brctl addif br0 ens18
This results in the VM not able to send any packets outbound and connection to the VM is lost.
I've tried masquerading both 172.20.0.0/16
and the br0
interface via:
iptables -t nat -A POSTROUTING -s 172.20.0.0/16 -j MASQUERADE
iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE
Unfortunately, neither of these worked. What's weird is when running the program, I'm not seeing any packets being processed by these rules when running iptables -t nat -L -n -v
:
Chain POSTROUTING (policy ACCEPT 5 packets, 355 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 172.20.0.0/16 0.0.0.0/0
0 0 MASQUERADE all -- * br0 0.0.0.0/0 0.0.0.0/0
I also tried setting the source IP of the program to 172.20.0.2
to see if the first rule would process the packets. Sadly, it didn't.
I've also tried setting net.ipv4.ip_forward
to 1
via sysctl net.ipv4.ip_forward=1
. I had no luck with this as well, though.
Here are the forwarding rules I tried in IPTables:
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- A A 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br0 br0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br0 !br0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
0 0 ACCEPT all -- A br0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br0 A 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- ens18 br0 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- br0 ens18 0.0.0.0/0 0.0.0.0/0
I know a lot of these are probably useless, but I was just trying things to see if they made any difference.
Here is additional information including a full ifconfig
and more:
root@netvm02:/home/roy# ifconfig
veth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 02:a2:0f:2a:7b:bf txqueuelen 1000 (Ethernet)
RX packets 3655 bytes 154906 (154.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2380 bytes 101548 (101.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.20.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::185a:96ff:fe62:d174 prefixlen 64 scopeid 0x20<link>
ether 02:a2:0f:2a:7b:bf txqueuelen 1000 (Ethernet)
RX packets 726 bytes 55088 (55.0 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 276 bytes 12624 (12.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens18: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.50.0.11 netmask 255.255.255.0 broadcast 10.50.0.255
inet6 fe80::e087:deff:fe1f:d504 prefixlen 64 scopeid 0x20<link>
ether e2:87:de:1f:d5:04 txqueuelen 1000 (Ethernet)
RX packets 1423812 bytes 306465717 (306.4 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1694988587 bytes 2103526747383 (2.1 TB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 2436 bytes 223919 (223.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2436 bytes 223919 (223.9 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@netvm02:/home/roy# ip netns exec test ifconfig
veth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.20.0.2 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::407d:2aff:fe5e:8c78 prefixlen 64 scopeid 0x20<link>
ether 42:7d:2a:5e:8c:78 txqueuelen 1000 (Ethernet)
RX packets 2380 bytes 101548 (101.5 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3677 bytes 155830 (155.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@netvm02:/home/roy# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> 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
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether e2:87:de:1f:d5:04 brd ff:ff:ff:ff:ff:ff
inet 10.50.0.11/24 brd 10.50.0.255 scope global dynamic ens18
valid_lft 80490sec preferred_lft 80490sec
inet6 fe80::e087:deff:fe1f:d504/64 scope link
valid_lft forever preferred_lft forever
4: veth1@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP group default qlen 1000
link/ether 02:a2:0f:2a:7b:bf brd ff:ff:ff:ff:ff:ff link-netnsid 0
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 02:a2:0f:2a:7b:bf brd ff:ff:ff:ff:ff:ff
inet 172.20.0.1/16 scope global br0
valid_lft forever preferred_lft forever
inet6 fe80::185a:96ff:fe62:d174/64 scope link
valid_lft forever preferred_lft forever
root@netvm02:/home/roy# ip netns exec test ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> 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
3: veth2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 42:7d:2a:5e:8c:78 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.20.0.2/16 scope global veth2
valid_lft forever preferred_lft forever
inet6 fe80::407d:2aff:fe5e:8c78/64 scope link
valid_lft forever preferred_lft forever
root@netvm02:/home/roy# ip route
default via 10.50.0.1 dev ens18 proto dhcp src 10.50.0.11 metric 100
10.50.0.0/24 dev ens18 proto kernel scope link src 10.50.0.11
10.50.0.1 dev ens18 proto dhcp scope link src 10.50.0.11 metric 100
172.20.0.0/16 dev br0 proto kernel scope link src 172.20.0.1
root@netvm02:/home/roy# ip netns exec test ip route
default dev veth2 scope link
172.20.0.0/16 dev veth2 proto kernel scope link src 172.20.0.2
root@netvm02:/home/roy# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.02a20f2a7bbf no veth1
Additionally, both 10.50.0.11
and 10.50.0.3
are VMs on my home server running ProxMox. They're using DHCP on the main interfaces (ens18), but have static IP mappings from my Edge Router.
I haven't messed with bridges or veths much before this, so there is probably something I'm missing.
I just want traffic from br0
to be able to reach the Internet. In the above, I am testing connections on my local network, but the application I plan to run will be sending packets to IPs outside of the network.
If you need any additional information, please let me know!
Any help is highly appreciated and thank you for your time!
You have to treat a separate network namespace as though it were a different host and the connection between the veth pair as the line where the external packets come in. So you MUST activate routing. The iptables in the main namespace will see the packets in PREROUTING and POSTROUTING and INPUT and OUTPUT.
So to setup the outbound functions (replace
eth0
with your outward interface):Now you can test with
ip netns exec test ping example.com