Skip to main content

srt_runtime/packet/
control.rs

1//! SRT control packet dispatch — `draft-sharabayko-srt-01` §3.2, Figure 4,
2//! Table 1.
3//!
4//! ```text
5//! word0  1|         Control Type (15)   |          Subtype (16)         |
6//! word1                   Type-specific Information (32)
7//! word2                          Timestamp (32)
8//! word3                    Destination Socket ID (32)
9//! rest                              CIF (variable, per Control Type)
10//! ```
11//!
12//! `Subtype` is `0x0` for every Control Type in Table 1 except
13//! `User-Defined Type` (`0x7FFF`), where it carries `SRT_CMD_KMREQ` /
14//! `SRT_CMD_KMRSP` (Table 5) when the CIF is a Key Material message (§3.2.2).
15//! `Subtype` and the `Type-specific Information` word (where a given packet
16//! type does not use it) are validated as reserved-must-be-zero and are not
17//! stored in the typed per-type structs — see the crate root's reserved-bit
18//! policy.
19
20use super::ack::AckPacket;
21use super::handshake::HandshakePacket;
22use super::misc::{
23    AckAckPacket, CongestionWarningPacket, DropReqPacket, KeepAlivePacket, PeerErrorPacket,
24    ShutdownPacket,
25};
26use super::nak::NakPacket;
27use super::{Error, Result, SRT_HEADER_LEN, be32, put_be32};
28
29/// `Control Type` wire values (`draft-sharabayko-srt-01` §3.2, Table 1).
30pub const CONTROL_TYPE_HANDSHAKE: u16 = 0x0000;
31/// Keep-Alive (§3.2.3).
32pub const CONTROL_TYPE_KEEPALIVE: u16 = 0x0001;
33/// ACK (§3.2.4).
34pub const CONTROL_TYPE_ACK: u16 = 0x0002;
35/// NAK / Loss Report (§3.2.5).
36pub const CONTROL_TYPE_NAK: u16 = 0x0003;
37/// Congestion Warning (§3.2.6).
38pub const CONTROL_TYPE_CONGESTION_WARNING: u16 = 0x0004;
39/// Shutdown (§3.2.7).
40pub const CONTROL_TYPE_SHUTDOWN: u16 = 0x0005;
41/// ACKACK (§3.2.8).
42pub const CONTROL_TYPE_ACKACK: u16 = 0x0006;
43/// Message Drop Request (§3.2.9).
44pub const CONTROL_TYPE_DROPREQ: u16 = 0x0007;
45/// Peer Error (§3.2.10).
46pub const CONTROL_TYPE_PEERERROR: u16 = 0x0008;
47/// User-Defined Type (reserved value; also carries the Key Material message
48/// per §3.2.2 via `Subtype` `SRT_CMD_KMREQ`/`SRT_CMD_KMRSP`).
49pub const CONTROL_TYPE_USER_DEFINED: u16 = 0x7FFF;
50
51/// The `Control Type` field (`draft-sharabayko-srt-01` §3.2, Table 1) — 15
52/// bits.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55#[non_exhaustive]
56pub enum ControlType {
57    /// `0x0000` — Handshake (§3.2.1).
58    Handshake,
59    /// `0x0001` — Keep-Alive (§3.2.3).
60    KeepAlive,
61    /// `0x0002` — ACK (§3.2.4).
62    Ack,
63    /// `0x0003` — NAK / Loss Report (§3.2.5).
64    Nak,
65    /// `0x0004` — Congestion Warning (§3.2.6).
66    CongestionWarning,
67    /// `0x0005` — Shutdown (§3.2.7).
68    Shutdown,
69    /// `0x0006` — ACKACK (§3.2.8).
70    AckAck,
71    /// `0x0007` — Message Drop Request (§3.2.9).
72    DropReq,
73    /// `0x0008` — Peer Error (§3.2.10).
74    PeerError,
75    /// `0x7FFF` — User-Defined Type (also the Key Material control-packet
76    /// delivery form, §3.2.2).
77    UserDefined,
78    /// A Control Type value Table 1 does not define.
79    Reserved(u16),
80}
81
82impl ControlType {
83    /// Decode a 15-bit `Control Type` value.
84    pub fn from_bits(v: u16) -> Self {
85        match v {
86            CONTROL_TYPE_HANDSHAKE => ControlType::Handshake,
87            CONTROL_TYPE_KEEPALIVE => ControlType::KeepAlive,
88            CONTROL_TYPE_ACK => ControlType::Ack,
89            CONTROL_TYPE_NAK => ControlType::Nak,
90            CONTROL_TYPE_CONGESTION_WARNING => ControlType::CongestionWarning,
91            CONTROL_TYPE_SHUTDOWN => ControlType::Shutdown,
92            CONTROL_TYPE_ACKACK => ControlType::AckAck,
93            CONTROL_TYPE_DROPREQ => ControlType::DropReq,
94            CONTROL_TYPE_PEERERROR => ControlType::PeerError,
95            CONTROL_TYPE_USER_DEFINED => ControlType::UserDefined,
96            other => ControlType::Reserved(other),
97        }
98    }
99
100    /// The wire value.
101    pub fn to_bits(self) -> u16 {
102        match self {
103            ControlType::Handshake => CONTROL_TYPE_HANDSHAKE,
104            ControlType::KeepAlive => CONTROL_TYPE_KEEPALIVE,
105            ControlType::Ack => CONTROL_TYPE_ACK,
106            ControlType::Nak => CONTROL_TYPE_NAK,
107            ControlType::CongestionWarning => CONTROL_TYPE_CONGESTION_WARNING,
108            ControlType::Shutdown => CONTROL_TYPE_SHUTDOWN,
109            ControlType::AckAck => CONTROL_TYPE_ACKACK,
110            ControlType::DropReq => CONTROL_TYPE_DROPREQ,
111            ControlType::PeerError => CONTROL_TYPE_PEERERROR,
112            ControlType::UserDefined => CONTROL_TYPE_USER_DEFINED,
113            ControlType::Reserved(v) => v,
114        }
115    }
116
117    /// Spec label.
118    pub fn name(&self) -> &'static str {
119        match self {
120            ControlType::Handshake => "handshake",
121            ControlType::KeepAlive => "keep-alive",
122            ControlType::Ack => "ACK",
123            ControlType::Nak => "NAK",
124            ControlType::CongestionWarning => "congestion warning",
125            ControlType::Shutdown => "shutdown",
126            ControlType::AckAck => "ACKACK",
127            ControlType::DropReq => "message drop request",
128            ControlType::PeerError => "peer error",
129            ControlType::UserDefined => "user-defined",
130            ControlType::Reserved(_) => "reserved",
131        }
132    }
133}
134
135broadcast_common::impl_spec_display!(ControlType, Reserved);
136
137/// A Control Type = User-Defined (`0x7FFF`) packet, or a Control Type not
138/// defined by Table 1. The CIF is genuinely opaque here — the only defined
139/// use is the Key Material message (§3.2.2) via `Subtype`
140/// `SRT_CMD_KMREQ`/`SRT_CMD_KMRSP` — see [`Self::as_key_material`].
141#[derive(Debug, Clone, PartialEq)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize))]
143pub struct UserDefinedPacket<'a> {
144    /// The raw 15-bit Control Type value.
145    pub control_type: u16,
146    /// The 16-bit Subtype — `SRT_CMD_KMREQ`/`SRT_CMD_KMRSP` when the CIF is
147    /// Key Material (Table 5), otherwise vendor-defined.
148    pub subtype: u16,
149    /// The header `Type-specific Information` word.
150    pub type_specific_info: u32,
151    /// Timestamp (§3).
152    pub timestamp: u32,
153    /// Destination Socket ID (§3).
154    pub dest_socket_id: u32,
155    /// The raw CIF bytes.
156    pub cif: &'a [u8],
157}
158
159impl<'a> UserDefinedPacket<'a> {
160    /// Attempt to decode [`Self::cif`] as a Key Material message (§3.2.2).
161    /// Valid regardless of [`Self::subtype`] — callers that care should check
162    /// `subtype` against `SRT_CMD_KMREQ`/`SRT_CMD_KMRSP` (Table 5) first.
163    pub fn as_key_material(&self) -> Result<super::KeyMaterial<'a>> {
164        super::KeyMaterial::parse(self.cif)
165    }
166}
167
168/// An SRT control packet (`draft-sharabayko-srt-01` §3.2).
169#[derive(Debug, Clone, PartialEq)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize))]
171#[non_exhaustive]
172pub enum ControlPacket<'a> {
173    /// Handshake (§3.2.1).
174    Handshake(HandshakePacket<'a>),
175    /// Keep-Alive (§3.2.3).
176    KeepAlive(KeepAlivePacket),
177    /// ACK (§3.2.4).
178    Ack(AckPacket),
179    /// NAK / Loss Report (§3.2.5).
180    Nak(NakPacket<'a>),
181    /// Congestion Warning (§3.2.6).
182    CongestionWarning(CongestionWarningPacket),
183    /// Shutdown (§3.2.7).
184    Shutdown(ShutdownPacket),
185    /// ACKACK (§3.2.8).
186    AckAck(AckAckPacket),
187    /// Message Drop Request (§3.2.9).
188    DropReq(DropReqPacket),
189    /// Peer Error (§3.2.10).
190    PeerError(PeerErrorPacket),
191    /// User-Defined Type, or a Control Type Table 1 does not define.
192    UserDefined(UserDefinedPacket<'a>),
193}
194
195fn check_reserved_u16(what: &'static str, v: u16) -> Result<()> {
196    if v != 0 {
197        return Err(Error::ReservedFieldNotZero {
198            what,
199            value: u64::from(v),
200        });
201    }
202    Ok(())
203}
204
205fn check_reserved_u32(what: &'static str, v: u32) -> Result<()> {
206    if v != 0 {
207        return Err(Error::ReservedFieldNotZero {
208            what,
209            value: u64::from(v),
210        });
211    }
212    Ok(())
213}
214
215fn check_no_cif(what: &'static str, cif: &[u8]) -> Result<()> {
216    if !cif.is_empty() {
217        return Err(Error::UnexpectedTrailingBytes {
218            what,
219            extra: cif.len(),
220        });
221    }
222    Ok(())
223}
224
225impl<'a> ControlPacket<'a> {
226    /// Parse a control packet from `bytes` (the full SRT packet).
227    ///
228    /// # Errors
229    /// [`Error::BufferTooShort`] if shorter than the 16-byte header;
230    /// [`Error::WrongPacketKind`] if the `F` bit is clear (this is a data
231    /// packet); type-specific errors from the dispatched CIF parser.
232    pub fn parse(bytes: &'a [u8]) -> Result<Self> {
233        if bytes.len() < SRT_HEADER_LEN {
234            return Err(Error::BufferTooShort {
235                need: SRT_HEADER_LEN,
236                have: bytes.len(),
237                what: "SRT control packet header",
238            });
239        }
240        let word0 = be32(bytes, 0);
241        if word0 & super::F_BIT == 0 {
242            return Err(Error::WrongPacketKind {
243                expected: "control packet (F=1)",
244            });
245        }
246        let control_type_bits = ((word0 >> 16) & 0x7FFF) as u16;
247        let subtype = (word0 & 0xFFFF) as u16;
248        let type_specific_info = be32(bytes, 4);
249        let timestamp = be32(bytes, 8);
250        let dest_socket_id = be32(bytes, 12);
251        let cif = &bytes[SRT_HEADER_LEN..];
252        let control_type = ControlType::from_bits(control_type_bits);
253
254        Ok(match control_type {
255            ControlType::Handshake => {
256                check_reserved_u16("Subtype", subtype)?;
257                check_reserved_u32("Type-specific Information", type_specific_info)?;
258                ControlPacket::Handshake(HandshakePacket::parse_cif(
259                    timestamp,
260                    dest_socket_id,
261                    cif,
262                )?)
263            }
264            ControlType::KeepAlive => {
265                check_reserved_u16("Subtype", subtype)?;
266                check_reserved_u32("Type-specific Information", type_specific_info)?;
267                check_no_cif("keep-alive CIF", cif)?;
268                ControlPacket::KeepAlive(KeepAlivePacket {
269                    timestamp,
270                    dest_socket_id,
271                })
272            }
273            ControlType::Ack => {
274                check_reserved_u16("Subtype", subtype)?;
275                ControlPacket::Ack(AckPacket::parse_cif(
276                    type_specific_info,
277                    timestamp,
278                    dest_socket_id,
279                    cif,
280                )?)
281            }
282            ControlType::Nak => {
283                check_reserved_u16("Subtype", subtype)?;
284                check_reserved_u32("Type-specific Information", type_specific_info)?;
285                ControlPacket::Nak(NakPacket::parse_cif(timestamp, dest_socket_id, cif))
286            }
287            ControlType::CongestionWarning => {
288                check_reserved_u16("Subtype", subtype)?;
289                check_reserved_u32("Type-specific Information", type_specific_info)?;
290                check_no_cif("congestion warning CIF", cif)?;
291                ControlPacket::CongestionWarning(CongestionWarningPacket {
292                    timestamp,
293                    dest_socket_id,
294                })
295            }
296            ControlType::Shutdown => {
297                check_reserved_u16("Subtype", subtype)?;
298                check_reserved_u32("Type-specific Information", type_specific_info)?;
299                check_no_cif("shutdown CIF", cif)?;
300                ControlPacket::Shutdown(ShutdownPacket {
301                    timestamp,
302                    dest_socket_id,
303                })
304            }
305            ControlType::AckAck => {
306                check_reserved_u16("Subtype", subtype)?;
307                check_no_cif("ACKACK CIF", cif)?;
308                ControlPacket::AckAck(AckAckPacket {
309                    ack_number: type_specific_info,
310                    timestamp,
311                    dest_socket_id,
312                })
313            }
314            ControlType::DropReq => {
315                check_reserved_u16("Subtype", subtype)?;
316                ControlPacket::DropReq(DropReqPacket::parse_cif(
317                    type_specific_info,
318                    timestamp,
319                    dest_socket_id,
320                    cif,
321                )?)
322            }
323            ControlType::PeerError => {
324                check_reserved_u16("Subtype", subtype)?;
325                check_no_cif("peer error CIF", cif)?;
326                ControlPacket::PeerError(PeerErrorPacket {
327                    error_code: type_specific_info,
328                    timestamp,
329                    dest_socket_id,
330                })
331            }
332            ControlType::UserDefined | ControlType::Reserved(_) => {
333                ControlPacket::UserDefined(UserDefinedPacket {
334                    control_type: control_type_bits,
335                    subtype,
336                    type_specific_info,
337                    timestamp,
338                    dest_socket_id,
339                    cif,
340                })
341            }
342        })
343    }
344
345    /// The `Control Type` this packet carries.
346    pub fn control_type(&self) -> ControlType {
347        match self {
348            ControlPacket::Handshake(_) => ControlType::Handshake,
349            ControlPacket::KeepAlive(_) => ControlType::KeepAlive,
350            ControlPacket::Ack(_) => ControlType::Ack,
351            ControlPacket::Nak(_) => ControlType::Nak,
352            ControlPacket::CongestionWarning(_) => ControlType::CongestionWarning,
353            ControlPacket::Shutdown(_) => ControlType::Shutdown,
354            ControlPacket::AckAck(_) => ControlType::AckAck,
355            ControlPacket::DropReq(_) => ControlType::DropReq,
356            ControlPacket::PeerError(_) => ControlType::PeerError,
357            ControlPacket::UserDefined(u) => ControlType::from_bits(u.control_type),
358        }
359    }
360
361    fn subtype(&self) -> u16 {
362        match self {
363            ControlPacket::UserDefined(u) => u.subtype,
364            _ => 0,
365        }
366    }
367
368    fn word1(&self) -> u32 {
369        match self {
370            ControlPacket::Handshake(_)
371            | ControlPacket::KeepAlive(_)
372            | ControlPacket::Nak(_)
373            | ControlPacket::CongestionWarning(_)
374            | ControlPacket::Shutdown(_) => 0,
375            ControlPacket::Ack(a) => a.ack_number,
376            ControlPacket::AckAck(a) => a.ack_number,
377            ControlPacket::DropReq(d) => d.message_number,
378            ControlPacket::PeerError(p) => p.error_code,
379            ControlPacket::UserDefined(u) => u.type_specific_info,
380        }
381    }
382
383    fn timestamp(&self) -> u32 {
384        match self {
385            ControlPacket::Handshake(h) => h.timestamp,
386            ControlPacket::KeepAlive(k) => k.timestamp,
387            ControlPacket::Ack(a) => a.timestamp,
388            ControlPacket::Nak(n) => n.timestamp,
389            ControlPacket::CongestionWarning(c) => c.timestamp,
390            ControlPacket::Shutdown(s) => s.timestamp,
391            ControlPacket::AckAck(a) => a.timestamp,
392            ControlPacket::DropReq(d) => d.timestamp,
393            ControlPacket::PeerError(p) => p.timestamp,
394            ControlPacket::UserDefined(u) => u.timestamp,
395        }
396    }
397
398    fn dest_socket_id(&self) -> u32 {
399        match self {
400            ControlPacket::Handshake(h) => h.dest_socket_id,
401            ControlPacket::KeepAlive(k) => k.dest_socket_id,
402            ControlPacket::Ack(a) => a.dest_socket_id,
403            ControlPacket::Nak(n) => n.dest_socket_id,
404            ControlPacket::CongestionWarning(c) => c.dest_socket_id,
405            ControlPacket::Shutdown(s) => s.dest_socket_id,
406            ControlPacket::AckAck(a) => a.dest_socket_id,
407            ControlPacket::DropReq(d) => d.dest_socket_id,
408            ControlPacket::PeerError(p) => p.dest_socket_id,
409            ControlPacket::UserDefined(u) => u.dest_socket_id,
410        }
411    }
412
413    fn cif_len(&self) -> usize {
414        match self {
415            ControlPacket::Handshake(h) => h.cif_len(),
416            ControlPacket::KeepAlive(_)
417            | ControlPacket::CongestionWarning(_)
418            | ControlPacket::Shutdown(_)
419            | ControlPacket::AckAck(_)
420            | ControlPacket::PeerError(_) => 0,
421            ControlPacket::Ack(a) => a.cif_len(),
422            ControlPacket::Nak(n) => n.cif_len(),
423            ControlPacket::DropReq(d) => d.cif_len(),
424            ControlPacket::UserDefined(u) => u.cif.len(),
425        }
426    }
427
428    /// Number of bytes [`Self::serialize_into`] will write.
429    pub fn serialized_len(&self) -> usize {
430        SRT_HEADER_LEN + self.cif_len()
431    }
432
433    /// Serialize this control packet into `buf`.
434    pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
435        let len = self.serialized_len();
436        if buf.len() < len {
437            return Err(Error::OutputBufferTooSmall {
438                need: len,
439                have: buf.len(),
440            });
441        }
442        let control_type_bits = self.control_type().to_bits();
443        if control_type_bits > 0x7FFF {
444            return Err(Error::FieldTooWide {
445                what: "Control Type",
446                value: u64::from(control_type_bits),
447                bits: 15,
448            });
449        }
450        let word0 = super::F_BIT | (u32::from(control_type_bits) << 16) | u32::from(self.subtype());
451        put_be32(buf, 0, word0);
452        put_be32(buf, 4, self.word1());
453        put_be32(buf, 8, self.timestamp());
454        put_be32(buf, 12, self.dest_socket_id());
455        let cif = &mut buf[SRT_HEADER_LEN..len];
456        match self {
457            ControlPacket::Handshake(h) => {
458                h.write_cif(cif)?;
459            }
460            ControlPacket::KeepAlive(_)
461            | ControlPacket::CongestionWarning(_)
462            | ControlPacket::Shutdown(_)
463            | ControlPacket::AckAck(_)
464            | ControlPacket::PeerError(_) => {}
465            ControlPacket::Ack(a) => {
466                a.write_cif(cif)?;
467            }
468            ControlPacket::Nak(n) => {
469                n.write_cif(cif);
470            }
471            ControlPacket::DropReq(d) => {
472                d.write_cif(cif);
473            }
474            ControlPacket::UserDefined(u) => {
475                cif.copy_from_slice(u.cif);
476            }
477        }
478        Ok(len)
479    }
480}