Broadcast packets do not propagate through a Linux veth tunnel

489 Views Asked by At

I am developing a networking application that relies on subnet broadcasting. I decided to set up a controlled environment for testing and development purposes on my local machine using virtual Ethernet interfaces (veth). The setup is straightforward:

ip link add veth0 type veth peer name veth1
ifconfig veth0 192.168.241.1 netmask 255.255.255.0 up
ifconfig veth1 192.168.241.2 netmask 255.255.255.0 up

ip link confirms that the interfaces are up and that the broadcast flags are set:

8: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 46:29:76:81:27:af brd ff:ff:ff:ff:ff:ff
9: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 3a:ad:f9:cb:28:a8 brd ff:ff:ff:ff:ff:ff

A trivial test in Python REPL shows that the interfaces are functional. This is the receiver:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.bind(('192.168.241.2', 48469))
>>> s.recvfrom(1000)
(b'abc', ('192.168.241.1', 45560))

And this is the sender:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.connect(('192.168.241.2', 48469))
>>> s.send(b'abc')
3

Works well for unicast packets. Broadcast packets, however, appear to be silently dropped. The following sender-side REPL can be used to reproduce the problem:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
>>> s.connect(('192.168.241.255', 48469))
>>> s.send(b'abc')
3
>>> s  # For diagnostic purposes
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('192.168.241.1', 48065), raddr=('192.168.241.255', 48469)>

The socket is configured successfully, but the emitted broadcast packets never make it to the other side of the tunnel. This is not the behavior I expected, especially considering that the interface flags indicate that the interfaces support broadcasting. I am running these tests on Linux Mint 18 with a stock kernel. What am I missing?

1

There are 1 best solutions below

0
On BEST ANSWER

The tunnel is fine.

The problem was that Linux discards all incoming broadcast traffic unless the socket is bound to INADDR_ANY. If the receiver-side REPL is updated as follows, everything works:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.bind(('', 48469))  # Bind to INADDR_ANY to accept broadcast packets
>>> s.recvfrom(1000)
(b'abc', ('192.168.241.1', 45560))

On Windows, the behavior is different (irrespective of the tunnel): binding a socket to a specific interface does not reject broadcast traffic.

Man 7 IP says that SO_BROADCAST should be set in order to receive broadcast datagrams; however, the behavior I am observing on the receiving side does not seem to match the description:

Datagrams to broadcast addresses can be sent or received only when the SO_BROADCAST socket flag is set.

Broadcast datagrams can always be received with INADDR_ANY; never if the socket is bound to a specific interface. Both statements hold irrespective of s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1).