简体   繁体   中英

How to receive a UDP broadcast sent to 255.255.255.255 using boost asio?

I have written a device discovery program that can run in client or server mode. In client mode it sends a UDP broadcast packet to 255.255.255.255 on port 30000 and then listens for responses on port 30001. In server mode it listens for a UDP broadcast on port 30000 and sends a UDP broadcast packet to 255.255.255.255 on port 30001 in response.

When I run this program on 2 devices with IP addresses 192.168.10.61 and 192.168.10.62 it all works perfectly. The whole point of this program is to allow devices with unknown IP addresses to discover one another so long as they are connected to the same physical network. So to test that, I changed the IP address of the first device to something random like 12.34.56.42/255.255.240. And now it stops working.

Using tcpdump on the 192.168.10.62 machine I can see that the UDP packet from the 12.134.56.42 machine was received:

# tcpdump -i eth0 port 30000 -c 1 -v
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:38:02.552427 IP (tos 0x0, ttl 64, id 18835, offset 0, flags [DF], proto UDP (17), length 49)
12.34.56.42.56815 > 255.255.255.255.30000: UDP, length 21
1 packet captured
6 packets received by filter
0 packets dropped by kernel

but my discovery program no longer receives it. This is the code I am using to receive the UDP broadcast packets:

int WaitForPacket(uint16_t portNum, vector<char>& udpBuf, udp::endpoint& remoteEndpoint, const chrono::milliseconds timeout)
{
    io_service ioService;

    udp::socket socket(ioService, udp::endpoint(boost::asio::ip::address_v4::any(), portNum));
    socket.set_option(socket_base::broadcast(true));

    boost::system::error_code error;
    int numBytes = receive_from(socket, buffer(udpBuf), remoteEndpoint, error, timeout);

    if (error && error != error::message_size && error != error::timed_out)
    {
        printf("Got error: %s\n", error.message().c_str());
        return -1;
    }

    return numBytes;
}

/*
 * The boost asio library does not provide a blocking read with timeout function so we have to roll our own.
 */
int receive_from(
    boost::asio::ip::udp::socket&         socket,
    const boost::asio::mutable_buffers_1& buf,
    boost::asio::ip::udp::endpoint&       remoteEndpoint,
    boost::system::error_code&            error,
    chrono::milliseconds                  timeout)
{
    volatile bool ioDone = false;
    int numBytesReceived = 0;
    boost::asio::io_service& ioService = socket.get_io_service();    

    socket.async_receive_from(buf, remoteEndpoint,
                              [&error, &ioDone, &numBytesReceived](const boost::system::error_code& errorAsync, size_t bytesReceived)
                              {
                                  ioDone = true;
                                  error = errorAsync;
                                  numBytesReceived = bytesReceived;
                              });

    this_thread::sleep_for(chrono::milliseconds(100));
    ioService.reset();
    ioService.poll_one();

    auto endTime = chrono::system_clock::now() + timeout;

    while (!ioDone)
    {
        ioService.reset();
        this_thread::sleep_for(chrono::milliseconds(100));
        auto now = chrono::system_clock::now();
        if (now > endTime)
        {
            socket.cancel();
            error = error::timed_out;
            return 0;
        }
        ioService.poll_one();
    }
    ioService.reset();

    return numBytesReceived;
}

I checked similar questions on Stack Overflow and found some that said the receiving socket has to be bound to INADDR_ANY in order to receive broadcast packets. I was originally creating the socket like this udp::socket socket(ioService, udp::endpoint(udp::v4(), portNum)); but have now changed it to use ip::address_v4::any() instead. That didn't make any difference.

Can anybody tell me what I need to change in order to receive the UDP broadcast packet as expected ?

I am running this on iMX6 devices running Linux and am compiling using boost 1.58.

I finally discovered why my code was never seeing the broadcast packets even though tcpdump proved that they were being received. After finding this StackOverflow question:

Disable reverse path filtering from Linux kernel space

it seems that I just needed to disable Reverse Path Filtering on both hosts like this:

echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter

and then my code worked perfectly with no modifications. Hopefully this will help other people wondering why they can't get UDP broadcasts to the network broadcast address (255.255.255.255) to work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM