libjuice 🍋💦 (JUICE is a UDP Interactive Connectivity Establishment library) allows to open bidirectionnal User Datagram Protocol (UDP) streams with Network Address Translator (NAT) traversal.
The library is a simplified implementation of the Interactive Connectivity Establishment (ICE) protocol, client-side and server-side, written in C without dependencies for multiple platforms, including GNU/Linux, Android, FreeBSD, Apple macOS, iOS, and Microsoft Windows. The client supports only a single component over UDP per session in a standard single-gateway network topology, as this should be sufficient for the majority of use cases nowadays.
libjuice is licensed under MPL 2.0, see LICENSE.
libjuice is available on AUR and vcpkg. Bindings are available for Rust.
For a STUN/TURN server application based on libjuice, see Violet.
The library implements a simplified full ICE agent (RFC5245 then RFC8445) featuring:
- STUN protocol (RFC5389 then RFC8489)
- TURN relaying (RFC5766 then RFC8656)
- TCP candidates (RFC6544)
- ICE Consent freshness (RFC7675)
- ICE Patiently Awaiting Connectivity (RFC 8863)
- SDP-based interface (RFC8839)
- IPv4 and IPv6 dual-stack support
- Optional multiplexing on a single UDP port
The limitations compared to a fully-featured ICE agent are:
- Only UDP is supported as transport protocol and other protocols are ignored.
- Only one component is supported, which is sufficient for WebRTC Data Channels and multiplexed RTP+RTCP.
- Only RFC8828 mode 2 is supported (default route + all local addresses).
It also implements a lightweight STUN/TURN server (RFC8489 and RFC8656). The server can be disabled at compile-time with the NO_SERVER flag.
None!
Optionally, Nettle can provide SHA1 and SHA256 algorithms instead of the internal implementation. On ESP-IDF, the built-in mbedtls is used automatically.
The CMakeLists.txt uses if(ESP_PLATFORM) to auto-detect the build environment. When built by ESP-IDF, it registers as an IDF component; otherwise, it behaves as a standard CMake project for desktop platforms. No manual switching is needed.
This library is ported as an ESP-IDF component supporting ESP32, ESP32-S2, ESP32-S3, ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2, etc. Tested on ESP-IDF v5.5 and v6.0.
cd examples/connectivity
idf.py set-target esp32c3 # or esp32c5, esp32s3, etc.
idf.py menuconfig # configure WiFi credentials under "Example Connection Configuration"
idf.py build flash monitorAdd libjuice to your project's components/ directory, or reference it via idf_component.yml. The component auto-registers with idf_component_register() and provides the juice/juice.h public header.
# In your main component CMakeLists.txt
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES libjuice)- Crypto: Uses ESP-IDF's mbedtls instead of the internal picohash implementation
- RNG: Uses hardware
esp_fill_random()for cryptographically secure random numbers - Threading: Uses FreeRTOS tasks (not pthreads) with configurable stack sizes
- Networking: Uses
esp_netifAPI for interface enumeration; providesgetnameinfo()compatibility shim for lwIP - Symbol namespacing: Internal UDP/HMAC/hash symbols are prefixed to avoid conflicts with lwIP and wpa_supplicant
Run idf.py menuconfig and navigate to libjuice to tune memory usage:
| Parameter | Default | Range | Description |
|---|---|---|---|
JUICE_ICE_MAX_CANDIDATES |
8 | 4-20 | Max ICE candidates per description (~220B each) |
JUICE_TASK_STACK_SIZE |
6144 | 4096-16384 | Internal FreeRTOS task stack size |
JUICE_MAX_STUN_SERVERS |
1 | 1-4 | Max STUN server entries |
JUICE_MAX_RELAY_SERVERS |
1 | 1-4 | Max TURN relay entries |
JUICE_STUN_MAX_CREDENTIAL_LEN |
128 | 64-764 | STUN credential field max length |
JUICE_BUFFER_SIZE |
1500 | 1024-4096 | Network send/receive buffer size |
JUICE_LOG_BUFFER_SIZE |
256 | 128-4096 | Log formatting buffer size |
JUICE_ENABLE_SERVER |
off | on/off | ICE/TURN server support |
With default settings, each ICE agent uses approximately 18-22KB of heap memory.
$ git clone https://github.com/paullouisageneau/libjuice.git
$ cd libjuiceThe CMake library targets libjuice and libjuice-static respectively correspond to the shared and static libraries. The default target will build the library and tests. It exports the targets with namespace LibJuice::LibJuice and LibJuice::LibJuiceStatic to link the library from another CMake project.
$ cmake -B build
$ cd build
$ make -j2The option USE_NETTLE allows to use the Nettle library instead of the internal implementation for HMAC-SHA1:
$ cmake -B build -DUSE_NETTLE=1
$ cd build
$ make -j2$ cmake -B build -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file
$ cd build
$ make -j2$ cmake -B build -G "NMake Makefiles"
$ cd build
$ nmake$ makeThe option USE_NETTLE allows to use the Nettle library instead of the internal implementation for HMAC-SHA1:
$ make USE_NETTLE=1See examples/connectivity/ for an ESP-IDF connectivity example.
See test/connectivity.c for a complete local connection example (desktop).
See test/server.c for a server example.