1use crate::bip::bvlc::{BvlcFunction, BvlcHeader};
2use crate::{DataLink, DataLinkAddress, DataLinkError};
3use rustbac_core::encoding::{reader::Reader, writer::Writer};
4use std::io;
5use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
6use std::sync::Arc;
7use tokio::net::UdpSocket;
8use tokio::sync::Mutex;
9use tokio::time::{timeout, Duration, Instant};
10
11const MAX_BIP_FRAME_LEN: usize = 1600;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct BroadcastDistributionEntry {
15 pub address: SocketAddrV4,
16 pub mask: Ipv4Addr,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct ForeignDeviceTableEntry {
21 pub address: SocketAddrV4,
22 pub ttl_seconds: u16,
23 pub remaining_seconds: u16,
24}
25
26#[derive(Debug, Clone)]
27pub struct BacnetIpTransport {
28 socket: Arc<UdpSocket>,
29 bbmd: Option<SocketAddr>,
30 bbmd_command_lock: Arc<Mutex<()>>,
31}
32
33impl BacnetIpTransport {
34 pub async fn bind(bind_addr: SocketAddr) -> Result<Self, DataLinkError> {
35 let socket = UdpSocket::bind(bind_addr).await?;
36 socket.set_broadcast(true)?;
37 Ok(Self {
38 socket: Arc::new(socket),
39 bbmd: None,
40 bbmd_command_lock: Arc::new(Mutex::new(())),
41 })
42 }
43
44 pub async fn bind_foreign(
45 bind_addr: SocketAddr,
46 bbmd_addr: SocketAddr,
47 ) -> Result<Self, DataLinkError> {
48 let socket = UdpSocket::bind(bind_addr).await?;
49 socket.set_broadcast(true)?;
50 Ok(Self {
51 socket: Arc::new(socket),
52 bbmd: Some(bbmd_addr),
53 bbmd_command_lock: Arc::new(Mutex::new(())),
54 })
55 }
56
57 pub fn local_addr(&self) -> Result<SocketAddr, DataLinkError> {
58 self.socket.local_addr().map_err(DataLinkError::Io)
59 }
60
61 pub fn bbmd_addr(&self) -> Option<SocketAddr> {
62 self.bbmd
63 }
64
65 fn require_bbmd(&self) -> Result<SocketAddr, DataLinkError> {
66 self.bbmd.ok_or(DataLinkError::BbmdNotConfigured)
67 }
68
69 fn parse_bvlc_result(payload: &[u8]) -> Result<(), DataLinkError> {
70 if payload.len() < 2 {
71 return Err(DataLinkError::InvalidFrame);
72 }
73 let code = u16::from_be_bytes([payload[0], payload[1]]);
74 if code == 0 {
75 Ok(())
76 } else {
77 Err(DataLinkError::BvlcResult(code))
78 }
79 }
80
81 async fn send_bvlc_to_bbmd(
82 &self,
83 function: BvlcFunction,
84 payload: &[u8],
85 ) -> Result<(), DataLinkError> {
86 let bbmd = self.bbmd.ok_or(DataLinkError::BbmdNotConfigured)?;
87 let total_len = 4usize
88 .checked_add(payload.len())
89 .ok_or(DataLinkError::FrameTooLarge)?;
90 if total_len > usize::from(u16::MAX) {
91 return Err(DataLinkError::FrameTooLarge);
92 }
93
94 let mut frame = vec![0u8; total_len];
95 let mut w = Writer::new(&mut frame);
96 BvlcHeader {
97 function,
98 length: total_len as u16,
99 }
100 .encode(&mut w)
101 .map_err(|_| DataLinkError::InvalidFrame)?;
102 w.write_all(payload)
103 .map_err(|_| DataLinkError::InvalidFrame)?;
104
105 self.socket.send_to(w.as_written(), bbmd).await?;
106 Ok(())
107 }
108
109 async fn recv_bvlc_reply(
110 &self,
111 expected: BvlcFunction,
112 timeout_duration: Duration,
113 ) -> Result<Vec<u8>, DataLinkError> {
114 let bbmd = self.require_bbmd()?;
115 let deadline = Instant::now() + timeout_duration;
116 let mut rx = [0u8; 1600];
117 loop {
118 let remaining = deadline.saturating_duration_since(Instant::now());
119 if remaining.is_zero() {
120 return Err(DataLinkError::Io(io::Error::new(
121 io::ErrorKind::TimedOut,
122 "bbmd response timeout",
123 )));
124 }
125
126 let (n, src) = timeout(remaining, self.socket.recv_from(&mut rx))
127 .await
128 .map_err(|_| io::Error::new(io::ErrorKind::TimedOut, "bbmd response timeout"))?
129 .map_err(DataLinkError::Io)?;
130 if src != bbmd {
131 continue;
132 }
133
134 let mut r = Reader::new(&rx[..n]);
135 let hdr = BvlcHeader::decode(&mut r).map_err(|_| DataLinkError::InvalidFrame)?;
136 let payload = r
137 .read_exact(hdr.length as usize - 4)
138 .map_err(|_| DataLinkError::InvalidFrame)?;
139
140 if hdr.function == expected {
141 return Ok(payload.to_vec());
142 }
143
144 if hdr.function == BvlcFunction::Result {
145 Self::parse_bvlc_result(payload)?;
146 if expected == BvlcFunction::Result {
147 return Ok(payload.to_vec());
148 }
149 return Err(DataLinkError::InvalidFrame);
150 }
151 }
152 }
153
154 pub async fn register_foreign_device_no_wait(
155 &self,
156 ttl_seconds: u16,
157 ) -> Result<(), DataLinkError> {
158 let _guard = self.bbmd_command_lock.lock().await;
159 let payload = ttl_seconds.to_be_bytes();
160 self.send_bvlc_to_bbmd(BvlcFunction::RegisterForeignDevice, &payload)
161 .await
162 }
163
164 pub async fn register_foreign_device(&self, ttl_seconds: u16) -> Result<(), DataLinkError> {
165 let _guard = self.bbmd_command_lock.lock().await;
166 let payload = ttl_seconds.to_be_bytes();
167 self.send_bvlc_to_bbmd(BvlcFunction::RegisterForeignDevice, &payload)
168 .await?;
169 let payload = self
170 .recv_bvlc_reply(BvlcFunction::Result, Duration::from_secs(2))
171 .await?;
172 Self::parse_bvlc_result(&payload)
173 }
174
175 pub async fn read_broadcast_distribution_table(
176 &self,
177 ) -> Result<Vec<BroadcastDistributionEntry>, DataLinkError> {
178 let _guard = self.bbmd_command_lock.lock().await;
179 self.send_bvlc_to_bbmd(BvlcFunction::ReadBroadcastDistributionTable, &[])
180 .await?;
181 let payload = self
182 .recv_bvlc_reply(
183 BvlcFunction::ReadBroadcastDistributionTableAck,
184 Duration::from_secs(2),
185 )
186 .await?;
187 if payload.len() % 10 != 0 {
188 return Err(DataLinkError::InvalidFrame);
189 }
190
191 let mut out = Vec::with_capacity(payload.len() / 10);
192 for chunk in payload.chunks_exact(10) {
193 out.push(BroadcastDistributionEntry {
194 address: SocketAddrV4::new(
195 Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]),
196 u16::from_be_bytes([chunk[4], chunk[5]]),
197 ),
198 mask: Ipv4Addr::new(chunk[6], chunk[7], chunk[8], chunk[9]),
199 });
200 }
201 Ok(out)
202 }
203
204 pub async fn write_broadcast_distribution_table(
205 &self,
206 entries: &[BroadcastDistributionEntry],
207 ) -> Result<(), DataLinkError> {
208 let _guard = self.bbmd_command_lock.lock().await;
209 let mut payload = Vec::with_capacity(entries.len() * 10);
210 for entry in entries {
211 payload.extend_from_slice(&entry.address.ip().octets());
212 payload.extend_from_slice(&entry.address.port().to_be_bytes());
213 payload.extend_from_slice(&entry.mask.octets());
214 }
215
216 self.send_bvlc_to_bbmd(BvlcFunction::WriteBroadcastDistributionTable, &payload)
217 .await?;
218 let payload = self
219 .recv_bvlc_reply(BvlcFunction::Result, Duration::from_secs(2))
220 .await?;
221 Self::parse_bvlc_result(&payload)
222 }
223
224 pub async fn read_foreign_device_table(
225 &self,
226 ) -> Result<Vec<ForeignDeviceTableEntry>, DataLinkError> {
227 let _guard = self.bbmd_command_lock.lock().await;
228 self.send_bvlc_to_bbmd(BvlcFunction::ReadForeignDeviceTable, &[])
229 .await?;
230 let payload = self
231 .recv_bvlc_reply(
232 BvlcFunction::ReadForeignDeviceTableAck,
233 Duration::from_secs(2),
234 )
235 .await?;
236 if payload.len() % 10 != 0 {
237 return Err(DataLinkError::InvalidFrame);
238 }
239
240 let mut out = Vec::with_capacity(payload.len() / 10);
241 for chunk in payload.chunks_exact(10) {
242 out.push(ForeignDeviceTableEntry {
243 address: SocketAddrV4::new(
244 Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]),
245 u16::from_be_bytes([chunk[4], chunk[5]]),
246 ),
247 ttl_seconds: u16::from_be_bytes([chunk[6], chunk[7]]),
248 remaining_seconds: u16::from_be_bytes([chunk[8], chunk[9]]),
249 });
250 }
251 Ok(out)
252 }
253
254 pub async fn delete_foreign_device_table_entry(
255 &self,
256 address: SocketAddrV4,
257 ) -> Result<(), DataLinkError> {
258 let _guard = self.bbmd_command_lock.lock().await;
259 let mut payload = [0u8; 6];
260 payload[..4].copy_from_slice(&address.ip().octets());
261 payload[4..].copy_from_slice(&address.port().to_be_bytes());
262 self.send_bvlc_to_bbmd(BvlcFunction::DeleteForeignDeviceTableEntry, &payload)
263 .await?;
264 let payload = self
265 .recv_bvlc_reply(BvlcFunction::Result, Duration::from_secs(2))
266 .await?;
267 Self::parse_bvlc_result(&payload)
268 }
269}
270
271impl DataLink for BacnetIpTransport {
272 async fn send(&self, address: DataLinkAddress, payload: &[u8]) -> Result<(), DataLinkError> {
273 let addr = address.as_socket_addr();
274 let is_broadcast = matches!(addr.ip(), IpAddr::V4(v4) if v4.is_broadcast());
275
276 let (function, target_addr) = if is_broadcast {
277 if let Some(bbmd) = self.bbmd {
278 (BvlcFunction::DistributeBroadcastToNetwork, bbmd)
279 } else {
280 (BvlcFunction::OriginalBroadcastNpdu, addr)
281 }
282 } else {
283 (BvlcFunction::OriginalUnicastNpdu, addr)
284 };
285
286 let mut frame = [0u8; MAX_BIP_FRAME_LEN];
287 let total_len = 4usize
288 .checked_add(payload.len())
289 .ok_or(DataLinkError::FrameTooLarge)?;
290 if total_len > frame.len() {
291 return Err(DataLinkError::FrameTooLarge);
292 }
293
294 let mut w = Writer::new(&mut frame);
295 BvlcHeader {
296 function,
297 length: total_len as u16,
298 }
299 .encode(&mut w)
300 .map_err(|_| DataLinkError::InvalidFrame)?;
301 w.write_all(payload)
302 .map_err(|_| DataLinkError::FrameTooLarge)?;
303
304 self.socket.send_to(w.as_written(), target_addr).await?;
305 Ok(())
306 }
307
308 async fn recv(&self, buf: &mut [u8]) -> Result<(usize, DataLinkAddress), DataLinkError> {
309 let mut frame = [0u8; MAX_BIP_FRAME_LEN];
310 let (n, src) = self.socket.recv_from(&mut frame).await?;
311 let mut r = Reader::new(&frame[..n]);
312 let hdr = BvlcHeader::decode(&mut r).map_err(|_| DataLinkError::InvalidFrame)?;
313
314 match hdr.function {
315 BvlcFunction::OriginalUnicastNpdu
316 | BvlcFunction::OriginalBroadcastNpdu
317 | BvlcFunction::DistributeBroadcastToNetwork => {
318 let payload_len = hdr.length as usize - 4;
319 let payload = r
320 .read_exact(payload_len)
321 .map_err(|_| DataLinkError::InvalidFrame)?;
322 if payload.len() > buf.len() {
323 return Err(DataLinkError::FrameTooLarge);
324 }
325 buf[..payload.len()].copy_from_slice(payload);
326 Ok((payload.len(), DataLinkAddress::Ip(src)))
327 }
328 BvlcFunction::ForwardedNpdu => {
329 let forwarded = r
330 .read_exact(hdr.length as usize - 4)
331 .map_err(|_| DataLinkError::InvalidFrame)?;
332 if forwarded.len() < 6 {
333 return Err(DataLinkError::InvalidFrame);
334 }
335 let origin_ip =
336 Ipv4Addr::new(forwarded[0], forwarded[1], forwarded[2], forwarded[3]);
337 let origin_port = u16::from_be_bytes([forwarded[4], forwarded[5]]);
338 let payload = &forwarded[6..];
339 if payload.len() > buf.len() {
340 return Err(DataLinkError::FrameTooLarge);
341 }
342 buf[..payload.len()].copy_from_slice(payload);
343 Ok((
344 payload.len(),
345 DataLinkAddress::Ip(SocketAddr::new(IpAddr::V4(origin_ip), origin_port)),
346 ))
347 }
348 BvlcFunction::Unknown(v) => Err(DataLinkError::UnsupportedBvlcFunction(v)),
349 _ => Err(DataLinkError::InvalidFrame),
350 }
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use super::{BacnetIpTransport, BroadcastDistributionEntry, ForeignDeviceTableEntry};
357 use crate::bip::bvlc::{BvlcFunction, BvlcHeader, BVLC_TYPE_BIP};
358 use crate::{DataLink, DataLinkAddress, DataLinkError};
359 use rustbac_core::encoding::{reader::Reader, writer::Writer};
360 use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
361 use tokio::net::UdpSocket;
362 use tokio::time::{timeout, Duration};
363
364 #[tokio::test]
365 async fn recv_forwarded_npdu_returns_forwarded_origin() {
366 let transport =
367 BacnetIpTransport::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
368 .await
369 .unwrap();
370 let target = transport.local_addr().unwrap();
371 let sender = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
372 .await
373 .unwrap();
374
375 let mut frame = [0u8; 64];
376 let mut w = Writer::new(&mut frame);
377 BvlcHeader {
378 function: BvlcFunction::ForwardedNpdu,
379 length: 4 + 6 + 3,
380 }
381 .encode(&mut w)
382 .unwrap();
383 w.write_all(&[10, 1, 2, 3]).unwrap();
384 w.write_be_u16(47808).unwrap();
385 w.write_all(&[1, 2, 3]).unwrap();
386
387 sender.send_to(w.as_written(), target).await.unwrap();
388
389 let mut out = [0u8; 16];
390 let (n, src) = transport.recv(&mut out).await.unwrap();
391 assert_eq!(n, 3);
392 assert_eq!(&out[..3], &[1, 2, 3]);
393 assert_eq!(
394 src,
395 DataLinkAddress::Ip(SocketAddr::new(
396 IpAddr::V4(Ipv4Addr::new(10, 1, 2, 3)),
397 47808
398 ))
399 );
400 }
401
402 #[tokio::test]
403 async fn register_foreign_device_success() {
404 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
405 .await
406 .unwrap();
407 let bbmd_addr = bbmd.local_addr().unwrap();
408
409 let transport = BacnetIpTransport::bind_foreign(
410 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
411 bbmd_addr,
412 )
413 .await
414 .unwrap();
415
416 let responder = tokio::spawn(async move {
417 let mut recv = [0u8; 64];
418 let (n, src) = bbmd.recv_from(&mut recv).await.unwrap();
419 let mut r = Reader::new(&recv[..n]);
420 let hdr = BvlcHeader::decode(&mut r).unwrap();
421 assert_eq!(hdr.function, BvlcFunction::RegisterForeignDevice);
422
423 let reply = [BVLC_TYPE_BIP, 0x00, 0x00, 0x06, 0x00, 0x00];
424 bbmd.send_to(&reply, src).await.unwrap();
425 });
426
427 transport.register_foreign_device(60).await.unwrap();
428 responder.await.unwrap();
429 }
430
431 #[tokio::test]
432 async fn register_foreign_device_no_wait_sends_ttl() {
433 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
434 .await
435 .unwrap();
436 let bbmd_addr = bbmd.local_addr().unwrap();
437 let transport = BacnetIpTransport::bind_foreign(
438 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
439 bbmd_addr,
440 )
441 .await
442 .unwrap();
443
444 transport.register_foreign_device_no_wait(90).await.unwrap();
445
446 let mut recv = [0u8; 64];
447 let (n, _) = bbmd.recv_from(&mut recv).await.unwrap();
448 let mut r = Reader::new(&recv[..n]);
449 let hdr = BvlcHeader::decode(&mut r).unwrap();
450 assert_eq!(hdr.function, BvlcFunction::RegisterForeignDevice);
451 assert_eq!(r.read_be_u16().unwrap(), 90);
452 }
453
454 #[tokio::test]
455 async fn read_broadcast_distribution_table_parses_entries() {
456 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
457 .await
458 .unwrap();
459 let bbmd_addr = bbmd.local_addr().unwrap();
460 let transport = BacnetIpTransport::bind_foreign(
461 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
462 bbmd_addr,
463 )
464 .await
465 .unwrap();
466
467 let responder = tokio::spawn(async move {
468 let mut recv = [0u8; 128];
469 let (n, src) = bbmd.recv_from(&mut recv).await.unwrap();
470 let mut r = Reader::new(&recv[..n]);
471 let hdr = BvlcHeader::decode(&mut r).unwrap();
472 assert_eq!(hdr.function, BvlcFunction::ReadBroadcastDistributionTable);
473
474 let mut reply = [0u8; 32];
475 let mut w = Writer::new(&mut reply);
476 BvlcHeader {
477 function: BvlcFunction::ReadBroadcastDistributionTableAck,
478 length: 14,
479 }
480 .encode(&mut w)
481 .unwrap();
482 w.write_all(&[192, 168, 10, 20]).unwrap();
483 w.write_be_u16(47808).unwrap();
484 w.write_all(&[255, 255, 255, 0]).unwrap();
485 bbmd.send_to(w.as_written(), src).await.unwrap();
486 });
487
488 let entries = transport.read_broadcast_distribution_table().await.unwrap();
489 assert_eq!(
490 entries,
491 vec![BroadcastDistributionEntry {
492 address: SocketAddrV4::new(Ipv4Addr::new(192, 168, 10, 20), 47808),
493 mask: Ipv4Addr::new(255, 255, 255, 0),
494 }]
495 );
496 responder.await.unwrap();
497 }
498
499 #[tokio::test]
500 async fn write_broadcast_distribution_table_sends_entries() {
501 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
502 .await
503 .unwrap();
504 let bbmd_addr = bbmd.local_addr().unwrap();
505 let transport = BacnetIpTransport::bind_foreign(
506 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
507 bbmd_addr,
508 )
509 .await
510 .unwrap();
511
512 let responder = tokio::spawn(async move {
513 let mut recv = [0u8; 128];
514 let (n, src) = bbmd.recv_from(&mut recv).await.unwrap();
515 let mut r = Reader::new(&recv[..n]);
516 let hdr = BvlcHeader::decode(&mut r).unwrap();
517 assert_eq!(hdr.function, BvlcFunction::WriteBroadcastDistributionTable);
518 let payload = r.read_exact(hdr.length as usize - 4).unwrap();
519 assert_eq!(payload, &[10, 1, 2, 3, 0xBA, 0xC0, 255, 255, 255, 0][..]);
520
521 let reply = [BVLC_TYPE_BIP, 0x00, 0x00, 0x06, 0x00, 0x00];
522 bbmd.send_to(&reply, src).await.unwrap();
523 });
524
525 let entries = [BroadcastDistributionEntry {
526 address: SocketAddrV4::new(Ipv4Addr::new(10, 1, 2, 3), 47808),
527 mask: Ipv4Addr::new(255, 255, 255, 0),
528 }];
529 transport
530 .write_broadcast_distribution_table(&entries)
531 .await
532 .unwrap();
533 responder.await.unwrap();
534 }
535
536 #[tokio::test]
537 async fn read_foreign_device_table_parses_entries() {
538 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
539 .await
540 .unwrap();
541 let bbmd_addr = bbmd.local_addr().unwrap();
542 let transport = BacnetIpTransport::bind_foreign(
543 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
544 bbmd_addr,
545 )
546 .await
547 .unwrap();
548
549 let responder = tokio::spawn(async move {
550 let mut recv = [0u8; 128];
551 let (n, src) = bbmd.recv_from(&mut recv).await.unwrap();
552 let mut r = Reader::new(&recv[..n]);
553 let hdr = BvlcHeader::decode(&mut r).unwrap();
554 assert_eq!(hdr.function, BvlcFunction::ReadForeignDeviceTable);
555
556 let mut reply = [0u8; 32];
557 let mut w = Writer::new(&mut reply);
558 BvlcHeader {
559 function: BvlcFunction::ReadForeignDeviceTableAck,
560 length: 14,
561 }
562 .encode(&mut w)
563 .unwrap();
564 w.write_all(&[172, 16, 0, 42]).unwrap();
565 w.write_be_u16(47808).unwrap();
566 w.write_be_u16(120).unwrap();
567 w.write_be_u16(90).unwrap();
568 bbmd.send_to(w.as_written(), src).await.unwrap();
569 });
570
571 let entries = transport.read_foreign_device_table().await.unwrap();
572 assert_eq!(
573 entries,
574 vec![ForeignDeviceTableEntry {
575 address: SocketAddrV4::new(Ipv4Addr::new(172, 16, 0, 42), 47808),
576 ttl_seconds: 120,
577 remaining_seconds: 90,
578 }]
579 );
580 responder.await.unwrap();
581 }
582
583 #[tokio::test]
584 async fn delete_foreign_device_table_entry_sends_target() {
585 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
586 .await
587 .unwrap();
588 let bbmd_addr = bbmd.local_addr().unwrap();
589 let transport = BacnetIpTransport::bind_foreign(
590 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
591 bbmd_addr,
592 )
593 .await
594 .unwrap();
595
596 let responder = tokio::spawn(async move {
597 let mut recv = [0u8; 128];
598 let (n, src) = bbmd.recv_from(&mut recv).await.unwrap();
599 let mut r = Reader::new(&recv[..n]);
600 let hdr = BvlcHeader::decode(&mut r).unwrap();
601 assert_eq!(hdr.function, BvlcFunction::DeleteForeignDeviceTableEntry);
602 let payload = r.read_exact(hdr.length as usize - 4).unwrap();
603 assert_eq!(payload, &[10, 20, 30, 40, 0xBA, 0xC0][..]);
604
605 let reply = [BVLC_TYPE_BIP, 0x00, 0x00, 0x06, 0x00, 0x00];
606 bbmd.send_to(&reply, src).await.unwrap();
607 });
608
609 transport
610 .delete_foreign_device_table_entry(SocketAddrV4::new(
611 Ipv4Addr::new(10, 20, 30, 40),
612 47808,
613 ))
614 .await
615 .unwrap();
616 responder.await.unwrap();
617 }
618
619 #[tokio::test]
620 async fn broadcast_uses_distribute_to_network_when_bbmd_configured() {
621 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
622 .await
623 .unwrap();
624 let bbmd_addr = bbmd.local_addr().unwrap();
625
626 let transport = BacnetIpTransport::bind_foreign(
627 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
628 bbmd_addr,
629 )
630 .await
631 .unwrap();
632
633 transport
634 .send(DataLinkAddress::local_broadcast(47808), &[1, 2, 3])
635 .await
636 .unwrap();
637
638 let mut recv = [0u8; 64];
639 let (n, _) = bbmd.recv_from(&mut recv).await.unwrap();
640 let mut r = Reader::new(&recv[..n]);
641 let hdr = BvlcHeader::decode(&mut r).unwrap();
642 assert_eq!(hdr.function, BvlcFunction::DistributeBroadcastToNetwork);
643 }
644
645 #[tokio::test]
646 async fn bbmd_admin_commands_are_serialized() {
647 let bbmd = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
648 .await
649 .unwrap();
650 let bbmd_addr = bbmd.local_addr().unwrap();
651 let transport = BacnetIpTransport::bind_foreign(
652 SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
653 bbmd_addr,
654 )
655 .await
656 .unwrap();
657
658 let t1 = transport.clone();
659 let t2 = transport.clone();
660
661 let first = tokio::spawn(async move { t1.read_broadcast_distribution_table().await });
662 let second = tokio::spawn(async move { t2.read_foreign_device_table().await });
663
664 let mut recv = [0u8; 128];
665 let (n1, src1) = bbmd.recv_from(&mut recv).await.unwrap();
666 let mut r1 = Reader::new(&recv[..n1]);
667 let hdr1 = BvlcHeader::decode(&mut r1).unwrap();
668 assert_eq!(hdr1.function, BvlcFunction::ReadBroadcastDistributionTable);
669
670 let no_second_yet = timeout(Duration::from_millis(100), bbmd.recv_from(&mut recv)).await;
672 assert!(no_second_yet.is_err());
673
674 let mut reply1 = [0u8; 14];
675 let mut w1 = Writer::new(&mut reply1);
676 BvlcHeader {
677 function: BvlcFunction::ReadBroadcastDistributionTableAck,
678 length: 14,
679 }
680 .encode(&mut w1)
681 .unwrap();
682 w1.write_all(&[192, 168, 1, 1]).unwrap();
683 w1.write_be_u16(47808).unwrap();
684 w1.write_all(&[255, 255, 255, 0]).unwrap();
685 bbmd.send_to(w1.as_written(), src1).await.unwrap();
686
687 let (n2, src2) = bbmd.recv_from(&mut recv).await.unwrap();
688 let mut r2 = Reader::new(&recv[..n2]);
689 let hdr2 = BvlcHeader::decode(&mut r2).unwrap();
690 assert_eq!(hdr2.function, BvlcFunction::ReadForeignDeviceTable);
691
692 let mut reply2 = [0u8; 14];
693 let mut w2 = Writer::new(&mut reply2);
694 BvlcHeader {
695 function: BvlcFunction::ReadForeignDeviceTableAck,
696 length: 14,
697 }
698 .encode(&mut w2)
699 .unwrap();
700 w2.write_all(&[10, 0, 0, 2]).unwrap();
701 w2.write_be_u16(47808).unwrap();
702 w2.write_be_u16(60).unwrap();
703 w2.write_be_u16(30).unwrap();
704 bbmd.send_to(w2.as_written(), src2).await.unwrap();
705
706 let first_entries = first.await.unwrap().unwrap();
707 let second_entries = second.await.unwrap().unwrap();
708 assert_eq!(first_entries.len(), 1);
709 assert_eq!(second_entries.len(), 1);
710 }
711
712 #[tokio::test]
713 async fn unknown_bvlc_function_errors() {
714 let transport =
715 BacnetIpTransport::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
716 .await
717 .unwrap();
718 let target = transport.local_addr().unwrap();
719 let sender = UdpSocket::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))
720 .await
721 .unwrap();
722
723 let frame = [BVLC_TYPE_BIP, 0x99, 0x00, 0x04];
724 sender.send_to(&frame, target).await.unwrap();
725
726 let mut out = [0u8; 16];
727 let err = transport.recv(&mut out).await.unwrap_err();
728 assert!(matches!(err, DataLinkError::UnsupportedBvlcFunction(0x99)));
729 }
730}