xIceAgent is the central component of the xp2p module. It implements the full ICE (Interactive Connectivity Establishment) protocol as defined in RFC 8445 , providing NAT traversal and peer-to-peer UDP connectivity.
The agent handles:
Candidate gathering — Enumerates local network interfaces (host candidates), queries STUN servers (server-reflexive candidates), and optionally allocates TURN relays (relay candidates).
Connectivity checks — Performs STUN Binding request/response exchanges on all candidate pairs to find working paths.
Nomination — Selects the best candidate pair for data transport (aggressive nomination in controlling mode).
Data transport — Sends and receives application data over the nominated pair, with TURN relay fallback via ChannelData framing.
Consent freshness — Periodically verifies the peer is still reachable (RFC 7675).
#include <xp2p/ice_agent.h>
The ICE agent progresses through the following states:
New → Gathering → Checking → Connected → Completed
↘ ↗
Failed
↓
Closed
State Value Description
xIceState_New0 Initial state, no activity yet
xIceState_Gathering1 Gathering local candidates (host / srflx / relay)
xIceState_Checking2 Performing connectivity checks on candidate pairs
xIceState_Connected3 At least one valid pair found
xIceState_Completed4 All checks done, nominated pair selected
xIceState_Failed5 All checks failed, no valid pair
xIceState_Closed6 Agent has been shut down
Role Value Description
xIceRole_Controlling0 Initiates nomination (sends USE-CANDIDATE)
xIceRole_Controlled1 Accepts nomination from the controlling agent
struct xIceConf {
xIceRole role; // Controlling or Controlled
bool enable_ipv6; // Enable IPv6 candidates (default: false)
const char *stun_server; // STUN server "host:port" (or NULL)
const char *turn_server; // TURN server "host:port" (or NULL)
const char *turn_username; // TURN long-term credential username
const char *turn_password; // TURN long-term credential password
xIceOnStateChange on_state_change; // State change callback
xIceOnCandidate on_candidate; // New candidate callback
xIceOnData on_data; // Data received callback
void *ctx; // Forwarded to all callbacks
};
typedef void (*xIceOnStateChange)(xIceAgent agent, xIceState state, void *arg);
Called when the agent transitions to a new state. Use this to detect when the connection is established (Connected / Completed) or has failed.
typedef void (*xIceOnCandidate)(xIceAgent agent, const char *candidate_sdp, void *arg);
Called when a new local candidate is gathered. The candidate_sdp is an SDP candidate line (e.g. "candidate:...") suitable for Trickle ICE. When candidate_sdp is NULL, gathering is complete (end-of-candidates signal).
typedef void (*xIceOnData)(xIceAgent agent, const uint8_t *data, size_t len, void *arg);
Called when application data is received on the nominated pair. The data buffer is valid only for the duration of the callback.
Function Description
xIceAgentCreate(loop, conf)Create a new ICE agent. Generates random ice-ufrag/ice-pwd. Returns NULL on failure.
xIceAgentDestroy(agent)Destroy the agent, close sockets, cancel timers. Safe to call with NULL.
Function Description
xIceAgentGather(agent)Start candidate gathering. Enumerates interfaces, sends STUN/TURN requests. Candidates reported via on_candidate.
Function Description
xIceAgentCreateOffer(agent)Generate an SDP offer string. Caller must free() the result.
xIceAgentCreateAnswer(agent)Generate an SDP answer string. Caller must free() the result.
xIceAgentSetRemoteDescription(agent, sdp)Parse remote SDP (ice-ufrag, ice-pwd, candidates) and start connectivity checks.
xIceAgentAddRemoteCandidate(agent, sdp)Add a single remote candidate (Trickle ICE).
Function Description
xIceAgentSend(agent, data, len)Send data through the nominated pair. Only valid in Connected or Completed state.
Type Priority Pref Description
host126 Direct local interface address
srflx100 Server-reflexive (public address from STUN)
prflx110 Peer-reflexive (discovered during checks)
relay0 TURN relay address
Priority is computed per RFC 8445 §5.1.2.1:
priority = (2^24) × type_pref + (2^8) × local_pref + (256 - component_id)
sequenceDiagram
participant App as Application
participant A as Agent A (Controlling)
participant B as Agent B (Controlled)
participant STUN as STUN Server
App->>A: xIceAgentCreate(loop, conf)
App->>B: xIceAgentCreate(loop, conf)
App->>A: xIceAgentGather()
App->>B: xIceAgentGather()
A->>STUN: STUN Binding Request
B->>STUN: STUN Binding Request
STUN-->>A: Binding Response (srflx addr)
STUN-->>B: Binding Response (srflx addr)
A-->>App: on_candidate(host), on_candidate(srflx), on_candidate(NULL)
B-->>App: on_candidate(host), on_candidate(srflx), on_candidate(NULL)
App->>A: offer = xIceAgentCreateOffer()
App->>B: xIceAgentSetRemoteDescription(offer)
App->>B: answer = xIceAgentCreateAnswer()
App->>A: xIceAgentSetRemoteDescription(answer)
A->>B: STUN Binding Request (connectivity check)
B-->>A: Binding Response
A->>B: STUN Binding Request + USE-CANDIDATE
A-->>App: on_state_change(Connected)
B-->>App: on_state_change(Connected)
App->>A: xIceAgentSend("Hello!")
A->>B: UDP data
B-->>App: on_data("Hello!")
The examples/ice_echo.c demo creates two agents in the same process, exchanges SDP, and echoes data:
# Default (host candidates only, no STUN)
./build/ice_echo
# With STUN server
./build/ice_echo -s stun.l.google.com:19302
# Filter to only use server-reflexive candidates
./build/ice_echo -s stun.l.google.com:19302 -f srflx
# Enable IPv6 candidate gathering
./build/ice_echo -6
Flag Description
-s host:portSTUN server address (default: stun.l.google.com:19302). Pass -s "" to disable.
-f typeFilter candidates by type (host, srflx, relay). Default: keep all.
-6Enable IPv6 candidate gathering (disabled by default).
Constant Value Description
XICE_GATHER_TIMEOUT_MS5000 Candidate gathering timeout
XICE_CHECK_TIMEOUT_MS10000 Connectivity check timeout
XICE_CHECK_PACING_MS50 Check pacing interval
XICE_CONSENT_INTERVAL_MS15000 Consent freshness interval (RFC 7675)
XICE_MAX_CANDIDATES32 Max candidates per agent
XICE_MAX_PAIRS128 Max candidate pairs
XSTUN_INITIAL_RTO_MS500 Initial STUN retransmission timeout
XSTUN_MAX_RETRANSMITS7 Max STUN retransmissions