UDP Broadcasting And Easily Finding Network Services

Local area networks (LANs) that use technologies like Ethernet and Wi-Fi are incredibly useful for letting devices talk with each other. Yet a core problem here is knowing which devices are where on the network, as anyone who has ever tried to add a network printer or network share to their system can probably attest to. Unless you happen to know the IP address of the LAN device, the port, and protocol, the target device may as well be located on the Moon without further help, such as automatic network discovery in lieu of waddling over to the device and reading the label listing its IP address.

Over the decades quite a few ways have been developed to enable such network discovery, with many of them using UDP broadcast as the first step. By broadcasting a global message on the entire LAN, any device that has an actively listening UDP socket on that particular port can parse said message and decide whether it’s feeling sociable enough to reply.

The topic of UDP broadcasting is however not as straightforward as it may sound if you’re just getting started, including the existence of many opinions on the ‘right way’. There is also a massive divide between a sprawling service discovery protocol like mDNS and a light-weight one like that one that I had to implement a few years ago for an open source project.

Network Broadcasting

The obvious advantage of a broadcast message is that a client device that seeks its protocol soul mate on the LAN doesn’t need to ping all possible IP address and subnets. Instead,  a broadcast message is designed so that all connected networking devices know that it should be forwarded to all other known devices. Thus with a single message from the client, in theory, only a single message will then neatly land at every single other connected system.

Of course, this ignores happy joy fun things such as convoluted network configurations, such as those involving overlapping Wi-Fi repeaters and subsequent routing, but in general we can assume that this is how it works. Various edge cases and fascinating complications of these will be considered in a later section.

(Credit: Xkcd)

Much of this service auto-discovery is tossed under the header of ‘zero-configuration networking‘, or zeroconf for people who don’t like typing. The best part about zeroconf is probably that there are so many standards here, ranging from DNS-SD to mDNS, UPnP, SLP and others. Perhaps unsurprisingly, one of the major issues here is that platform support here is spotty, with mDNS – despite being one of the most universal – not having much support outside of MacOS/OS X with Bonjour and Linux/BSD with Avahi.

Thus while trying to add the auto-discovery of NymphCast receivers and media servers by NymphCast clients, I found myself asking the daunting question of whether I was at risk of being about to embark on reinventing the proverbial wheel. After all, nobody wants to become the subject of an xkcd comic.

UDP Discovery Basics

As it turns out, I ought not to have been too worried, as despite looking everywhere I could find nothing along the lines of the NyanSD network service discovery (NSD) protocol that I ended up implementing and integrating into NymphCast. What I wanted after all was the most no-frills NSD possible that could be easily integrated, while working the same across just about any desktop, server and embedded platform imaginable.

All that’s needed for this is a way to create an appropriate UDP socket, and a way to either broadcast a query and receive the response, or to listen for incoming UDP packets. Here you can figure out the platform-native method for each target platform, or not reinvent the wheel and use an existing networking library for C++ like Poco. This is what I used for NyanSD, along with my ByteBauble utility to handle endianness conversions.

For the UDP server — the listening side — the procedure is fairly standard, with a regular UDP listening socket. As UDP is a connectionless protocol, there is not a lot of preamble here, just a UDP socket instance (here Poco::Net::DatagramSocket), which is bound to the target port and regularly polls for any fresh UDP packets to process. This can all be seen in the single source file for NyanSD which covers both the client and server side code.

Where things get spicy is with the client that sends the broadcast query and waits for any replies. If we were to just shove the query data into the socket along with the request to toss it over to a regular IP address, not a lot would happen. To make it into a broadcast request we need a few things:

  1. Let the network subsystem know that we want to do broadcast things.
  2. Create the special broadcast address for the target network interface.

With Poco the first point is easily handled by simply calling setBroadcast(true) on the UDP socket instance. For BSD sockets this sets the appropriate flag on the socket, which is essentially repeated across all OS implementations due to how prevalent the BSD socket library is.

The second point can be summarized for IPv4 as a curt ‘make it end with .255’. For example 192.168.0.255 when the client network interface’s IP address is 192.168.0.42. If there are multiple interfaces on the client system, you can go through the list one by one to broadcast on each of them before filtering out potential duplicate returns.

As for how to do broadcasting with IPv6: you don’t, as this protocol relies on multicast and special multicast receiver groups, which is another kettle of fish and of not much relevance for LANs.

Complications

If you look at the NyanSD API, it may give the impression that the query process is incredibly straightforward, with the sendQuery() function neatly returning a stack of remote systems that responded to our query. While these are definitely all the responses, it’s important to remember that NyanSD queries every single network interface. This means that the responses are likely to contain duplicates, which may even come from the loopback address when a service runs locally.

The filtering of this is captured in the NymphCast client library (libnymphcast) where the findServers() function in the main source file calls the isDuplicate() and isDuplicateName() functions, as well as the removeLoopback() function that nukes any responses that match a remote service found via a non-loopback interface. This last filtering is essential for NymphCast when e.g. using playback groups that would otherwise get confused by a stray loopback address.

Although one may think that such in-depth filtering is unnecessary if all you have is a single Wi-Fi or Ethernet interface in your system, one of the curveballs that I encountered during real-life testing was apparently related to Wi-Fi repeaters. For some reason it seems that the way that the repeaters did their broadcasting led to erroneous duplication of packets and thus multiple returns from a single system.

Depending on your exact use case and network configuration you may encounter any such issues and perhaps an exciting new one.

NyanSD Findings

Over the years that NyanSD has been used in the NymphCast project, it has proven to be one of the most reliable and probably nearly zero-fuss components. I have so far used it on Windows, various Linux distributions, FreeBSD, Haiku, Android, and the ESP32 via FreeRTOS and ESP-IDF. What this experience has proven to me most of all is that service discovery doesn’t have to be complicated.

The basic UDP protocol is simple and reliable enough that, barring a very sick LAN, there shouldn’t be any issues here. Assuming you get your filtering sorted of the responses, it’s probably the last part of a project to worry about.

One thing that I’m also very happy with in NyanSD is that there’s no set port in the protocol, like how mDNS always uses port 5353. What this means is that I can have NyanSD listen with a UDP socket on the same port as the NymphCast server’s TCP socket, which also means that different services with their own port can be targeted directly rather than every NyanSD-enabled service on the network getting blasted by every NyanSD query.

I did also do some work on a NyanSD daemon as a more central services database, but so far I have had no real need for it in a practical deployment. I guess that such a thing could be very useful if the port of a service is not set in stone, but generally that’s the one aspect of network services that tends to be boringly predictable.

25 thoughts on “UDP Broadcasting And Easily Finding Network Services

      1. That is not how accreditation works.
        There will ALWAYS be people who have never seen/heard of something.

        And it’s right in the license Randall provides. Your opinion on redundancy doesn’t matter when the originator requires it.

    1. That is not how accreditation works.
      There will ALWAYS be people who have never seen/heard of something.

      And it’s right in the license Randall provides. Your opinion on redundancy doesn’t matter when the originator requires it.

  1. Please tell me you didn’t actually restrict yourself to networks with /24 subnets by hardcoding this “make it end with .255” nonsense into your code….

      1. But it works on my machine/network! I jest I jest, a little. I also wonder how well it works with IPv6 …

        But to be fair, its again a NiH thing. UDP broadcast is very unreliable very custom. Even when it works for the author. Example Ingot some tapo cameras that do broadcasting on 255.255.255.255, which works, on single networks. Add a second Ethernet, and shitbfalls apart. All fixable/work aroundable, but no Kore out of the box ‘it just wotrks’.

        mDNS-SD works quite well and is sort of becoming the defacto standard. Just open a avahi-discover and see what’s already using it on your network.

        Granted, there’s one big caveat, zeroconf doesn’t cross networks well, unless you have an ahavi repeater to bridge networks.

        Good luck to our author however! Its doing and hacking that makes us all learn and improve!

      1. I agree with others that “make it end in .255 is completely wrong, it presupposes that every network is a /24 or at the high end of as smaller subnet, which is not always going to be the case. The broadcast address for any IPv4 network is the last address in its address range, it’s that simple.

        If your network is 192.168.0/24, it’s 192.168.0.255. if your network is the /25 that’s in the upper half of that space, 192.168.0.128/25, then your broadcast address is ALSO 192.168.0.255, luckily. But if your network is the LOWER half of that space, 192.168.0.0/25, then your broadcast address is 192.168.0.127. 192.168.0.255 isn’t even in the same subnet!

        But what I actually wanted to respond to was this:

        One thing that I’m also very happy with in NyanSD is that there’s no set port in the protocol, like how mDNS always uses port 5353.

        That’s a great decision. One of the biggest pain points in KDE Connect is their selection of a single dedicated network port for device discovery and pairing.

        A fixed port means that you can’t run multiple or separate instances of the service on the same machine, which isn’t a problem for dedicated devices but can be painful for user-land protocols like kdeconnect on shared systems. To this day, there’s no way for two different users to simultaneously use KDE Connect on the same host, as only one of them can be listening on port 1516.

    1. I’d love to get more information on what you’re referring to here, as the aforementioned post-fixing was the only method that I came across my research at the time that actually seems to work…

      Just saying ‘you did it wrong’ isn’t going help me much here :)

      1. There’s Poco::Net::NetworkInterface::broadcastAddress(). Or if you feel like doing it a more convoluted way you could invoke ip.mask(ifc.subnetMask(), Poco::Net::IPAddress::broadcast()). Modifying string representation of an IP address is virtually always a code smell.

        1. Thank you for that, I’ll see about changing it as soon as possible.

          In my defence, at least when I was doing the research on the topic it was exceedingly hard to find solid information on the topic. Maybe more RTFD would have been in order.

          1. One thing to consider, it’s worth doodling out on a whiteboard how your code would handle /22, /27, /31, and even /32 (no broadcast here: explicit host routing: if my interface is 10.1.2.3 and yours is 10.2.7.93, a one-ip route is formed, and ARP is bypassed).

            Most /31 networks exist only because someone doesn’t understand /32, or because /32 is improperly implemented in the network stack. (and despite the common name, it’s not merely for two-party point-to-point usage).

            For your broadcast usage, it may be mostly irrelevant, but not entirely: some embedded devices and chipsets function in the always-route-everything mode no matter what netmask they’re given; they differentiate between layer 2 media broadast (necessary to get a dhcp lease, for example) and layer 3 ip broadcast (which they don’t understand, or explicitly ignore for security or code-minimization reasons).

            It’s not that /32 is special-cased. It’s the other way around :)

            Direct-routing -> classful routing(255.255.255.0; each octet either 00 or FF) -> classless routing(/24, mask width is bounded 0..32).

            Subnetting was added to collapse the need to have one route entry for each peer, but since it’s so useful, it became the common case. The original direct-route logic became the weird one, and since it’s common to design for the general case, /32 doesn’t even work in some environments (personal experience: several Microsoft HyperV commercial deployments(conflicts with their assumptions) vs a very large-scale linux-and-bsd fleet(just worked) ).

            If you need to host a large number of endpoints, it’s often more efficient use of your ipspace to use /32 routes rather than wasting most of your ips by subnetting. Doesn’t matter much in homelabs or private networks, but if you’re working with direct real-ip pools, a few thousand IPs are really expensive these days.

  2. For basically aesthetic reasons, i don’t like auto-discovery of services…it seems like a security problem and a configuration problem all in one. Whenever i notice avahi running on a new installation, i remove/disable it. I don’t want things to be in some squishy state in between unconfigured and configured. I don’t want different devices talking to eachother without me telling them to or even knowing about it.

    The only time it’s actually bit me…i worked in an office with two ‘smart switches’ (routers, i guess) that we used as ethernet hubs. We only really needed one of them, the second was just redundancy. I was a software contractor there and there was another contractor that was responsible for setting up the network. For some reason, i needed to use the second switch, and i was told that the original contractor couldn’t get both switches to work at once. And indeed i turned on the second switch, and the whole office went down. I eventually made it into the configuration pane for the routers and found they were both configured to advertise and receive their routing configuration over the same protocol (not DHCP, some router-specific thing), so if both were on then they’d form a tight loop between them and forget about their upstream feed.

    I was particularly frustrated that they spoke about half a dozen different discovery protocols, and obviously the kind of consultant that selects them out of a catalog had no idea about it.

    So i just use DHCP as the one UDP broadcast discover to rule them all and in the darkness bind them. Can’t seem to escape it :) At least i understand it!

    1. Sadly I run into similar issues all the time and for myself it comes from idiots selling products as a service cameras/phones/so called networking experts(primarily ubiquiti/unifi garbage). For me anyway Ubiquiti and Phone “specialists” are the biggest headaches. They have no clue about anything, zero knowledge of standards and if you ever mention the OSI architecture they look at you like a deer in headlights. They come in destroy a network and leave then blame everyone else for their absolute stupidity.

      1. I’m not a networking guy. I’ve never implemented any Ubiquiti equipment but have seen a fair amount in enterprise level environments which all seemed to be doing what they were meant to do with no real issues.

        For future reference, what have you experienced that compels you to classify Ubiquiti as garbage? (Asking, not questioning).

        1. FWIW, a brief review…i installed a ubiquiti UFO (“unifi AP AC PRO”) in my house because my old wifi was flakey, and i figured i would try a reputable brand for a change. Like you say, a large institution near me used ubiquiti all over so i figured it was worth a bet. i was vexed by the fact that it required an android app to set it up, but once it was configured it has been trouble-free for years now. And i’ve since learned it’s pretty easy to get openwrt onto it, so really it’s great! So i’m happy with it, but probably i won’t buy one again.

          But i think gambler’s complaint is with the installers, not the device itself. Every company these days winds up with an ecosystem of technicians around it and i don’t know what that’s like for like a small office to hire someone to install ubiquiti..

        2. as greg has stated and I did put too much weight on their hardware that the installers are my main issue. They have a product that they do market and try and say anyone can install and manage it. That might be true but at that point it becomes worthless to me as an example a situation i’m in right now is i was brought in to clean up a large point of sale mess going on. The issues being thrown by the POS system are network errors. The company outsourced their networking to a unifi group. I requested logs, their response we don’t do logging figure it out yourself. I had to contact the freaking president of company to get them to forward logs to a syslog server I put in so i can start to see what is going on. After I put it in then the fun started as the unifi idiots had to ask me how to enable logging on their equipment and how to forward it. This is the type of competence I see daily with both unifi and phone installers.

          As for ubiquiti itself it is OK hardware and is fairly stable. I do have issues with them using non standards like when they introduced wifi handoff into their ecosystem, they 802.11r (i think) was pretty much ignored completely and they did their own thing. I also had a horrible time when they initially pushed their POE standard that did not work with anything else. So I do have issues with the company but the installers are the main idiots because they are idiots

  3. As for how to do broadcasting with IPv6: you don’t, as this protocol relies on multicast and special multicast receiver groups, which is another kettle of fish and of not much relevance for LANs.

    I find this a bit confusing. Multicast is of not much relevance outside LANs and it’s mostly used in local networks, both in IPv4 and IPv6. It’s superior to broadcast in many ways, that’s why protocols such as mDNS and LLMNR are based on it.

  4. I’ve BTDT for a shared clipboard in a textfile reader which started off on 16-bit Windows and continued to 32- and 64-bit Linux and Solaris on various processors. It worked well but routing between network segments was an obvious issue since UDP broadcasts normally don’t propagate, I had to do a fair amount of fiddling around to get subnet masks etc. correct (I didn’t take the easy way out and assume that everything was /24) but in some cases (notably VM hosts) I had to use a UDP proxy.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.