Home > Software design >  Why I can send packets through `eth0` but not `eth1` in Docker container?
Why I can send packets through `eth0` but not `eth1` in Docker container?

Time:12-20

I created a docker container and connected it to two bridge networks as:

# network 1
docker network create --driver=bridge network1 --subnet=172.56.0.0/24

#network 2
docker network create --driver=bridge network2 --subnet=172.56.1.0/24

docker run \
    --name container \
    --privileged \
    --cap-add=ALL -d \
    -v /dev:/dev \
    --network network1 \
    -v /lib/modules:/lib/modules \
    container-image tail -f /dev/null

docker network connect network2 container

Now, if I run ip addr inside container, I have two ethernet network interfaces:

6551: eth0@if6552: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:38:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.56.0.2/24 brd 172.56.0.255 scope global eth0
       valid_lft forever preferred_lft forever
6553: eth1@if6554: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:38:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.56.1.2/24 brd 172.56.1.255 scope global eth1
       valid_lft forever preferred_lft forever

I'm using scapy to send/receive through IP and ICMP protocol, with something like this:

from scapy.all import *
import sys

src = sys.argv[1]
dst = sys.argv[2]

msg = "Hello World!"

packet = IP(src = src, dst = dst)/ICMP()/msg

data = sr1(packet).load.decode('utf-8')
print(f"Received {data!r}")

I'm able to run it when src = 172.56.0.2 and dst = www.google.com or when I'm using eth0 as a source, but if I change it to src = 172.56.1.2, it won't work at all. Is there anything wrong with my eth1 interface here? Any help would be appreciated.

CodePudding user response:

The problem is the routing table. Take a look at conf.route:

>>> from scapy.all import *
>>> conf.route
Network     Netmask        Gateway     Iface  Output IP   Metric
0.0.0.0     0.0.0.0        172.56.0.1  eth0   172.56.0.2  0
127.0.0.0   255.0.0.0      0.0.0.0     lo     127.0.0.1   1
172.56.0.0  255.255.255.0  0.0.0.0     eth0   172.56.0.2  0
172.56.1.0  255.255.255.0  0.0.0.0     eth1   172.56.1.2  0

In the above route table, the default route is via 172.56.0.1. Any attempt to reach an address that isn't on a directly connected network will be sent via the default gateway, which is only reachable via eth0. If you want your request to go out eth1, you need to modify your routing table. For example, we can replace the default route:

>>> conf.route.delt(net='0.0.0.0/0', gw='172.56.0.1', metric=0)
>>> conf.route.add(net='0.0.0.0/0', gw='172.56.1.1', metric=0)
>>> conf.route
Network     Netmask        Gateway     Iface  Output IP   Metric
0.0.0.0     0.0.0.0        172.56.1.1  eth1   172.56.1.2  0
127.0.0.0   255.0.0.0      0.0.0.0     lo     127.0.0.1   1
172.56.0.0  255.255.255.0  0.0.0.0     eth0   172.56.0.2  0
172.56.1.0  255.255.255.0  0.0.0.0     eth1   172.56.1.2  0

With this modified routing table, our requests will be sent out eth1.

If we assume that the appropriate gateway will always be the .1 address associated with the source interface, we can rewrite your code like this to automatically apply the correct route:

from scapy.all import *
import sys
import ipaddress

src = ipaddress.ip_interface(sys.argv[1])
dst = sys.argv[2]

gw = src.network[1]
conf.route.routes = [route for route in conf.route.routes if route[1] != 0]
conf.route.add(net='0.0.0.0/0', gw=f'{gw}', metric=0)

msg = "Hello World!"

packet = IP(src = f'{src.ip}', dst=dst)/ICMP()/msg
data = sr1(packet).load.decode('utf-8')
print(f"Received {data!r}")

With the modified code, the first argument (sys.argv[1]) needs to be an address/mask expression. This now works with both interface addresses:

root@bacb8598b801:~# python sendpacket.py 172.56.0.2/24 8.8.8.8
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
Received 'Hello World!'
root@bacb8598b801:~# python sendpacket.py 172.56.1.2/24 8.8.8.8
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
Received 'Hello World!'

Watching tcpdump on the two bridge interfaces, you can see that traffic is being routed via the expected interface for each source address.

  • Related