ndn_discovery/hello/
probe.rs1use bytes::Bytes;
39use ndn_packet::{Name, tlv_type};
40use ndn_tlv::TlvWriter;
41
42use crate::scope::{probe_direct, probe_via};
43use crate::wire::{parse_raw_data, parse_raw_interest, write_name_tlv, write_nni};
44
45pub fn build_direct_probe(target: &Name, nonce: u32) -> Bytes {
51 let mut w = TlvWriter::new();
52 w.write_nested(tlv_type::INTEREST, |w: &mut TlvWriter| {
53 w.write_nested(tlv_type::NAME, |w: &mut TlvWriter| {
54 for comp in probe_direct().components() {
55 w.write_tlv(comp.typ, &comp.value);
56 }
57 for comp in target.components() {
58 w.write_tlv(comp.typ, &comp.value);
59 }
60 w.write_tlv(tlv_type::NAME_COMPONENT, &nonce.to_be_bytes());
61 });
62 w.write_tlv(tlv_type::NONCE, &nonce.to_be_bytes());
63 write_nni(w, tlv_type::INTEREST_LIFETIME, 2000);
64 });
65 w.finish()
66}
67
68pub fn build_indirect_probe(intermediary: &Name, target: &Name, nonce: u32) -> Bytes {
75 let mut w = TlvWriter::new();
76 w.write_nested(tlv_type::INTEREST, |w: &mut TlvWriter| {
77 w.write_nested(tlv_type::NAME, |w: &mut TlvWriter| {
78 for comp in probe_via().components() {
79 w.write_tlv(comp.typ, &comp.value);
80 }
81 for comp in intermediary.components() {
82 w.write_tlv(comp.typ, &comp.value);
83 }
84 for comp in target.components() {
85 w.write_tlv(comp.typ, &comp.value);
86 }
87 w.write_tlv(tlv_type::NAME_COMPONENT, &nonce.to_be_bytes());
88 });
89 w.write_tlv(tlv_type::NONCE, &nonce.to_be_bytes());
90 write_nni(w, tlv_type::INTEREST_LIFETIME, 4000);
91 });
92 w.finish()
93}
94
95pub fn build_probe_ack(interest_name: &Name) -> Bytes {
99 let mut w = TlvWriter::new();
100 w.write_nested(tlv_type::DATA, |w: &mut TlvWriter| {
101 write_name_tlv(w, interest_name);
102 w.write_nested(tlv_type::META_INFO, |w: &mut TlvWriter| {
103 write_nni(w, tlv_type::FRESHNESS_PERIOD, 0);
104 });
105 w.write_tlv(tlv_type::CONTENT, &[]);
107 w.write_nested(tlv_type::SIGNATURE_INFO, |w: &mut TlvWriter| {
108 w.write_tlv(tlv_type::SIGNATURE_TYPE, &[0u8]);
109 });
110 w.write_tlv(tlv_type::SIGNATURE_VALUE, &[0u8; 32]);
111 });
112 w.finish()
113}
114
115pub fn parse_direct_probe(raw: &Bytes) -> Option<DirectProbe> {
122 let parsed = parse_raw_interest(raw)?;
123 let name = &parsed.name;
124 let prefix = probe_direct();
125
126 if !name.has_prefix(prefix) {
127 return None;
128 }
129
130 let comps = name.components();
131 let prefix_len = prefix.components().len();
132
133 if comps.len() < prefix_len + 2 {
135 return None;
136 }
137
138 let nonce_comp = &comps[comps.len() - 1];
139 if nonce_comp.value.len() != 4 {
140 return None;
141 }
142 let nonce = u32::from_be_bytes(nonce_comp.value[..4].try_into().ok()?);
143
144 let target_comps = &comps[prefix_len..comps.len() - 1];
146 let target = Name::from_components(target_comps.iter().cloned());
147
148 Some(DirectProbe { target, nonce })
149}
150
151pub fn parse_indirect_probe(raw: &Bytes) -> Option<IndirectProbe> {
164 let parsed = parse_raw_interest(raw)?;
165 let name = &parsed.name;
166 let prefix = probe_via();
167
168 if !name.has_prefix(prefix) {
169 return None;
170 }
171
172 let comps = name.components();
173 let prefix_len = prefix.components().len();
174
175 if comps.len() < prefix_len + 4 {
177 return None;
178 }
179
180 let count_comp = &comps[prefix_len];
182 if count_comp.value.len() != 1 {
183 return None;
184 }
185 let intermediary_len = count_comp.value[0] as usize;
186
187 let inter_start = prefix_len + 1;
188 let inter_end = inter_start + intermediary_len;
189 let target_end = comps.len() - 1; if inter_end >= target_end {
192 return None; }
194
195 let nonce_comp = &comps[comps.len() - 1];
196 if nonce_comp.value.len() != 4 {
197 return None;
198 }
199 let nonce = u32::from_be_bytes(nonce_comp.value[..4].try_into().ok()?);
200
201 let intermediary = Name::from_components(comps[inter_start..inter_end].iter().cloned());
202 let target = Name::from_components(comps[inter_end..target_end].iter().cloned());
203
204 Some(IndirectProbe {
205 intermediary,
206 target,
207 nonce,
208 })
209}
210
211pub fn build_indirect_probe_encoded(intermediary: &Name, target: &Name, nonce: u32) -> Bytes {
215 let inter_len = intermediary.components().len();
216 assert!(inter_len <= 255, "intermediary name too long");
217
218 let mut w = TlvWriter::new();
219 w.write_nested(tlv_type::INTEREST, |w: &mut TlvWriter| {
220 w.write_nested(tlv_type::NAME, |w: &mut TlvWriter| {
221 for comp in probe_via().components() {
222 w.write_tlv(comp.typ, &comp.value);
223 }
224 w.write_tlv(tlv_type::NAME_COMPONENT, &[inter_len as u8]);
226 for comp in intermediary.components() {
227 w.write_tlv(comp.typ, &comp.value);
228 }
229 for comp in target.components() {
230 w.write_tlv(comp.typ, &comp.value);
231 }
232 w.write_tlv(tlv_type::NAME_COMPONENT, &nonce.to_be_bytes());
233 });
234 w.write_tlv(tlv_type::NONCE, &nonce.to_be_bytes());
235 write_nni(w, tlv_type::INTEREST_LIFETIME, 4000);
236 });
237 w.finish()
238}
239
240#[derive(Debug, Clone)]
242pub struct DirectProbe {
243 pub target: Name,
244 pub nonce: u32,
245}
246
247#[derive(Debug, Clone)]
249pub struct IndirectProbe {
250 pub intermediary: Name,
251 pub target: Name,
252 pub nonce: u32,
253}
254
255pub fn is_probe_ack(raw: &Bytes) -> bool {
257 let Some(parsed) = parse_raw_data(raw) else {
258 return false;
259 };
260 let name = &parsed.name;
261 name.has_prefix(probe_direct()) || name.has_prefix(probe_via())
262}
263
264#[cfg(test)]
267mod tests {
268 use std::str::FromStr;
269
270 use super::*;
271
272 fn n(s: &str) -> Name {
273 Name::from_str(s).unwrap()
274 }
275
276 #[test]
277 fn direct_probe_roundtrip() {
278 let target = n("/ndn/site/nodeB");
279 let nonce = 0xABCD_1234;
280 let pkt = build_direct_probe(&target, nonce);
281
282 let parsed = parse_direct_probe(&pkt).unwrap();
283 assert_eq!(parsed.target, target);
284 assert_eq!(parsed.nonce, nonce);
285 }
286
287 #[test]
288 fn indirect_probe_roundtrip() {
289 let intermediary = n("/ndn/site/nodeC");
290 let target = n("/ndn/site/nodeB");
291 let nonce = 0xDEAD_BEEF;
292 let pkt = build_indirect_probe_encoded(&intermediary, &target, nonce);
293
294 let parsed = parse_indirect_probe(&pkt).unwrap();
295 assert_eq!(parsed.intermediary, intermediary);
296 assert_eq!(parsed.target, target);
297 assert_eq!(parsed.nonce, nonce);
298 }
299
300 #[test]
301 fn probe_ack_is_detected() {
302 let probe_name = n("/ndn/local/nd/probe/direct/ndn/site/nodeB/00000001");
303 let ack = build_probe_ack(&probe_name);
304 assert!(is_probe_ack(&ack));
305 }
306
307 #[test]
308 fn direct_probe_rejects_wrong_prefix() {
309 let other = build_indirect_probe_encoded(&n("/ndn/site/c"), &n("/ndn/site/b"), 1);
310 assert!(parse_direct_probe(&other).is_none());
312 }
313}