mDNS Integration

struct np_mdns_functions

The Nabto Edge platform uses mDNS for discovery on the local network. That is, if a client needs to find a local device on the local network.

Some systems come with their own mDNS service, on these systems we recommend to use the system provided mDNS service. If a system does not provide an mDNS service, we have a generic service which is located in the src/modules/mdns folder.

The jobs of the mDNS discovery are:

  1. Inform a client about local devices with Nabto Edge.
  2. Tell the client at which ip and port the Nabto Edge service is running on the device.

Requirements for Nabto Edge devices to be discovered via mDNS

  1. Provide discovable Service Type Identifier of : _nabto._udp
  2. Provide Text record with two key/values pairs:
    • productid=<productId>
    • deviceid=<deviceId>

The mDNS Instance Name of the device is not important since it basically is used to identify the device on the local network. Once a Nabto Edge connection has been established the client will lookup the device and product id and forwardly use those to locate and connect to the device.

mDNS service discovery is built into the Nabto Edge platform through the np_mdns interface. This interface have one function.

void (*publish_service)(struct np_mdns* obj, uint16_t port, const char* productId, const char* deviceId);

This function is called a bit after the device has been started, since the device needs to have opened its local socket first and acquired the local port number of the local socket.

When this function is called, the mDNS implementation should register the service _nabto._udp as having the port port number on the device. Further, two key value pairs should be registered for the service productid=<productId> and deviceid=<deviceId>.

Testing

Several mDNS clients exist which can be used to test that an mDNS implementation works. On Windows and Mac, it is recommended to use the dns-sd tool which comes from bonjour.

On Windows this sdk can be downloaded from https://developer.apple.com/bonjour/.

On Mac, the dns-sd tool is installed by default.

On Linux the avahi-tools package can be used, it comes with the avahi-browse command (avahi-browse -a -r).

Example scan for all nabto edge devices on the local network:

C:\>dns-sd -B _nabto._udp
Browsing for _nabto._udp
Timestamp     A/R Flags if Domain                    Service Type              Instance Name
 4:22:42.514  Add     2  4 local.                    _nabto._udp.              de-nhbpwxcx

The example shows information about a specific device, the important information is the txt records and the port number.

C:\>dns-sd -L de-nhbpwxcx _nabto._udp
Lookup de-nhbpwxcx._nabto._udp.local
 4:23:48.795  de-nhbpwxcx._nabto._udp.local. can be reached at de-nhbpwxcx.local.:49654 (interface 4)
 deviceId=de-nhbpwxcx productId=pr-bqyh43fb

Example: Get the ips for a device using dns-sd:

C:\>dns-sd -G v4v6 de-nhbpwxcx.local.
Timestamp     A/R Flags if Hostname                  Address                                      TTL
 4:33:27.514  Add     3  4 de-nhbpwxcx.local.        192.168.123.117                              120
 4:33:27.514  Add     2  4 de-nhbpwxcx.local.        2001:DB8:1234:0003:8D41:1F5C:3C08:2D3F%<0>   120

Using this information we know that the device with the product id pr-bqyh43fb and the device id de-nhbpwxcx can be reached on the ips 192.168.123.117 and 2001:DB8:1234:0003:8D41:1F5C:3C08:2D3F on UDP port 49654

mDNS example implementation

The ESP32 comes with an mDNS server, https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mdns.html.

Assuming the mDNS server is started acording to the documentation, then the mDNS integration is as follows.

static struct np_mdns_functions vtable = {
    .publish_service = &publish_service
};

struct np_mdns esp32_mdns_get_impl()
{
    struct np_mdns obj;
    obj.vptr = &vtable;
    obj.data = NULL;
    return obj;
}

void publish_service(struct np_mdns* obj, uint16_t port, const char* productId, const char* deviceId)
{
    mdns_txt_item_t serviceTxtData[2] = {
        {"productid",productId},
        {"deviceid",deviceId}
    };

    mdns_service_add(NULL, "_nabto", "_udp", port, serviceTxtData, 2);
}