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.