At Golioth, we pride ourselves on doing both the deep technical work—as well as the routine maintenance—required to ensure we back up our claim of being the most efficient way that devices in the field can securely talk to services in the cloud.
In service of this mission, last week we turned on DTLS 1.2 Connection ID support in Golioth Cloud. While most users will not see any visible differences in how their devices connect to Golioth, behind the scenes many devices are now enabled to perform more efficiently. This is especially true for those connecting via cellular networks.
Devices using Connection ID can see a meaningful impact on bandwidth costs, power usage, and battery life. This not only benefits users of Golioth, but also means that more devices are able to operate for longer, reducing waste and energy use. Because we believe deeply in this potential widespread impact, we have been and will continue to do all work in this area in the open.
What are Connection IDs?
We have written previously about how we use CoAP over DTLS / UDP to establish secure connections between Golioth and devices. Because UDP is a connection-less protocol, attributes of each datagram that arrive at a local endpoint must be used to identify the remote endpoint on the other side.
In short, DTLS uses the IP address and port of the remote endpoint to identify connections. This is a reasonable mechanism, but some devices change IP address and port frequently, resulting in the need to constantly perform handshakes. In some scenarios, a full handshake may be required just to send a single packet. Performing these handshakes can have a negative impact on battery life, drive up bandwidth costs, and increase communication latency. For some devices, this cost makes the end product at best more expensive, and at worst, infeasible.
Connection IDs provide an alternative solution, allowing for clients and servers to negotiate an identifier that can be used in lieu of the IP address and port. Once negotiated, the endpoint(s) that are sending a Connection ID — typically just the client but sometimes both the client and the server — are still able to have their DTLS connection state associated with incoming records when their IP address and port changes. The result is that a device could travel from one side of the world to the other, continuing to communicate with the same server, while only performing the single initial handshake.
How was Connection ID support implemented?
Efficient communication between devices and the cloud requires compatible software on both sides of the connection.
On the cloud side, we are beneficiaries of the work done by folks in the Pion community, which includes a set of libraries for real-time web-based communication. Conventional use of these libraries is enabling video streaming applications on the internet, such as Twitch. However, the protocols they implement are useful in many constrained environments where the network is lossy or unreliable.
The Golioth team contributed Connection ID support in the
pion/dtls library. This consisted of both the implementation of Connection ID extension handling and modifications to UDP datagram routing. The former involved careful updates to the parsing of DTLS records; Connection IDs change record headers from being fixed length to variable length. As part of this work, we are also adding a very small new API in the Golang
pion/dtls utilized an underlying
net.Listener, which was conventionally supplied by the
pion/transport library. This UDP
net.Conn connections to the
pion/dtls listener, which would in turn establish a DTLS connection by performing a handshake with the client on the other side of the connection. However, the
net.Conn interface does not allow for consumers to change the remote IP address and port to which packets are sent. When not using Connection IDs, this is not an issue because the IP address and port are what identifies the connection. However, when Connection IDs are in use, the ID itself is used to identify the connection, and the remote IP address and port may change over time. Thus, a new interface,
net.PacketListener, was added to
pion/dtls, which enables the changing of the remote address of a connection, and an implementation of the interface that routes based on Connection IDs when present was supplied.
On the device side, most users leverage the Golioth Firmware SDK to communicate with Golioth services, such as OTA, Settings, Stream, and more. The SDK is meant to work with any hardware, which is why we integrate with platforms such as Zephyr, Espressif ESP-IDF, Infineon Modus Toolbox, and Linux. Many of these platforms utilize DTLS support offered by
mbedTLS, which added support for the IETF draft of Connection IDs in 2019, then included official support in the 3.3.0 release in 2022. The SDK uses
libcoap, which implements CoAP support on top of a variety of DTLS implementations, including
libcoap started consuming
mbedTLS’s Connection ID API in July of this year. We have been assisting in ensuring that new versions of
libcoap are able to build on the platforms with which we integrate.
However, these platforms frequently maintain their own forks of dependencies in order to integrate with the rest of their ecosystem. We have both been contributing and supporting others contributions wherever possible in order to expedite the use of Connection IDs in the Golioth Firmware SDK. With Connection ID support already in place in ESP-IDF and Nordic’s
sdk-nrf, and coming soon in the next Zephyr release, we hope to turn them on by default for all platforms in upcoming Golioth Firmware SDK releases.
How do I use Connection IDs?
Using the Golioth Firmware SDK is the only requirement to utilize Connection IDs. As support is added across all embedded platforms, update to the latest version in your device side code and your connection will automatically be enabled. The video at the top of this post shows how you can inspect your own device traffic to see the functionality in action.
What comes next?
In total, we have made contributions across many open source projects as part of our effort to make Connection IDs widely available, and we look forward to continuing to lend a hand wherever possible. While this post has provided a brief overview of DTLS, Connection IDs, and the ecosystem of libraries we integrate with, ongoing use of the functionality will allow us to provide tangible measures of its impact. We’ll make sure to make announcements as support continues to be added across platforms and consumed in the Golioth Firmware SDK.
Until then, go create a Golioth account and start connecting your devices to the cloud!