ndn_discovery/hello/
payload.rs1use bytes::Bytes;
38use ndn_packet::Name;
39use ndn_tlv::{TlvReader, TlvWriter};
40
41use crate::wire::{parse_name_from_tlv, write_name_tlv};
42
43pub const T_NODE_NAME: u64 = 0xC1;
47pub const T_SERVED_PREFIX: u64 = 0xC2;
49pub const T_CAPABILITIES: u64 = 0xC3;
51pub const T_NEIGHBOR_DIFF: u64 = 0xC4;
53pub const T_ADD_ENTRY: u64 = 0xC5;
55pub const T_REMOVE_ENTRY: u64 = 0xC6;
57pub const T_PUBLIC_KEY: u64 = 0xC8;
59pub const T_UNICAST_PORT: u64 = 0xC9;
65
66pub const CAP_FRAGMENTATION: u8 = 0x01;
70pub const CAP_CONTENT_STORE: u8 = 0x02;
72pub const CAP_VALIDATION: u8 = 0x04;
74pub const CAP_SVS: u8 = 0x08;
76
77#[derive(Clone, Debug, PartialEq, Eq)]
81pub enum DiffEntry {
82 Add(Name),
83 Remove(Name),
84}
85
86#[derive(Clone, Debug, Default)]
93pub struct NeighborDiff {
94 pub entries: Vec<DiffEntry>,
95}
96
97#[derive(Clone, Debug)]
101pub struct HelloPayload {
102 pub node_name: Name,
104 pub served_prefixes: Vec<Name>,
106 pub capabilities: u8,
108 pub neighbor_diffs: Vec<NeighborDiff>,
110 pub public_key: Option<Bytes>,
114 pub unicast_port: Option<u16>,
118}
119
120impl HelloPayload {
121 pub fn new(node_name: Name) -> Self {
123 Self {
124 node_name,
125 served_prefixes: Vec::new(),
126 capabilities: 0,
127 neighbor_diffs: Vec::new(),
128 public_key: None,
129 unicast_port: None,
130 }
131 }
132
133 pub fn encode(&self) -> Bytes {
137 let mut w = TlvWriter::new();
138 w.write_nested(T_NODE_NAME, |w: &mut TlvWriter| {
140 write_name_tlv(w, &self.node_name);
141 });
142 for prefix in &self.served_prefixes {
144 w.write_nested(T_SERVED_PREFIX, |w: &mut TlvWriter| {
145 write_name_tlv(w, prefix);
146 });
147 }
148 if self.capabilities != 0 {
150 w.write_tlv(T_CAPABILITIES, &[self.capabilities]);
151 }
152 for diff in &self.neighbor_diffs {
154 w.write_nested(T_NEIGHBOR_DIFF, |w: &mut TlvWriter| {
155 for entry in &diff.entries {
156 match entry {
157 DiffEntry::Add(name) => {
158 w.write_nested(T_ADD_ENTRY, |w: &mut TlvWriter| {
159 write_name_tlv(w, name);
160 });
161 }
162 DiffEntry::Remove(name) => {
163 w.write_nested(T_REMOVE_ENTRY, |w: &mut TlvWriter| {
164 write_name_tlv(w, name);
165 });
166 }
167 }
168 }
169 });
170 }
171 if let Some(ref pk) = self.public_key {
173 w.write_tlv(T_PUBLIC_KEY, pk);
174 }
175 if let Some(port) = self.unicast_port {
177 w.write_tlv(T_UNICAST_PORT, &port.to_be_bytes());
178 }
179 w.finish()
180 }
181
182 pub fn decode(content: &Bytes) -> Option<Self> {
189 let mut r = TlvReader::new(content.clone());
190 let mut node_name: Option<Name> = None;
191 let mut served_prefixes = Vec::new();
192 let mut capabilities: u8 = 0;
193 let mut neighbor_diffs = Vec::new();
194 let mut public_key: Option<Bytes> = None;
195 let mut unicast_port: Option<u16> = None;
196
197 while !r.is_empty() {
198 let (t, v) = r.read_tlv().ok()?;
199 match t {
200 T_NODE_NAME => {
201 node_name = Some(decode_name_from_nested(&v)?);
202 }
203 T_SERVED_PREFIX => {
204 if let Some(name) = decode_name_from_nested(&v) {
205 served_prefixes.push(name);
206 }
207 }
208 T_CAPABILITIES => {
209 capabilities = *v.first().unwrap_or(&0);
210 }
211 T_NEIGHBOR_DIFF => {
212 if let Some(diff) = decode_neighbor_diff(&v) {
213 neighbor_diffs.push(diff);
214 }
215 }
216 T_PUBLIC_KEY => {
217 if v.len() == 32 {
218 public_key = Some(v);
219 }
220 }
221 T_UNICAST_PORT => {
222 if v.len() == 2 {
223 unicast_port = Some(u16::from_be_bytes([v[0], v[1]]));
224 }
225 }
226 _ => {} }
228 }
229
230 Some(HelloPayload {
231 node_name: node_name?,
232 served_prefixes,
233 capabilities,
234 neighbor_diffs,
235 public_key,
236 unicast_port,
237 })
238 }
239}
240
241fn decode_name_from_nested(v: &Bytes) -> Option<Name> {
246 parse_name_from_tlv(v)
247}
248
249fn decode_neighbor_diff(v: &Bytes) -> Option<NeighborDiff> {
250 let mut r = TlvReader::new(v.clone());
251 let mut entries = Vec::new();
252 while !r.is_empty() {
253 let (t, val) = r.read_tlv().ok()?;
254 match t {
255 T_ADD_ENTRY => {
256 if let Some(name) = decode_name_from_nested(&val) {
257 entries.push(DiffEntry::Add(name));
258 }
259 }
260 T_REMOVE_ENTRY => {
261 if let Some(name) = decode_name_from_nested(&val) {
262 entries.push(DiffEntry::Remove(name));
263 }
264 }
265 _ => {}
266 }
267 }
268 Some(NeighborDiff { entries })
269}
270
271#[cfg(test)]
274mod tests {
275 use super::*;
276 use std::str::FromStr;
277
278 fn name(s: &str) -> Name {
279 Name::from_str(s).unwrap()
280 }
281
282 #[test]
283 fn minimal_roundtrip() {
284 let payload = HelloPayload::new(name("/ndn/test/node"));
285 let wire = payload.encode();
286 let decoded = HelloPayload::decode(&wire).unwrap();
287 assert_eq!(decoded.node_name, name("/ndn/test/node"));
288 assert!(decoded.served_prefixes.is_empty());
289 assert_eq!(decoded.capabilities, 0);
290 assert!(decoded.neighbor_diffs.is_empty());
291 }
292
293 #[test]
294 fn served_prefixes_roundtrip() {
295 let mut payload = HelloPayload::new(name("/ndn/site/router"));
296 payload.served_prefixes.push(name("/ndn/edu/ucla/cs"));
297 payload.served_prefixes.push(name("/ndn/edu/ucla/math"));
298 let wire = payload.encode();
299 let decoded = HelloPayload::decode(&wire).unwrap();
300 assert_eq!(decoded.served_prefixes.len(), 2);
301 assert_eq!(decoded.served_prefixes[0], name("/ndn/edu/ucla/cs"));
302 assert_eq!(decoded.served_prefixes[1], name("/ndn/edu/ucla/math"));
303 }
304
305 #[test]
306 fn capabilities_roundtrip() {
307 let mut payload = HelloPayload::new(name("/ndn/test/node"));
308 payload.capabilities = CAP_CONTENT_STORE | CAP_SVS;
309 let wire = payload.encode();
310 let decoded = HelloPayload::decode(&wire).unwrap();
311 assert_eq!(decoded.capabilities, CAP_CONTENT_STORE | CAP_SVS);
312 }
313
314 #[test]
315 fn capabilities_zero_omitted() {
316 let payload = HelloPayload::new(name("/ndn/test/node"));
317 let wire = payload.encode();
318 assert!(!wire.windows(1).any(|b| b[0] == T_CAPABILITIES as u8));
320 }
321
322 #[test]
323 fn neighbor_diff_roundtrip() {
324 let mut payload = HelloPayload::new(name("/ndn/test/node"));
325 payload.neighbor_diffs.push(NeighborDiff {
326 entries: vec![
327 DiffEntry::Add(name("/ndn/site/peerA")),
328 DiffEntry::Remove(name("/ndn/site/peerB")),
329 ],
330 });
331 let wire = payload.encode();
332 let decoded = HelloPayload::decode(&wire).unwrap();
333 assert_eq!(decoded.neighbor_diffs.len(), 1);
334 let diff = &decoded.neighbor_diffs[0];
335 assert_eq!(diff.entries.len(), 2);
336 assert_eq!(diff.entries[0], DiffEntry::Add(name("/ndn/site/peerA")));
337 assert_eq!(diff.entries[1], DiffEntry::Remove(name("/ndn/site/peerB")));
338 }
339
340 #[test]
341 fn unknown_tlv_types_skipped() {
342 let mut payload = HelloPayload::new(name("/ndn/test/node"));
344 payload.capabilities = CAP_FRAGMENTATION;
345 let mut wire = payload.encode().to_vec();
346 wire.extend_from_slice(&[0xD0, 0x02, 0xDE, 0xAD]);
348 let bytes = Bytes::from(wire);
349 let decoded = HelloPayload::decode(&bytes).unwrap();
351 assert_eq!(decoded.node_name, name("/ndn/test/node"));
352 assert_eq!(decoded.capabilities, CAP_FRAGMENTATION);
353 }
354
355 #[test]
356 fn missing_node_name_returns_none() {
357 let mut w = TlvWriter::new();
359 w.write_tlv(T_CAPABILITIES, &[CAP_SVS]);
360 let wire = w.finish();
361 assert!(HelloPayload::decode(&wire).is_none());
362 }
363}