Xirsys.XTurn.DataPlane (xturn v2.0.0)

Copy Markdown View Source

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

  • RFC 5766 (ChannelData, Send indication, permissions)
  • RFC 6062 (TCP control allocations drop media here)

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

meta()

@type meta() :: %{
  client_ip: term(),
  client_port: term(),
  server_ip: term(),
  server_port: term(),
  transport: module(),
  socket: term(),
  is_control: boolean()
}

Data-plane socket metadata (meta): client/server endpoints, transport, socket, and :is_control.

Functions

classify(bin)

@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

forward_channel(frame, meta)

@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.

forward_send(frame, meta)

@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.

to_client(packet, peer_address, peer_to_channel, client_socket)

@spec to_client(binary(), {term(), non_neg_integer()}, map(), term()) :: binary()

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.