3

I am working on BeagleBone Black (running Debian Linux) and I am trying to send some datagrams to broadcast via UDP using Qt 5.3.

Here is my code:

#include <QCoreApplication>
#include <QUdpSocket>
#include <QDebug>

#include <sys/socket.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QUdpSocket socket;

    socket.bind(QHostAddress::AnyIPv4, 1111);

    int opt=1;
    setsockopt(socket.socketDescriptor(), SOL_SOCKET, SO_BROADCAST, &opt, sizeof(int));

    QByteArray d = QString("Hello, world!").toLatin1();
    int r = socket.writeDatagram(d, QHostAddress::Broadcast, 1111);

    qDebug() << r;
    qDebug() << socket.error();
    qDebug() << socket.errorString();

    return a.exec();
}

Unluckily it does not work and the output of the program is:

-1

QAbstractSocket::NetworkError

"Unable to send a message"

So the writeDatagram primitive fails. The same exact code works perfectly fine when compiled for my desktop PC... So I am assuming that the code is good and probably there is something specifically related to BBB.

I also tried to send the datagram to a specific IP address (instead of broadcast) but it does not change: BBB seems to be not able to send UDP packets at all...

Any ideas about that? Is there something to be configured on BBB for letting this work?

* UPDATE *

I slightly modified the code for explicitly enabling SO_BROADCAST on that socket and to bind the socket to any IPv4 interface (just to test) but it does not work anyway...

Looking at process strace (you can see it here) it seems that the linux kernel fails to recognize 255.255.255.255 as the broadcast address and tells that the network is unreacheable...

Here is my network configuration... it seems good to me, but correct me it not!

Morix Dev
  • 2,700
  • 1
  • 28
  • 49
  • have you crosscompiled for bbb? – Luca Davanzo Jul 21 '14 at 13:57
  • @Velthune: yes, sure... – Morix Dev Jul 21 '14 at 13:59
  • uhm ok, of course, and have you same qt version both in pc and in bbb? – Luca Davanzo Jul 21 '14 at 14:13
  • @Velthune: yes, Qt 5.3 on both – Morix Dev Jul 21 '14 at 14:15
  • I've edited my post to provide some additional details... – Morix Dev Jul 21 '14 at 15:12
  • You probably need to use the broadcast address for the particular interface you're using. That it works on another machine is a happy coincidence, but not something I'd depend on. Bravo for posting a self-contained test case. – Kuba hasn't forgotten Monica Jul 21 '14 at 15:14
  • @KubaOber: can you detail exactly what you mean? – Morix Dev Jul 21 '14 at 15:15
  • 1
    @MorixDev The `ifconfig` output gives a `Bcast:` address for the interface in question. Use it. It's *not* 255.255.255.255. – Kuba hasn't forgotten Monica Jul 21 '14 at 15:15
  • 255.255.255.255 is net mask – Luca Davanzo Jul 21 '14 at 15:20
  • @Velthune: I am pretty sure that 255.255.255.255 is the universal broadcast address...if you look for example at QHostAddress documentation it says: QHostAddress::Broadcast The IPv4 broadcast address. Equivalent to QHostAddress("255.255.255.255")... isn't int? – Morix Dev Jul 21 '14 at 19:52
  • @KubaOber: but is it my only option? I mean: on Windows if I send a datagram to 255.255.255.255 int is automatically emitted on all network interfaces... isn't it possible on Linux? – Morix Dev Jul 21 '14 at 19:56
  • 1
    depend where do you read that number. Read his: http://en.wikipedia.org/wiki/IPv4_subnetting_reference and this: http://en.wikipedia.org/wiki/Broadcast_address . "A special definition exists for the IP broadcast address 255.255.255.255. It is the broadcast address of the zero network or 0.0.0.0, which in Internet Protocol standards stands for this network, i.e. the local network. Transmission to this address is limited by definition, in that it is never forwarded by the routers connecting the local network to other networks." – Luca Davanzo Jul 22 '14 at 06:47
  • @Velthune: in my context (looking at my post and the provided links) I think it was quite clear that 255.255.255.255 referred to the local network broadcast address and not to the subnet mask... but anyway thanks for your suggestion. – Morix Dev Jul 23 '14 at 06:13

1 Answers1

4

I can reproduce this issue on RHEL 6 running 2.6.32-431.20.3.el6.x86_64. Even though a bind to 0.0.0.0 succeeds, the subsequent writeDatagram fails. Things work perfectly well when you bind to a particular interface.

As an aside, your network interface is not properly configured, even though I can't see any difference in the behavior of the program due to this alone. The broadcast address on your eth0 should be 192.168.79.255, not 255.255.255.255.

#include <QCoreApplication>
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QDebug>

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   QUdpSocket socket;

   QList<QHostAddress> ifAddrs = QNetworkInterface::allAddresses();
   qDebug() << ifAddrs;

   QHostAddress ifAddr(QHostAddress::Any);
   foreach (QHostAddress ia, ifAddrs) {
      if (ia.protocol() == QAbstractSocket::IPv6Protocol) continue;
      if (ia.isInSubnet(QHostAddress::LocalHost, 8)) continue;
      ifAddr = ia;
      break;
   }
   if (false) ifAddr = QHostAddress::Any; // *** Change to if (true) to make the write fail.
   qDebug() << ifAddr;

   if (!socket.bind(ifAddr, 1111)) {
      qDebug() << "bind failed" << socket.error();
   }

   QByteArray d = QString("Hello, world!").toLatin1();
   int r = socket.writeDatagram(d, QHostAddress::Broadcast, 1111);

   qDebug() << r;
   if (r < 0) {
      qDebug() << socket.error();
      qDebug() << socket.errorString();
   }

   return 0;
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • you're right about the improper configuration of my network device... it was one of my attempts to make things work... normally the broadcast address on eth0 is 192.168.79.255, you're right... anyway: you are saying me that in linux I cannot send an UDP datagram to 255.255.255.255 as I normally do in Windows? This could be a huge limitation... If using Windows I send a datagram to 255.255.255.255 then it is received by *all* the devices in my local network segment... in Linux I can only reach devices having IP 192.168.79.x (in my case). Am I wrong? – Morix Dev Jul 22 '14 at 12:00
  • @MorixDev You're certainly sending the datagram **to** 255.255.255.255 (note the address passed to `writeDatagram`). You're just not sending it "out anywhere", it has to go out through a *particular* interface you bind to. Since it's trivial to enumerate all interfaces in Qt, it's equally trivial to send it out all interfaces if you explicitly wish to do so. Are you *really* intending to send it out via the loopback interface, for example? I'm also not quite certain whether Windows really sends it out of all interfaces, or chooses the one with default routing. You'd need to test it first. – Kuba hasn't forgotten Monica Jul 22 '14 at 12:43
  • @MorixDev In any case, the expectation that a broadcast to 255.255.255.255 will reach everything on the local network segment has **caveats**. Sure it may reach everything, but there are zero guarantees that a *reply* unicast packet will reach *you*. If you were sending to the interface's broadcast address, you can at least depend on the replies getting back to you if things work OK. Say I have a 10.0.0.1 node on the same segment, and no routing set up between 192.168/16 and 10/8. The 10.0.0.1 host's UDP packet sent to a host on 192.168/16 won't go anywhere, because it has to be routed there. – Kuba hasn't forgotten Monica Jul 22 '14 at 12:49
  • @MorixDev Concrete scenario: 1. Node A is attached to the network. DHCP server assigns 192.168.79.55 to it. 2. DHCP server goes down. 2. Node B is attached. Sans DHCP, it self-assigns 169.254.8.56. 3. Node A broadcasts to 255.255.255.255. 4. Node B receives the broadcast. 5. Node B sends a unicast reply to 192.168.79.55. 6. There's no 6. The reply won't go out of the B's interface, and won't reach node A. There are certainly ways to make node B act as "desired", but such desire fundamentally breaks the standard-mandated behavior of node B, so it wouldn't be clever at all. – Kuba hasn't forgotten Monica Jul 22 '14 at 12:52
  • @MorixDev You have to understand that **255.255.255.255** is an alias for local network broadcast address. So sending to this address **gives you no more functionality than sending to the interface's broadcast address**. You are expecting something that simply isn't there. Whether Windows gives you an error status back or not doesn't change how things actually work. – Kuba hasn't forgotten Monica Jul 22 '14 at 12:56
  • at first reading I did not fully understand your comments but now they are crystal clear to me... You are right that sending to 255.255.255.255 I have no guarantees that a reply reach my device, but in the scenario I am working even the counterpart will reply sending UDP packets to 255.255.255.255, so I should read them (I am speaking about a quite-stupid devices discovery protocol inherited by a project not handled by me)... – Morix Dev Jul 23 '14 at 06:03
  • anyway, binding my socket to a given address (let's say 192.168.79.13) has also some drawbacks: I am able to receive no more incoming packet on that socket, because (I think) if I bind to 192.168.79.13 then I'll receive only packets explicitly targeted to 192.168.79.13 (and not those targeted to 255.255.255.255 for example)... So I arranged to have two distinct `QUdpSocket`: one for receiving (bound to `QHostAddress::AnyIPv4` on port 1111) and one for transmitting (bound to `192.168.79.13` on whatever port) – Morix Dev Jul 23 '14 at 06:06
  • on the TX socket I send datagrams using `writeDatagram(QHostAddress::Broadcast)`, as shown by you and it works! Anyway the packets outgoing from my interface have a source port different than 1111 (they have the source port I bound the TX socket to)... so I think that this approach cannot be used if I would like to have a source port for outgoing packets equals to the destination port used for incoming packets... obviously I cannot bind both the TX and RX socket to the same port and here is the problem... – Morix Dev Jul 23 '14 at 06:08
  • actually I do not really need to do that... but what if in future I'd like to achieve this result? Any suggestions from your side? – Morix Dev Jul 23 '14 at 06:09
  • @MorixDev "if I bind to 192.168.79.13 then I'll receive only packets explicitly targeted to 192.168.79.13 (and not those targeted to 255.255.255.255 for example)" That's wrong. You will receive all broadcast UDP packets that target your bound port, reach your interface, and are not filtered out by the kernel's reverse routing filters. – Kuba hasn't forgotten Monica Jul 23 '14 at 07:04
  • @MorixDev The bound port only matters to select received packets where the *destination* port matches the port you declared (out of all packets). You can send pretty much whatever you want as long as it makes sense for the particular interface. – Kuba hasn't forgotten Monica Jul 23 '14 at 07:06
  • @MorixDev Your inherited device discovery protocol has serious scalability issues. If there are N devices on the network that support your protocol, then each of your broadcast queries generates N^2 aggregate traffic across the nodes, since every N devices transmit via broadcast to N hosts. Actually it can be worse than N^2 - it's actually N*M, where N is the number of devices with your protocol running, and M is the number of nodes on the network, where M>=N by definition. You can very easily saturate a network that way. Don't even think of deploying this to office PCs of any sort! – Kuba hasn't forgotten Monica Jul 23 '14 at 07:09
  • experimentally if I bind to 192.168.79.13 port 1111 then I can't receive packages targeted to 255.255.255.255 port 1111; instead if I bind to `QHostAddress::AnyIPv4` port 1111 then I get all that packets... – Morix Dev Jul 23 '14 at 07:31
  • I agree with you about the scalability issues of the protocol I've inherited... but (unluckily) I can't change it... :( – Morix Dev Jul 23 '14 at 07:32
  • @MorixDev As long as you can receive packets targeted to 192.168.79.255 (if it's a /24 network) then you're good. You'd need to use wireshark to look at what's really the target on those `255.255.255.255` packets that leave the sender's network interface. – Kuba hasn't forgotten Monica Jul 23 '14 at 14:44
  • I've checked: the target IP is 255.255.255.255 (and the port is right)... but I do not receive them on BBB if I bind to 192.168.79.13... :( – Morix Dev Jul 23 '14 at 15:10
  • @MorixDev Is ip routing enabled on the beaglebone? If so, the router might be dropping the packets using the reverse routing filter *iff* the packets are not sent from the same subnet. – Kuba hasn't forgotten Monica Jul 23 '14 at 17:39
  • it could be, but I don't know exactly... can you tell me what I have to check? or at least can you tell me a good resource for learning about ip routing on linux? – Morix Dev Jul 23 '14 at 20:03