Udp-Broadcast on All Interfaces

UDP-Broadcast on all interfaces

First of all, you should consider broadcast obsolete, specially INADDR_BROADCAST (255.255.255.255). Your question highlights exactly one of the reasons that broadcast is unsuitable. It should die along with IPv4 (hopefully). Note that IPv6 doesn't even have a concept of broadcast (multicast is used, instead).

INADDR_BROADCAST is limited to the local link. Nowadays, it's only visible use is for DHCP auto-configuration, since at such time, the client will not know yet in what network it is connected to.

With a single sendto(), only a single packet is generated, and the outgoing interface is determined by the operating system's routing table (ip route on linux). You can't have a single sendto() generate more than one packet, you would have to iterate over all interfaces, and either use raw sockets or bind the socket to a device using setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX") to send each packet bypassing the OS routing table (this requires root privileges). Not a good solution.

Instead, since INADDR_BROADCAST is not routed anyway, you can achieve almost the same thing by iterating over each interface, and sending the packet to its broadcast address. For example, assuming that your networks have 255.255.255.0 (/24) masks, the broadcast addresses are 192.168.1.255 and 192.168.2.255. Call sendto() once for each of these addresses and you will have accomplished your goal.


Edit: fixed information regarding to INADDR_BROADCAST, and complementing the answer with information about SO_BINDTODEVICE.

Broadcasting UDP message to all the available network cards

This is actually trickier than it sounds because if you have more than one interface the broadcasts will not always go out to all the interfaces. To get around this I created this class.

public class MyUdpClient : UdpClient
{
public MyUdpClient() : base()
{
//Calls the protected Client property belonging to the UdpClient base class.
Socket s = this.Client;
//Uses the Socket returned by Client to set an option that is not available using UdpClient.
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
}

public MyUdpClient(IPEndPoint ipLocalEndPoint) : base(ipLocalEndPoint)
{
//Calls the protected Client property belonging to the UdpClient base class.
Socket s = this.Client;
//Uses the Socket returned by Client to set an option that is not available using UdpClient.
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
}

}

Then to send the UDP packet via broadcast, I use something like the following. I am using IPAddress.Broadcast and MyUdpClient, which is different from your code.

IPEndPoint  localEndPoint  = new IPEndPoint(IPAddress.Parse(LocalIP), 0);
IPEndPoint targetEndPoint = new IPEndPoint(IPAddress.Broadcast, iTargetPort);
MyUdpClient sendUdpClient = new MyUdpClient(localEndPoint);
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

Also, you should note that when you use a specific ipaddress instead of broadcast the route table only sends it out the interface that matches the address.

So in your example, unicast is used. You need to set LocalIP to the IP of the local interface you want to send out to. With three interfaces, you would have three local IP's and you need to pick the correct one to use.

IPEndPoint  localEndPoint  = new IPEndPoint(IPAddress.Parse(LocalIP), 0);
IPEndPoint targetEndPoint = new IPEndPoint(TargetIP, iTargetPort);
MyUdpClient sendUdpClient = new MyUdpClient(localEndPoint);
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

Because route is turned off you might see it on all interfaces but you will need to test this for the unicast case.

If you don't care about the send IP or port you can use the following code.

IPEndPoint  targetEndPoint = new IPEndPoint(TargetIP, iTargetPort);
MyUdpClient sendUdpClient = new MyUdpClient();
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

or for broadcast

IPEndPoint  targetEndPoint = new IPEndPoint(IPAddress.Broadcast, iTargetPort);
MyUdpClient sendUdpClient = new MyUdpClient();
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

The problem with IPAddress.Broadcast is that they will not route through any gateways. To get around this you can create a list of IPAddresses and then loop through and send. Also since Send can fail for network issues that you cannot control you should also have a try/catch block.

ArrayList ip_addr_acq = new ArrayList();

ip_addr_acq.Add(IPAddress.Parse("10.1.1.1")); // add to list of address to send to

try
{
foreach (IPAddress curAdd in ip_addr_acq)
{
IPEndPoint targetEndPoint = new IPEndPoint(curAdd , iTargetPort);
MyUdpClient sendUdpClient = new MyUdpClient();
int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);

Thread.Sleep(40); //small delay between each message
}
}
catch
{
// handle any exceptions
}

Edit: see above change to unicast with multiple interfaces and also Problem Trying to unicast packets to available networks.

boost asio broadcast not going out on all interfaces

As suggested by Alan Birtles in the comments to the question i found an explanation here:
UDP-Broadcast on all interfaces

I solved the issue by iterating over he configured interfaces and sending the broadcast to each networks broadcast address as suggested by the linked answer.

How to create a service that sends/receives UDP broadcasts on multiple interfaces

You could install the binary with CAP_NET_RAW (and CAP_NET_BIND_SERVICE if ports ≤ 1024 are used); setcap 'cap_net_raw=ep' yourdaemon as root. For IP, SO_BROADCAST does not require any capabilities (in particular, CAP_NET_BROADCAST is not used for IP).

(For the exact capabilities needed, see e.g. net/core/sock.c:sock_setbindtodevice(), net/core/sock.c:sock_setsockopt(), and include/net/sock.h:sock_set_flag() in the Linux kernel sources for verification.)

However, daemons are typically started as root. Here, the above would not suffice, as changing the user ID for the process (to drop privileges) also clears the effective capabilities. Yet, I too prefer my services to run with limited privileges.

I would choose between two basic approaches:

  1. Require that the daemon is executed by root, or with CAP_NET_RAW (and optionally CAP_NET_BIND_SERVICE) capabilities.

    Use prctl(), setgroups() or initgroups(), setresuid(), setresgid(), and from libcap, cap_init(), cap_set_flag(), and cap_set_proc() to drop privileges by switching to a dedicated user and group, but retaining the CAP_NET_RAW (and optionally CAP_NET_BIND_SERVICE) capabilities and them only.

    This allows the daemon to respond to e.g. HUP signal without completely restarting, as it has the necessary privileges to enumerate interfaces and read its own configuration files to open sockets for new interfaces.

  2. Use a privileged "loader", that opens all the necessary sockets, drops privileges, and executes the actual daemon.

    The daemon should get the socket and interface details as command-line parameters, or perhaps via standard input. The daemon is completely unprivileged.

    Unfortunately, if new interfaces are opened, or the configuration is changed, the daemon cannot do much except exit. (It cannot even execute the privileged loader, because privileges have already been dropped.)

The first approach is more common, and easier to implement in practice; especially if the daemon is only supposed to be executed by root. (Remember, the daemon can respond to configuration changes, since it has the necessary capabilities but not root privileges in general.) I have only used the second approach for "black box" binaries I do not trust.


Here is some example code.

privileges.h:
#ifndef PRIVILEGES_H
#define PRIVILEGES_H

#define   NEED_CAP_NET_ADMIN          (1U << 0)
#define NEED_CAP_NET_BIND_SERVICE (1U << 1)
#define NEED_CAP_NET_RAW (1U << 2)

extern int drop_privileges(const char *const user, const unsigned int capabilities);

#endif /* PRIVILEGES_H */

privileges.c:

#define _GNU_SOURCE
#define _BSD_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include "privileges.h"

/* Only three NEED_CAP_ constants defined. */
#define MAX_CAPABILITIES 3

static int permit_effective(cap_t caps, const unsigned int capabilities)
{
cap_value_t value[MAX_CAPABILITIES];
int values = 0;

if (capabilities & NEED_CAP_NET_ADMIN)
value[values++] = CAP_NET_ADMIN;

if (capabilities & NEED_CAP_NET_BIND_SERVICE)
value[values++] = CAP_NET_BIND_SERVICE;

if (capabilities & NEED_CAP_NET_RAW)
value[values++] = CAP_NET_RAW;

if (values < 1)
return 0;

if (cap_set_flag(caps, CAP_PERMITTED, values, value, CAP_SET) == -1)
return errno;
if (cap_set_flag(caps, CAP_EFFECTIVE, values, value, CAP_SET) == -1)
return errno;

return 0;
}

static int add_privileges(cap_t caps)
{
cap_value_t value[3] = { CAP_SETPCAP, CAP_SETUID, CAP_SETGID };

if (cap_set_flag(caps, CAP_PERMITTED, sizeof value / sizeof value[0], value, CAP_SET) == -1)
return errno;

if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof value / sizeof value[0], value, CAP_SET) == -1)
return errno;

return 0;
}

int drop_privileges(const char *const user, const unsigned int capabilities)
{
uid_t uid;
gid_t gid;
cap_t caps;

/* Make sure user is neither NULL nor empty. */
if (!user || !user[0])
return errno = EINVAL;

/* Find the user. */
{
struct passwd *pw;

pw = getpwnam(user);
if (!pw
#ifdef UID_MIN
|| pw->pw_uid < (uid_t)UID_MIN
#endif
#ifdef UID_MAX
|| pw->pw_uid > (uid_t)UID_MAX
#endif
#ifdef GID_MIN
|| pw->pw_gid < (gid_t)GID_MIN
#endif
#ifdef GID_MAX
|| pw->pw_gid > (gid_t)GID_MAX
#endif
)
return errno = EINVAL;

uid = pw->pw_uid;
gid = pw->pw_gid;

endpwent();
}

/* Install privileged capabilities. */
caps = cap_init();
if (!caps)
return errno = ENOMEM;
if (permit_effective(caps, capabilities)) {
const int cause = errno;
cap_free(caps);
return errno = cause;
}
if (add_privileges(caps)) {
const int cause = errno;
cap_free(caps);
return errno = cause;
}
if (cap_set_proc(caps) == -1) {
const int cause = errno;
cap_free(caps);
return errno = cause;
}
cap_free(caps);

/* Retain permitted capabilities over the identity change. */
prctl(PR_SET_KEEPCAPS, 1UL, 0UL,0UL,0UL);

if (setresgid(gid, gid, gid) == -1)
return errno = EPERM;

if (initgroups(user, gid) == -1)
return errno = EPERM;

if (setresuid(uid, uid, uid) == -1)
return errno = EPERM;

/* Install unprivileged capabilities. */
caps = cap_init();
if (!caps)
return errno = ENOMEM;
if (permit_effective(caps, capabilities)) {
const int cause = errno;
cap_free(caps);
return errno = cause;
}
if (cap_set_proc(caps) == -1) {
const int cause = errno;
cap_free(caps);
return errno = cause;
}
cap_free(caps);

/* Reset standard KEEPCAPS behaviour. */
prctl(PR_SET_KEEPCAPS, 0UL, 0UL,0UL,0UL);

/* Done. */
return 0;
}

udp-broadcast.h:

#ifndef   UDP_BROADCAST_H
#define UDP_BROADCAST_H
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>

struct udp_socket {
struct sockaddr_in broadcast; /* Broadcast address */
unsigned int if_index; /* Interface index */
int descriptor; /* Socket descriptor */
};

extern int open_udp_broadcast(struct udp_socket *const udpsocket,
const char *const interface,
int const port);

extern int udp_broadcast(const struct udp_socket *const udpsocket,
const void *const data,
const size_t size,
const int flags);

extern size_t udp_receive(const struct udp_socket *const udpsocket,
void *const data,
const size_t size_max,
const int flags,
struct sockaddr_in *const from_addr,
struct sockaddr_in *const to_addr,
struct sockaddr_in *const hdr_addr,
unsigned int *const if_index);

#endif /* UDP_BROADCAST_H */

udp-broadcast.c:

#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <errno.h>
#include "udp-broadcast.h"


int udp_broadcast(const struct udp_socket *const udpsocket,
const void *const data,
const size_t size,
const int flags)
{
ssize_t n;

if (!udpsocket || udpsocket->broadcast.sin_family != AF_INET)
return errno = EINVAL;

if (!data || size < 1)
return 0;

n = sendto(udpsocket->descriptor, data, size, flags,
(const struct sockaddr *)&(udpsocket->broadcast),
sizeof (struct sockaddr_in));

if (n == (ssize_t)-1)
return errno;
if (n == (ssize_t)size)
return 0;
return errno = EIO;
}


size_t udp_receive(const struct udp_socket *const udpsocket,
void *const data,
const size_t size_max,
const int flags,
struct sockaddr_in *const from_addr,
struct sockaddr_in *const to_addr,
struct sockaddr_in *const hdr_addr,
unsigned int *const if_index)
{
char ancillary[512];
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *cmsg;
ssize_t n;

if (!data || size_max < 1 || !udpsocket) {
errno = EINVAL;
return (size_t)0;
}

/* Clear results, just in case. */
if (from_addr) {
memset(from_addr, 0, sizeof *from_addr);
from_addr->sin_family = AF_UNSPEC;
}
if (to_addr) {
memset(to_addr, 0, sizeof *to_addr);
to_addr->sin_family = AF_UNSPEC;
}
if (hdr_addr) {
memset(hdr_addr, 0, sizeof *hdr_addr);
hdr_addr->sin_family = AF_UNSPEC;
}
if (if_index)
*if_index = 0U;

iov[0].iov_base = data;
iov[0].iov_len = size_max;

if (from_addr) {
msg.msg_name = from_addr;
msg.msg_namelen = sizeof (struct sockaddr_in);
} else {
msg.msg_name = NULL;
msg.msg_namelen = 0;
}

msg.msg_iov = iov;
msg.msg_iovlen = 1;

msg.msg_control = ancillary;
msg.msg_controllen = sizeof ancillary;

msg.msg_flags = 0;

n = recvmsg(udpsocket->descriptor, &msg, flags);
if (n == (ssize_t)-1)
return (size_t)0; /* errno set by recvmsg(). */
if (n < (ssize_t)1) {
errno = EIO;
return (size_t)0;
}

/* Populate data from ancillary message, if requested. */
if (to_addr || hdr_addr || if_index)
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
const struct in_pktinfo *const info = CMSG_DATA(cmsg);
if (!info)
continue;
if (if_index)
*if_index = info->ipi_ifindex;
if (to_addr) {
to_addr->sin_family = AF_INET;
to_addr->sin_port = udpsocket->broadcast.sin_port; /* This is a guess. */
to_addr->sin_addr = info->ipi_spec_dst;
}
if (hdr_addr) {
hdr_addr->sin_family = AF_INET;
hdr_addr->sin_port = udpsocket->broadcast.sin_port; /* A guess, again. */
hdr_addr->sin_addr = info->ipi_addr;
}
}

errno = 0;
return (size_t)n;
}

int open_udp_broadcast(struct udp_socket *const udpsocket,
const char *const interface,
int const port)
{
const size_t interface_len = (interface) ? strlen(interface) : 0;
const int set_flag = 1;
int sockfd;

if (udpsocket) {
memset(udpsocket, 0, sizeof *udpsocket);
udpsocket->broadcast.sin_family = AF_INET;
udpsocket->broadcast.sin_addr.s_addr = INADDR_BROADCAST;
if (port >= 1 && port <= 65535)
udpsocket->broadcast.sin_port = htons(port);
udpsocket->descriptor = -1;
}

if (!udpsocket || interface_len < 1 || port < 1 || port > 65535)
return errno = EINVAL;

/* Generic UDP socket. */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
return errno;

/* Set SO_REUSEADDR if possible. */
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set_flag, sizeof set_flag);

/* Set IP_FREEBIND if possible. */
setsockopt(sockfd, IPPROTO_IP, IP_FREEBIND, &set_flag, sizeof set_flag);

/* We need broadcast capability. */
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &set_flag, sizeof set_flag) == -1) {
const int real_errno = errno;
close(sockfd);
return errno = real_errno;
}

/* We want the IP_PKTINFO ancillary messages, to determine target address
* and interface index. */
if (setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set_flag, sizeof set_flag) == -1) {
const int real_errno = errno;
close(sockfd);
return errno = real_errno;
}

/* We bind to the broadcast address. */
if (bind(sockfd, (const struct sockaddr *)&(udpsocket->broadcast), sizeof udpsocket->broadcast) == -1) {
const int real_errno = errno;
close(sockfd);
return errno = real_errno;
}

/* Finally, we bind to the specified interface. */
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, interface, interface_len) == -1) {
const int real_errno = errno;
close(sockfd);
return errno = real_errno;
}

udpsocket->descriptor = sockfd;

udpsocket->if_index = if_nametoindex(interface);

errno = 0;
return 0;
}

main.c:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include "privileges.h"
#include "udp-broadcast.h"

static volatile sig_atomic_t done_triggered = 0;
static volatile sig_atomic_t reload_triggered = 0;

static void done_handler(int signum)
{
__sync_bool_compare_and_swap(&done_triggered, (sig_atomic_t)0, (sig_atomic_t)signum);
}

static void reload_handler(int signum)
{
__sync_bool_compare_and_swap(&reload_triggered, (sig_atomic_t)0, (sig_atomic_t)signum);
}

static int install_handler(const int signum, void (*handler)(int))
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}

/* Return 0 if done_triggered or reload_triggered, nonzero otherwise.
* Always clears reload_triggered.
*/
static inline int keep_running(void)
{
if (done_triggered)
return 0;
return !__sync_fetch_and_and(&reload_triggered, (sig_atomic_t)0);
}

static const char *ipv4_address(const void *const addr)
{
static char buffer[16];
char *end = buffer + sizeof buffer;
unsigned char byte[4];

if (!addr)
return "(none)";

memcpy(byte, addr, 4);

*(--end) = '\0';
do {
*(--end) = '0' + (byte[3] % 10);
byte[3] /= 10U;
} while (byte[3]);
*(--end) = '.';
do {
*(--end) = '0' + (byte[2] % 10);
byte[2] /= 10U;
} while (byte[2]);
*(--end) = '.';
do {
*(--end) = '0' + (byte[1] % 10);
byte[1] /= 10U;
} while (byte[1]);
*(--end) = '.';
do {
*(--end) = '0' + (byte[0] % 10);
byte[0] /= 10U;
} while (byte[0]);

return (const char *)end;
}

int main(int argc, char *argv[])
{
int port;
char dummy;

/* Check usage. */
if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s USERNAME INTERFACE PORT\n", argv[0]);
fprintf(stderr, "Where:\n");
fprintf(stderr, " USERNAME is the unprivileged user to run as,\n");
fprintf(stderr, " INTERFACE is the interface to bind to, and\n");
fprintf(stderr, " PORT is the UDP/IPv4 port number to use.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}

/* Parse the port into a number. */
if (sscanf(argv[3], "%d %c", &port, &dummy) != 1 || port < 1 || port > 65535) {
struct servent *serv = getservbyname(argv[3], "udp");
if (serv && serv->s_port > 1 && serv->s_port < 65536) {
port = serv->s_port;
endservent();
} else {
endservent();
fprintf(stderr, "%s: Invalid port.\n", argv[3]);
return EXIT_FAILURE;
}
}

/* Drop privileges. */
if (drop_privileges(argv[1], NEED_CAP_NET_RAW)) {
fprintf(stderr, "%s.\n", strerror(errno));
return EXIT_FAILURE;
}

/* Install signal handlers. */
if (install_handler(SIGINT, done_handler) ||
install_handler(SIGTERM, done_handler) ||
install_handler(SIGHUP, reload_handler) ||
install_handler(SIGUSR1, reload_handler)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}

fprintf(stderr, "Send a SIGINT (Ctrl+C) or SIGTERM to stop the service:\n");
fprintf(stderr, "\tkill -SIGTERM %ld\n", (long)getpid());
fprintf(stderr, "Send a SIGHUP or SIGUSR1 to have the service reload and rebroadcast:\n");
fprintf(stderr, "\tkill -SIGHUP %ld\n", (long)getpid());
fprintf(stderr, "Privileges dropped successfully.\n\n");
fflush(stderr);

while (!done_triggered) {
struct udp_socket s;

if (open_udp_broadcast(&s, argv[2], port)) {
fprintf(stderr, "%s port %s: %s.\n", argv[2], argv[3], strerror(errno));
return EXIT_FAILURE;
}

if (udp_broadcast(&s, "Hello?", 6, MSG_NOSIGNAL)) {
fprintf(stderr, "%s port %s: Broadcast failed: %s.\n", argv[2], argv[3], strerror(errno));
close(s.descriptor);
return EXIT_FAILURE;
}

if (s.if_index)
fprintf(stderr, "Broadcast sent using interface %s (index %u); waiting for responses.\n", argv[2], s.if_index);
else
fprintf(stderr, "Broadcast sent using interface %s; waiting for responses.\n", argv[2]);
fflush(stderr);

while (keep_running()) {
struct sockaddr_in from_addr, to_addr, hdr_addr;
unsigned char data[512];
unsigned int if_index;
size_t size, i;

size = udp_receive(&s, data, sizeof data, 0, &from_addr, &to_addr, &hdr_addr, &if_index);
if (size > 0) {
printf("Received %zu bytes:", size);
for (i = 0; i < size; i++)
if (i & 15)
printf(" %02x", data[i]);
else
printf("\n\t%02x", data[i]);
if (if_index)
printf("\n\t Index: %u", if_index);
printf("\n\t From: %s", ipv4_address(&from_addr.sin_addr));
printf("\n\t To: %s", ipv4_address(&to_addr.sin_addr));
printf("\n\tHeader: %s", ipv4_address(&hdr_addr.sin_addr));
printf("\n");
fflush(stdout);
} else
if (errno != EINTR) {
fprintf(stderr, "%s\n", strerror(errno));
break;
}
}

close(s.descriptor);
}

fprintf(stderr, "Exiting.\n");
return EXIT_SUCCESS;
}

Compile using

gcc -Wall -Wextra -O2 -c privileges.c
gcc -Wall -Wextra -O2 -c udp-broadcast.c
gcc -Wall -Wextra -O2 -c main.c
gcc -Wall -Wextra main.o udp-broadcast.o privileges.o -lcap -o example

and run the example as root, specifying an unprivileged user name to run as, the interface to bind to, and the UDP port number as parameters:

sudo ./example yourdaemonuser eth0 4000

Right now I only have one laptop in use, so receive side is basically untested. I do know that CAP_NET_RAW is sufficient here (Linux kernel 4.2.0-27 on x86-64), and that the UDP broadcast sends show up as outgoing from the ethernet interface address to 255.255.255.255:port, but I don't have another machine to send example responses to the daemon (which would be easy using e.g. NetCat: printf 'Response!' | nc -u4 -q2y interface-address port).

Please note that the code quality above is only initial test grade. Since I don't need this myself for anything, and only wanted to verify I am not talking out of my butt, I have not spent any effort in making the code clean or reliable.

Questions? Comments?

Sending broadcast in Python

The reason is that you are broadcasting on one interface and listening on another. See this answer UDP-Broadcast on all interfaces.

You need to broadcast on all interfaces, for example using the following (purely for demonstration) code. However, keep in mind that broadcasting over IP is a legacy feature which has been dropped from IPv6. Use multicasting instead.

import socket
from time import sleep

def main():
interfaces = socket.getaddrinfo(host=socket.gethostname(), port=None, family=socket.AF_INET)
allips = [ip[-1][0] for ip in interfaces]

msg = b'hello world'
while True:

for ip in allips:
print(f'sending on {ip}')
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind((ip,0))
sock.sendto(msg, ("255.255.255.255", 5005))
sock.close()

sleep(2)


main()

UDP Broadcast receive - Bind to several NIC's

If I understand you correctly, you have two NICs, connected to two physical networks (i.e. network cables, hubs), with each having a separate IP address from the same subnet address range?

The short answer is that your network configuration is wrong. If they really are separate physical networks, then they should have different subnet addresses. It depends on what you mean with separate physical networks, separate hardware? You can NOT have two separate subnets with the same subnet address. Thats why I am saying your network configuration is wrong.

However, The impression I get is that you are trying to bridge the two networks, so that the two NICs belong to the same subnet (not separate). Well, then you should bridge them. You bridge the two NICs together and assign ONE IP address to the bridge. Then you will be able to receive your packets on both NICs.

In linux:

brctl addbr br0
ifconfig eth0 0.0.0.0 down
ifconfig eth1 0.0.0.0 down
brctl addif br0 eth0
brctl addif br0 eth1
ifconfig eth0 up
ifconfig eth1 up
ifconfig br0 up
ifconfig br0 192.168.225.107 (or 192.168.225.108, whatever you prefer)


Related Topics



Leave a reply



Submit