net-bridge-tunnel.py tunnels broadcast and multicast traffic bidirectionally between two Linux nodes over a single TCP connection.
serverandclientmode- Length-prefixed TCP framing (
4bytes, network byte order) - Scapy sniff + inject (
sendpat Layer 2) - Loop prevention with local MAC exclusion in the BPF filter
- Concurrent sniffing and receiving via threads
- Reconnect logic in client mode
- Graceful shutdown on
SIGINTandSIGTERM
- Linux
- Python
3.7+ - Root privileges (raw sockets)
- Scapy:
python3 -m pip install scapysudo ./net-bridge-tunnel.py \
--mode server \
--port 9999 \
--interface br0 \
--filter "broadcast or multicast" \
--loglevel INFOsudo ./net-bridge-tunnel.py \
--mode client \
--remote-ip 10.0.0.10 \
--port 9999 \
--interface br0 \
--filter "broadcast or multicast" \
--loglevel INFOThe script explicitly builds the sniff filter as:
({user_filter}) and not ether src {local_mac}
With default arguments this becomes:
(broadcast or multicast) and not ether src aa:bb:cc:dd:ee:ff
This prevents frames injected by the script from being captured again and re-tunneled.
--mode:serverorclient(required)--remote-ip: required inclientmode--port: TCP port, default9999--interface: local interface, e.g.eth0orbr0--filter: extra BPF filter, defaultbroadcast or multicast--loglevel:DEBUG,INFO,WARNING
Place files:
net-bridge-tunnel.pyto/usr/local/bin/net-bridge-tunnel.pynet-bridge-tunnel-server.serviceto/etc/systemd/system/net-bridge-tunnel-server.servicenet-bridge-tunnel-client.serviceto/etc/systemd/system/net-bridge-tunnel-client.service
Make script executable:
sudo chmod +x /usr/local/bin/net-bridge-tunnel.pyReload and enable service:
sudo systemctl daemon-reload
sudo systemctl enable --now net-bridge-tunnel-server.service
sudo systemctl status net-bridge-tunnel-server.serviceFor the client service: adjust --remote-ip and --interface in the unit file.
Follow logs:
sudo journalctl -u net-bridge-tunnel-server.service -f- The script requires root privileges because Scapy uses raw sockets.
- The client automatically retries on disconnect.
- The server accepts one connection, handles the session, then waits for a new connection.
- Frames larger than
10 MBare dropped as a safety measure.