bacnet_bip_codec/ipv6/address.rs
1//! B/IPv6 addresses and virtual MAC addresses.
2
3use core::fmt;
4use core::net::{Ipv6Addr, SocketAddrV6};
5
6use super::{Bvll6Error, BIP6_ADDRESS_LENGTH, VMAC_LENGTH};
7
8/// Octets in the IPv6 address at the front of a B/IPv6 address.
9const IPV6_LENGTH: usize = 16;
10
11/// A B/IPv6 address: a 16-octet IPv6 address, then a 2-octet UDP port, both
12/// most significant octet first (Annex U.1.1.2).
13///
14/// It holds exactly the 18 octets that travel. A [`SocketAddrV6`] also has a
15/// flow label and a scope ID, neither of which a B/IPv6 address carries:
16/// converting from one drops them, and converting to one sets both to zero,
17/// so the receiver of a link-local address picks its scope.
18///
19/// ```
20/// use core::net::Ipv6Addr;
21/// use bacnet_bip_codec::ipv6::Bip6Address;
22///
23/// let address = Bip6Address::new(Ipv6Addr::new(0x2001, 0xDB8, 0, 0, 0, 0, 0, 1), 47808);
24/// let octets = address.to_octets();
25/// assert_eq!(octets[..4], [0x20, 0x01, 0x0D, 0xB8]);
26/// assert_eq!(octets[16..], [0xBA, 0xC0]);
27/// assert_eq!(Bip6Address::from_octets(octets), address);
28/// ```
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub struct Bip6Address {
31 /// The IPv6 address.
32 pub ip: Ipv6Addr,
33 /// The UDP port.
34 pub port: u16,
35}
36
37impl Bip6Address {
38 /// Construct an address.
39 #[must_use]
40 pub const fn new(ip: Ipv6Addr, port: u16) -> Self {
41 Self { ip, port }
42 }
43
44 /// The address's 18 wire octets: the IPv6 address, then the port.
45 #[must_use]
46 pub const fn to_octets(self) -> [u8; BIP6_ADDRESS_LENGTH] {
47 let mut octets = [0; BIP6_ADDRESS_LENGTH];
48 let (ip, port) = octets.split_at_mut(IPV6_LENGTH);
49 ip.copy_from_slice(&self.ip.octets());
50 port.copy_from_slice(&self.port.to_be_bytes());
51 octets
52 }
53
54 /// The address 18 wire octets hold.
55 #[must_use]
56 pub const fn from_octets(octets: [u8; BIP6_ADDRESS_LENGTH]) -> Self {
57 let mut ip = [0; IPV6_LENGTH];
58 ip.copy_from_slice(octets.split_at(IPV6_LENGTH).0);
59 Self {
60 ip: Ipv6Addr::from_octets(ip),
61 port: u16::from_be_bytes([octets[IPV6_LENGTH], octets[IPV6_LENGTH + 1]]),
62 }
63 }
64
65 /// The address in `octets`, which must be exactly 18 octets long.
66 pub fn from_slice(octets: &[u8]) -> Result<Self, Bvll6Error> {
67 match <[u8; BIP6_ADDRESS_LENGTH]>::try_from(octets) {
68 Ok(octets) => Ok(Self::from_octets(octets)),
69 Err(_) => Err(Bvll6Error::AddressLength {
70 length: octets.len(),
71 }),
72 }
73 }
74}
75
76/// Keeps the IPv6 address and port; the flow label and scope ID don't
77/// travel, so they are dropped.
78impl From<SocketAddrV6> for Bip6Address {
79 fn from(address: SocketAddrV6) -> Self {
80 Self::new(*address.ip(), address.port())
81 }
82}
83
84/// A socket address with flow label 0 and scope ID 0.
85impl From<Bip6Address> for SocketAddrV6 {
86 fn from(address: Bip6Address) -> Self {
87 SocketAddrV6::new(address.ip, address.port, 0, 0)
88 }
89}
90
91/// `[ip]:port`, the notation of RFC 5952 that Annex U.1.1.1.1 uses.
92impl fmt::Display for Bip6Address {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "[{}]:{}", self.ip, self.port)
95 }
96}
97
98/// A B/IPv6 virtual MAC address (VMAC): three octets, which the network
99/// layer uses as the node's MAC address (Annex U.1.3).
100///
101/// A node with a device instance uses that instance, high octet first; one
102/// without generates a random-instance VMAC instead (H.7.2). The codec
103/// carries any three octets and doesn't judge which kind they are.
104///
105/// ```
106/// use bacnet_bip_codec::ipv6::Bip6Vmac;
107///
108/// // Device instance 1234 (0x0004D2).
109/// let vmac = Bip6Vmac::new([0x00, 0x04, 0xD2]);
110/// assert_eq!(vmac.octets(), [0x00, 0x04, 0xD2]);
111/// assert_eq!(Bip6Vmac::from([0x00, 0x04, 0xD2]), vmac);
112/// ```
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
114pub struct Bip6Vmac([u8; VMAC_LENGTH]);
115
116impl Bip6Vmac {
117 /// The VMAC these three octets spell.
118 #[must_use]
119 pub const fn new(octets: [u8; VMAC_LENGTH]) -> Self {
120 Self(octets)
121 }
122
123 /// The three wire octets.
124 #[must_use]
125 pub const fn octets(self) -> [u8; VMAC_LENGTH] {
126 self.0
127 }
128}
129
130impl From<[u8; VMAC_LENGTH]> for Bip6Vmac {
131 fn from(octets: [u8; VMAC_LENGTH]) -> Self {
132 Self(octets)
133 }
134}
135
136impl From<Bip6Vmac> for [u8; VMAC_LENGTH] {
137 fn from(vmac: Bip6Vmac) -> Self {
138 vmac.0
139 }
140}