ndn_config/
control_parameters.rs

1/// NFD Management ControlParameters TLV encoding and decoding.
2///
3/// ControlParameters (TLV type 0x68) carries the arguments for NFD management
4/// commands. All fields are optional; which fields are required depends on the
5/// specific command (e.g. `rib/register` requires Name).
6///
7/// Wire format follows the NFD Management Protocol specification:
8/// <https://redmine.named-data.net/projects/nfd/wiki/ControlCommand>
9///
10/// # NonNegativeInteger encoding
11///
12/// Integer-valued fields use the NDN NonNegativeInteger encoding: the shortest
13/// big-endian representation that fits the value (1, 2, 4, or 8 bytes).
14use bytes::Bytes;
15use ndn_packet::{Name, NameComponent};
16use ndn_tlv::{TlvReader, TlvWriter};
17
18// ─── TLV type constants ──────────────────────────────────────────────────────
19
20pub mod tlv {
21    pub const CONTROL_PARAMETERS: u64 = 0x68;
22    pub const FACE_ID: u64 = 0x69;
23    pub const COST: u64 = 0x6A;
24    pub const STRATEGY: u64 = 0x6B;
25    pub const FLAGS: u64 = 0x6C;
26    pub const EXPIRATION_PERIOD: u64 = 0x6D;
27    pub const ORIGIN: u64 = 0x6F;
28    pub const MASK: u64 = 0x70;
29    pub const URI: u64 = 0x72;
30    pub const LOCAL_URI: u64 = 0x81;
31    pub const CAPACITY: u64 = 0x83;
32    pub const COUNT: u64 = 0x84;
33    pub const FACE_PERSISTENCY: u64 = 0x85;
34    pub const BASE_CONG_INTERVAL: u64 = 0x87;
35    pub const DEF_CONG_THRESHOLD: u64 = 0x88;
36    pub const MTU: u64 = 0x89;
37
38    // Standard NDN Name type.
39    pub const NAME: u64 = 0x07;
40    pub const NAME_COMPONENT: u64 = 0x08;
41}
42
43/// Route origin values (NFD RIB management).
44pub mod origin {
45    pub const APP: u64 = 0;
46    pub const AUTOREG: u64 = 64;
47    pub const CLIENT: u64 = 65;
48    pub const AUTOCONF: u64 = 66;
49    pub const DVR: u64 = 127;
50    pub const NLSR: u64 = 128;
51    pub const PREFIX_ANN: u64 = 129;
52    pub const STATIC: u64 = 255;
53}
54
55/// Route flags (NFD RIB management).
56pub mod route_flags {
57    pub const CHILD_INHERIT: u64 = 1;
58    pub const CAPTURE: u64 = 2;
59}
60
61// ─── ControlParameters ───────────────────────────────────────────────────────
62
63/// NFD ControlParameters — all fields optional.
64#[derive(Debug, Clone, Default, PartialEq, Eq)]
65pub struct ControlParameters {
66    pub name: Option<Name>,
67    pub face_id: Option<u64>,
68    pub uri: Option<String>,
69    pub local_uri: Option<String>,
70    pub origin: Option<u64>,
71    pub cost: Option<u64>,
72    pub flags: Option<u64>,
73    pub mask: Option<u64>,
74    pub expiration_period: Option<u64>,
75    pub face_persistency: Option<u64>,
76    pub strategy: Option<Name>,
77    pub mtu: Option<u64>,
78    pub capacity: Option<u64>,
79    pub count: Option<u64>,
80}
81
82impl ControlParameters {
83    pub fn new() -> Self {
84        Self::default()
85    }
86
87    /// Encode to wire format as a complete ControlParameters TLV (type 0x68).
88    pub fn encode(&self) -> Bytes {
89        let mut w = TlvWriter::new();
90        w.write_nested(tlv::CONTROL_PARAMETERS, |w| {
91            self.encode_inner(w);
92        });
93        w.finish()
94    }
95
96    /// Encode the inner fields (without the outer 0x68 wrapper).
97    /// Useful for embedding in a name component.
98    pub fn encode_value(&self) -> Bytes {
99        let mut w = TlvWriter::new();
100        self.encode_inner(&mut w);
101        w.finish()
102    }
103
104    fn encode_inner(&self, w: &mut TlvWriter) {
105        if let Some(ref name) = self.name {
106            encode_name(w, name);
107        }
108        if let Some(id) = self.face_id {
109            write_non_neg_int(w, tlv::FACE_ID, id);
110        }
111        if let Some(ref uri) = self.uri {
112            w.write_tlv(tlv::URI, uri.as_bytes());
113        }
114        if let Some(ref local_uri) = self.local_uri {
115            w.write_tlv(tlv::LOCAL_URI, local_uri.as_bytes());
116        }
117        if let Some(origin) = self.origin {
118            write_non_neg_int(w, tlv::ORIGIN, origin);
119        }
120        if let Some(cost) = self.cost {
121            write_non_neg_int(w, tlv::COST, cost);
122        }
123        if let Some(flags) = self.flags {
124            write_non_neg_int(w, tlv::FLAGS, flags);
125        }
126        if let Some(mask) = self.mask {
127            write_non_neg_int(w, tlv::MASK, mask);
128        }
129        if let Some(strategy) = self.strategy.as_ref() {
130            w.write_nested(tlv::STRATEGY, |w| {
131                encode_name(w, strategy);
132            });
133        }
134        if let Some(ep) = self.expiration_period {
135            write_non_neg_int(w, tlv::EXPIRATION_PERIOD, ep);
136        }
137        if let Some(fp) = self.face_persistency {
138            write_non_neg_int(w, tlv::FACE_PERSISTENCY, fp);
139        }
140        if let Some(mtu) = self.mtu {
141            write_non_neg_int(w, tlv::MTU, mtu);
142        }
143        if let Some(capacity) = self.capacity {
144            write_non_neg_int(w, tlv::CAPACITY, capacity);
145        }
146        if let Some(count) = self.count {
147            write_non_neg_int(w, tlv::COUNT, count);
148        }
149    }
150
151    /// Decode from a complete ControlParameters TLV (type 0x68).
152    pub fn decode(wire: Bytes) -> Result<Self, ControlParametersError> {
153        let mut r = TlvReader::new(wire);
154        let (typ, value) = r
155            .read_tlv()
156            .map_err(|_| ControlParametersError::MalformedTlv)?;
157        if typ != tlv::CONTROL_PARAMETERS {
158            return Err(ControlParametersError::WrongType(typ));
159        }
160        Self::decode_value(value)
161    }
162
163    /// Decode from the inner value bytes (without the outer 0x68 wrapper).
164    pub fn decode_value(value: Bytes) -> Result<Self, ControlParametersError> {
165        let mut r = TlvReader::new(value);
166        let mut params = ControlParameters::default();
167
168        while !r.is_empty() {
169            let (typ, val) = r
170                .read_tlv()
171                .map_err(|_| ControlParametersError::MalformedTlv)?;
172            match typ {
173                tlv::NAME => {
174                    params.name = Some(decode_name(val)?);
175                }
176                tlv::FACE_ID => {
177                    params.face_id = Some(read_non_neg_int(&val)?);
178                }
179                tlv::URI => {
180                    params.uri = Some(
181                        std::str::from_utf8(&val)
182                            .map_err(|_| ControlParametersError::InvalidUtf8)?
183                            .to_owned(),
184                    );
185                }
186                tlv::LOCAL_URI => {
187                    params.local_uri = Some(
188                        std::str::from_utf8(&val)
189                            .map_err(|_| ControlParametersError::InvalidUtf8)?
190                            .to_owned(),
191                    );
192                }
193                tlv::ORIGIN => {
194                    params.origin = Some(read_non_neg_int(&val)?);
195                }
196                tlv::COST => {
197                    params.cost = Some(read_non_neg_int(&val)?);
198                }
199                tlv::FLAGS => {
200                    params.flags = Some(read_non_neg_int(&val)?);
201                }
202                tlv::MASK => {
203                    params.mask = Some(read_non_neg_int(&val)?);
204                }
205                tlv::STRATEGY => {
206                    let mut inner = TlvReader::new(val);
207                    let (t, v) = inner
208                        .read_tlv()
209                        .map_err(|_| ControlParametersError::MalformedTlv)?;
210                    if t != tlv::NAME {
211                        return Err(ControlParametersError::WrongType(t));
212                    }
213                    params.strategy = Some(decode_name(v)?);
214                }
215                tlv::EXPIRATION_PERIOD => {
216                    params.expiration_period = Some(read_non_neg_int(&val)?);
217                }
218                tlv::FACE_PERSISTENCY => {
219                    params.face_persistency = Some(read_non_neg_int(&val)?);
220                }
221                tlv::MTU => {
222                    params.mtu = Some(read_non_neg_int(&val)?);
223                }
224                tlv::CAPACITY => {
225                    params.capacity = Some(read_non_neg_int(&val)?);
226                }
227                tlv::COUNT => {
228                    params.count = Some(read_non_neg_int(&val)?);
229                }
230                // Unknown non-critical types are silently skipped.
231                _ => {}
232            }
233        }
234
235        Ok(params)
236    }
237}
238
239// ─── Errors ──────────────────────────────────────────────────────────────────
240
241#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
242pub enum ControlParametersError {
243    #[error("malformed TLV")]
244    MalformedTlv,
245    #[error("unexpected TLV type {0:#x}")]
246    WrongType(u64),
247    #[error("invalid NonNegativeInteger length")]
248    InvalidNonNegInt,
249    #[error("invalid UTF-8 in string field")]
250    InvalidUtf8,
251}
252
253// ─── NonNegativeInteger helpers ──────────────────────────────────────────────
254
255/// Encode a NonNegativeInteger: the shortest big-endian representation.
256fn encode_non_neg_int(value: u64) -> Vec<u8> {
257    if value <= 0xFF {
258        vec![value as u8]
259    } else if value <= 0xFFFF {
260        (value as u16).to_be_bytes().to_vec()
261    } else if value <= 0xFFFF_FFFF {
262        (value as u32).to_be_bytes().to_vec()
263    } else {
264        value.to_be_bytes().to_vec()
265    }
266}
267
268/// Write a TLV element with a NonNegativeInteger value.
269fn write_non_neg_int(w: &mut TlvWriter, typ: u64, value: u64) {
270    w.write_tlv(typ, &encode_non_neg_int(value));
271}
272
273/// Read a NonNegativeInteger from a value slice (1, 2, 4, or 8 bytes).
274fn read_non_neg_int(buf: &[u8]) -> Result<u64, ControlParametersError> {
275    match buf.len() {
276        1 => Ok(buf[0] as u64),
277        2 => Ok(u16::from_be_bytes([buf[0], buf[1]]) as u64),
278        4 => Ok(u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) as u64),
279        8 => Ok(u64::from_be_bytes([
280            buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
281        ])),
282        _ => Err(ControlParametersError::InvalidNonNegInt),
283    }
284}
285
286// ─── Name helpers ────────────────────────────────────────────────────────────
287
288fn encode_name(w: &mut TlvWriter, name: &Name) {
289    w.write_nested(tlv::NAME, |w| {
290        for comp in name.components() {
291            w.write_tlv(comp.typ, &comp.value);
292        }
293    });
294}
295
296fn decode_name(value: Bytes) -> Result<Name, ControlParametersError> {
297    let mut r = TlvReader::new(value);
298    let mut components = Vec::new();
299    while !r.is_empty() {
300        let (typ, val) = r
301            .read_tlv()
302            .map_err(|_| ControlParametersError::MalformedTlv)?;
303        components.push(NameComponent { typ, value: val });
304    }
305    if components.is_empty() {
306        Ok(Name::root())
307    } else {
308        Ok(Name::from_components(components))
309    }
310}
311
312// ─── Tests ───────────────────────────────────────────────────────────────────
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317
318    fn name(components: &[&[u8]]) -> Name {
319        Name::from_components(
320            components
321                .iter()
322                .map(|c| NameComponent::generic(Bytes::copy_from_slice(c))),
323        )
324    }
325
326    #[test]
327    fn non_neg_int_encoding() {
328        assert_eq!(encode_non_neg_int(0), vec![0]);
329        assert_eq!(encode_non_neg_int(255), vec![255]);
330        assert_eq!(encode_non_neg_int(256), vec![1, 0]);
331        assert_eq!(encode_non_neg_int(0xFFFF), vec![0xFF, 0xFF]);
332        assert_eq!(encode_non_neg_int(0x10000), vec![0, 1, 0, 0]);
333        assert_eq!(
334            encode_non_neg_int(0x1_0000_0000),
335            vec![0, 0, 0, 1, 0, 0, 0, 0]
336        );
337    }
338
339    #[test]
340    fn non_neg_int_roundtrip() {
341        for v in [
342            0u64,
343            1,
344            255,
345            256,
346            0xFFFF,
347            0x10000,
348            0xFFFF_FFFF,
349            0x1_0000_0000,
350            u64::MAX,
351        ] {
352            let encoded = encode_non_neg_int(v);
353            let decoded = read_non_neg_int(&encoded).unwrap();
354            assert_eq!(decoded, v, "roundtrip failed for {v}");
355        }
356    }
357
358    #[test]
359    fn encode_decode_empty() {
360        let params = ControlParameters::new();
361        let wire = params.encode();
362        let decoded = ControlParameters::decode(wire).unwrap();
363        assert_eq!(decoded, params);
364    }
365
366    #[test]
367    fn encode_decode_rib_register() {
368        let params = ControlParameters {
369            name: Some(name(&[b"ndn", b"test"])),
370            face_id: Some(5),
371            origin: Some(origin::APP),
372            cost: Some(10),
373            flags: Some(route_flags::CHILD_INHERIT),
374            ..Default::default()
375        };
376        let wire = params.encode();
377        let decoded = ControlParameters::decode(wire).unwrap();
378        assert_eq!(decoded, params);
379    }
380
381    #[test]
382    fn encode_decode_faces_create() {
383        let params = ControlParameters {
384            uri: Some("shm://myapp".to_owned()),
385            face_persistency: Some(0), // persistent
386            ..Default::default()
387        };
388        let wire = params.encode();
389        let decoded = ControlParameters::decode(wire).unwrap();
390        assert_eq!(decoded, params);
391    }
392
393    #[test]
394    fn encode_decode_with_strategy() {
395        let params = ControlParameters {
396            name: Some(name(&[b"test"])),
397            strategy: Some(name(&[b"ndn", b"strategy", b"best-route"])),
398            ..Default::default()
399        };
400        let wire = params.encode();
401        let decoded = ControlParameters::decode(wire).unwrap();
402        assert_eq!(decoded, params);
403    }
404
405    #[test]
406    fn encode_decode_all_fields() {
407        let params = ControlParameters {
408            name: Some(name(&[b"hello"])),
409            face_id: Some(42),
410            uri: Some("udp4://192.168.1.1:6363".to_owned()),
411            local_uri: Some("udp4://0.0.0.0:6363".to_owned()),
412            origin: Some(origin::STATIC),
413            cost: Some(100),
414            flags: Some(route_flags::CHILD_INHERIT | route_flags::CAPTURE),
415            mask: Some(3),
416            expiration_period: Some(30_000),
417            face_persistency: Some(1),
418            strategy: Some(name(&[b"ndn", b"strategy", b"multicast"])),
419            mtu: Some(8800),
420            capacity: Some(1024 * 1024),
421            count: Some(42),
422        };
423        let wire = params.encode();
424        let decoded = ControlParameters::decode(wire).unwrap();
425        assert_eq!(decoded, params);
426    }
427
428    #[test]
429    fn decode_value_works() {
430        let params = ControlParameters {
431            name: Some(name(&[b"test"])),
432            cost: Some(5),
433            ..Default::default()
434        };
435        let value = params.encode_value();
436        let decoded = ControlParameters::decode_value(value).unwrap();
437        assert_eq!(decoded, params);
438    }
439
440    #[test]
441    fn decode_wrong_type_errors() {
442        // Build a TLV with type 0x05 (Interest) instead of 0x68
443        let mut w = TlvWriter::new();
444        w.write_nested(0x05, |_| {});
445        let result = ControlParameters::decode(w.finish());
446        assert!(matches!(
447            result,
448            Err(ControlParametersError::WrongType(0x05))
449        ));
450    }
451
452    #[test]
453    fn decode_ignores_unknown_types() {
454        // Build ControlParameters with an unknown even-typed field
455        let mut w = TlvWriter::new();
456        w.write_nested(tlv::CONTROL_PARAMETERS, |w| {
457            write_non_neg_int(w, tlv::COST, 10);
458            w.write_tlv(0xFE, b"unknown"); // unknown non-critical
459            write_non_neg_int(w, tlv::FACE_ID, 3);
460        });
461        let decoded = ControlParameters::decode(w.finish()).unwrap();
462        assert_eq!(decoded.cost, Some(10));
463        assert_eq!(decoded.face_id, Some(3));
464    }
465}