ndn_packet/
nack.rs

1use bytes::Bytes;
2
3use crate::tlv_type;
4use crate::{Interest, PacketError};
5use ndn_tlv::{TlvReader, TlvWriter};
6
7/// Reason codes carried in a Nack packet.
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub enum NackReason {
10    /// The forwarder has no route for this Interest.
11    NoRoute,
12    /// The Interest is a duplicate (loop detected).
13    Duplicate,
14    /// The forwarder is congested.
15    Congestion,
16    /// The data does not yet exist; consumer may retry after a hint.
17    NotYet,
18    /// Unknown/vendor-specific reason code.
19    Other(u64),
20}
21
22impl NackReason {
23    pub fn code(&self) -> u64 {
24        match self {
25            NackReason::Congestion => 50,
26            NackReason::Duplicate => 100,
27            NackReason::NoRoute => 150,
28            NackReason::NotYet => 160,
29            NackReason::Other(c) => *c,
30        }
31    }
32
33    pub fn from_code(code: u64) -> Self {
34        match code {
35            50 => NackReason::Congestion,
36            100 => NackReason::Duplicate,
37            150 => NackReason::NoRoute,
38            160 => NackReason::NotYet,
39            c => NackReason::Other(c),
40        }
41    }
42}
43
44/// An NDN Nack — a negative acknowledgement wrapping the rejected Interest.
45#[derive(Debug)]
46pub struct Nack {
47    pub reason: NackReason,
48    pub interest: Interest,
49}
50
51impl Nack {
52    pub fn new(interest: Interest, reason: NackReason) -> Self {
53        Self { reason, interest }
54    }
55
56    /// Decode a Nack from wire bytes.
57    ///
58    /// Accepts both NDNLPv2 format (LpPacket 0x64 with Nack header) and the
59    /// legacy bare Nack TLV (0x0320). Prefer LpPacket format for new code.
60    pub fn decode(raw: Bytes) -> Result<Self, PacketError> {
61        let first = *raw
62            .first()
63            .ok_or(PacketError::Tlv(ndn_tlv::TlvError::UnexpectedEof))?;
64
65        // NDNLPv2 LpPacket format.
66        if first as u64 == tlv_type::LP_PACKET {
67            let lp = crate::lp::LpPacket::decode(raw)?;
68            let reason = lp.nack.ok_or_else(|| {
69                PacketError::MalformedPacket("LpPacket has no Nack header".into())
70            })?;
71            let fragment = lp.fragment.ok_or_else(|| {
72                PacketError::MalformedPacket("Nack LpPacket has no fragment".into())
73            })?;
74            let interest = Interest::decode(fragment)?;
75            return Ok(Self { reason, interest });
76        }
77
78        // Legacy bare Nack TLV (0x0320).
79        let mut reader = TlvReader::new(raw.clone());
80        let (typ, value) = reader.read_tlv()?;
81        if typ != tlv_type::NACK {
82            return Err(PacketError::UnknownPacketType(typ));
83        }
84        let mut inner = TlvReader::new(value);
85
86        let mut reason = NackReason::Other(0);
87        let mut interest_raw: Option<Bytes> = None;
88
89        while !inner.is_empty() {
90            let (t, v) = inner.read_tlv()?;
91            match t {
92                t if t == tlv_type::NACK_REASON => {
93                    let mut code = 0u64;
94                    for &b in v.iter() {
95                        code = (code << 8) | b as u64;
96                    }
97                    reason = NackReason::from_code(code);
98                }
99                t if t == tlv_type::INTEREST => {
100                    let mut w = TlvWriter::new();
101                    w.write_tlv(tlv_type::INTEREST, &v);
102                    interest_raw = Some(w.finish());
103                }
104                _ => {}
105            }
106        }
107
108        let interest_bytes = interest_raw.ok_or(PacketError::Tlv(
109            ndn_tlv::TlvError::MissingField("Interest inside Nack"),
110        ))?;
111        let interest = Interest::decode(interest_bytes)?;
112        Ok(Self { reason, interest })
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use crate::{Name, NameComponent};
120    use bytes::Bytes;
121    use ndn_tlv::TlvWriter;
122
123    fn build_nack(reason_code: u8, name_components: &[&[u8]]) -> Bytes {
124        // Build the Interest inner content (Name TLV value).
125        let mut interest_inner = TlvWriter::new();
126        interest_inner.write_nested(tlv_type::NAME, |w| {
127            for comp in name_components {
128                w.write_tlv(tlv_type::NAME_COMPONENT, comp);
129            }
130        });
131
132        let mut w = TlvWriter::new();
133        w.write_nested(tlv_type::NACK, |w| {
134            w.write_tlv(tlv_type::NACK_REASON, &[reason_code]);
135            // Embed the Interest's inner content as a child TLV with INTEREST type.
136            // Nack::decode reconstructs the full Interest wire bytes from this.
137            w.write_tlv(tlv_type::INTEREST, &interest_inner.finish());
138        });
139        w.finish()
140    }
141
142    // ── NackReason round-trips ────────────────────────────────────────────────
143
144    #[test]
145    fn nack_reason_known_codes() {
146        let cases = [
147            (NackReason::Congestion, 50),
148            (NackReason::Duplicate, 100),
149            (NackReason::NoRoute, 150),
150            (NackReason::NotYet, 160),
151        ];
152        for (reason, code) in cases {
153            assert_eq!(reason.code(), code);
154            assert_eq!(NackReason::from_code(code), reason);
155        }
156    }
157
158    #[test]
159    fn nack_reason_unknown_code_roundtrip() {
160        let reason = NackReason::Other(42);
161        assert_eq!(reason.code(), 42);
162        assert_eq!(NackReason::from_code(42), NackReason::Other(42));
163    }
164
165    // ── Nack::new ─────────────────────────────────────────────────────────────
166
167    #[test]
168    fn nack_new_stores_fields() {
169        let name = Name::from_components([NameComponent::generic(Bytes::from_static(b"test"))]);
170        let interest = Interest::new(name.clone());
171        let nack = Nack::new(interest, NackReason::NoRoute);
172        assert_eq!(nack.reason, NackReason::NoRoute);
173        assert_eq!(*nack.interest.name, name);
174    }
175
176    // ── Nack::decode ─────────────────────────────────────────────────────────
177
178    #[test]
179    fn decode_nack_reason_and_name() {
180        let raw = build_nack(150, &[b"edu", b"ucla"]); // NoRoute = 150
181        let nack = Nack::decode(raw).unwrap();
182        assert_eq!(nack.reason, NackReason::NoRoute);
183        assert_eq!(nack.interest.name.len(), 2);
184        assert_eq!(nack.interest.name.components()[0].value.as_ref(), b"edu");
185    }
186
187    #[test]
188    fn decode_nack_congestion() {
189        let raw = build_nack(50, &[b"test"]);
190        let nack = Nack::decode(raw).unwrap();
191        assert_eq!(nack.reason, NackReason::Congestion);
192    }
193
194    #[test]
195    fn decode_nack_wrong_outer_type_errors() {
196        let mut w = TlvWriter::new();
197        w.write_tlv(0x05, &[]); // INTEREST type, not NACK
198        assert!(matches!(
199            Nack::decode(w.finish()).unwrap_err(),
200            crate::PacketError::UnknownPacketType(0x05)
201        ));
202    }
203
204    #[test]
205    fn decode_nack_missing_interest_errors() {
206        let mut w = TlvWriter::new();
207        w.write_nested(tlv_type::NACK, |w| {
208            w.write_tlv(tlv_type::NACK_REASON, &[50]);
209            // No Interest TLV embedded.
210        });
211        assert!(Nack::decode(w.finish()).is_err());
212    }
213}