Hot path for relayed TURN media (client to peer and hairpin).
What problem this solves
After a TURN allocation exists, most traffic is opaque media payloads, not STUN requests. Parsing every datagram fully would add latency. The data plane classifies frames with a slim header scan, checks permissions and channel bindings, applies optional plugins, and forwards UDP without entering the full control pipeline.
When the peer is another allocation on the same server, packets hairpin back
through ingress and to_client/4 instead of leaving the host.
Internal note
Invoked from relay ingress and socket handlers; not an operator-facing API.
RFCs
Summary
Types
Data-plane socket metadata (meta): client/server endpoints, transport, socket,
and :is_control.
Functions
Classifies a relayed frame without full STUN decode.
Forwards ChannelData from the client to the bound peer.
Forwards a Send indication from the client to the peer.
Encodes peer -> client traffic as ChannelData or a Data indication.
Types
Functions
@spec classify(binary()) :: {:channel, non_neg_integer(), binary()} | {:send, {term(), non_neg_integer()}, binary()} | :control | :ignore
Classifies a relayed frame without full STUN decode.
Returns {:channel, number, payload}, {:send, peer, payload}, :control,
or :ignore.
Examples
iex> Xirsys.XTurn.DataPlane.classify(<<0x4000::16, 4::16, "ping">>)
{:channel, 16384, "ping"}
iex> alias XMediaLib.Stun
iex> frame = Stun.encode(%Stun{
...> class: :request,
...> method: :binding,
...> transactionid: 1,
...> fingerprint: false,
...> attrs: %{}
...> })
iex> Xirsys.XTurn.DataPlane.classify(frame)
:control
iex> Xirsys.XTurn.DataPlane.classify(<<0xFF, 0xFF, 0x00>>)
:ignore
@spec forward_channel(binary(), meta()) :: :ok | :not_found | :drop
@spec forward_channel({:channel, non_neg_integer(), binary()}, meta()) :: :ok | :not_found | :drop
Forwards ChannelData from the client to the bound peer.
Accepts a raw frame (calls classify/1) or a pre-classified
{:channel, number, payload} tuple. Returns :ok, :not_found when the
channel is unbound, or :drop on control connections / plugin rejection.
@spec forward_send(binary(), meta()) :: :ok | :not_found | :drop
@spec forward_send({:send, {term(), non_neg_integer()}, binary()}, meta()) :: :ok | :not_found | :drop
Forwards a Send indication from the client to the peer.
Accepts a raw frame or a pre-classified {:send, peer, payload} tuple.
Checks allocation permissions, handles hairpin when the peer is local,
and returns :ok, :not_found, or :drop.
Encodes peer -> client traffic as ChannelData or a Data indication.
When peer_to_channel maps peer_address to a channel number, emits a
ChannelData frame (with stream padding when client_socket.transport.framing()
is :stream). Otherwise builds a STUN Data indication via StunHelper.