10
votes

We have existing software that periodically broadcasts UDP packets to a specific port (7125) on the local subnet (x.x.x.255). We have monitoring software running on HP-UX (11.11) that is able to receive these packets no problem. However, after porting the monitoring software to Linux (RHEL 6.1) we have found that it does not receive the broadcast packets. tcpdump shows the packets arriving at the Linux host, but the kernel does not send them through to our software.

I've been using a couple of python 2.x scripts that mimic the socket API calls the monitoring software uses to test different scenarios. The Linux kernel passes the packets to the receiver software if the sender uses unicast (10.1.0.5), but not broadcast (10.1.0.255). I've been searching the web for several days and have not found anyone with the same problem. Any ideas?

receiver.py

from __future__ import print_function
import socket

localHost = ''
localPort = 7125
remoteHost = '10.1.0.5'
remotePort = 19100

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((localHost, localPort))
s.connect((remoteHost, remotePort))
print('Listening on {0}:{1} for traffic from {2}:{3}'.format(localHost, localPort, remoteHost, remotePort))
data = s.recv(1024)
print('Received: {0}'.format(data))
s.close()

sender.py

from __future__ import print_function
import socket
import time

localHost = ''
localPort = 19100
remoteHost = '10.1.0.255'
remotePort = 7125

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((localHost, localPort))
s.connect((remoteHost, remotePort))
data = 'sending this from {0}:{1} to {2}:{3}'.format(localHost, localPort, remoteHost, remotePort)
print(data)
print('2')
time.sleep(1)
print('1')
time.sleep(1)
s.send(data)
print('sent at {0}'.format(time.ctime()))
s.close()
1
Doesn't your receiver need to bind to the broadcast address or INADDR_BROADCAST (255.255.255.255, the INADDR_ANY of broadcast)? That is, in addition to setting the SO_BROADCAST option (on both sides), as you are doing already. Are you checking the error/return codes of all these socket system calls?Matthew Hall
@MatthewHall aha, binding to the broadcast address does work! I guess this means Linux makes you choose between unicast and broadcast? We are able to bind to INADDR_ANY and receive both unicast and broadcast packets on HP-UX.goose
Yes, it does seem that you have to choose. I've now posted a canonical answer to your question. However, I'm somewhat befuddled by why the behavior differs at all on Linux from HP-UX (though there are arguments as to why separation of broadcast and unicast would be preferable). To me, that suggests we don't know everything, and well, that's unacceptable as usual. I'm tempted to write a couple test programs in C to fully flesh out the broadcast options on Linux, although I've no HP-UX... Until then, hopefully my answer covers it.Matthew Hall
Very interesting: in Linux, as per your comment to @MatthewHall's response, I need the receiver to bind to the subnet broadcast address (10.1.0.255 in your example) for this to work, but on Windows, I must bind to localHost: exceptions are thrown if binding to the subnet broadcast address or "<broadcast>". Any idea why?Ahmed Fasih

1 Answers

16
votes

Well, I suggested this answer in a comment, and it proved correct in practice. I would like to investigate surrounding nuances further with my own code, but this is the canonical case-closer.

In addition to setting the SO_BROADCAST socket option on both sides (as you are already correctly doing), you must also bind your receiver to the broadcast address (e.g., INADDR_BROADCAST, which is 255.255.255.255 and essentially serves the same role as INADDR_ANY for unicast).

Apparently, in the HP-UX configuration of the original poster, a UDP socket bound to a unicast address (or INADDR_ANY, specifically) but with the SO_BROADCAST socket option set will still receive all UDP datagrams addressed to the local broadcast address as well as unicast traffic directed at the host.

Under Linux, this is not the case. Binding a UDP socket, even when SO_BROADCAST-enabled, to INADDR_ANY is insufficient to receive both unicast and broadcast datagrams on the bound port. One can use a separate INADDR_BROADCAST-bound SO_BROADCAST socket for the broadcast traffic.