1use super::*;
11
12impl<T: TransportPort + 'static> ServerBuilder<T> {
13 pub fn registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
19 self.config.registered_network_port = Some(oid);
20 self
21 }
22}
23impl BipServerBuilder {
24 pub fn registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
26 self.config.registered_network_port = Some(oid);
27 self
28 }
29}
30
31pub(super) fn prepare<T: TransportPort>(
32 db: &mut ObjectDatabase,
33 transport: &mut T,
34 oid: Option<ObjectIdentifier>,
35) -> Result<(), Error> {
36 let Some(oid) = oid else {
37 return Ok(());
38 };
39 let address = match transport.bip_port() {
40 Some(port) => port.endpoint,
41 None if transport.bip6_port().is_some() => return prepare_bip6(db, transport, oid),
42 None if transport.bip_broadcast_endpoint().is_some() => {
44 return Err(Error::Encoding(
45 "registered Network Port refused: a BBMD that also registers as a foreign \
46 device has no single B/IP mode"
47 .into(),
48 ))
49 }
50 None => {
51 return Err(Error::Encoding(
52 "registered port requires B/IP or B/IPv6".into(),
53 ))
54 }
55 };
56 let ip = *address.ip();
57 if ip.is_unspecified()
58 || ip.is_multicast()
59 || ip.is_broadcast()
60 || transport
61 .bip_broadcast_endpoint()
62 .is_some_and(|broadcast| *broadcast.ip() == ip)
63 {
64 return Err(Error::Encoding(
65 "registered B/IP requires a concrete unicast interface".into(),
66 ));
67 }
68 let (_, lease) = db.reserve_bip_port_internal(oid, ip.octets(), address.port())?;
69 transport.retain_network_port_lease_internal(lease)
70}
71
72fn prepare_bip6<T: TransportPort>(
75 db: &mut ObjectDatabase,
76 transport: &mut T,
77 oid: ObjectIdentifier,
78) -> Result<(), Error> {
79 let address = transport
80 .bip6_port()
81 .ok_or_else(|| Error::Encoding("registered port requires B/IP or B/IPv6".into()))?
82 .endpoint;
83 let ip = *address.ip();
84 let refused = if ip.is_unspecified() || ip.is_multicast() {
85 Some("registered B/IPv6 requires a concrete unicast interface")
86 } else if ip.to_ipv4_mapped().is_some() {
87 Some("registered B/IPv6 refuses an IPv4-mapped address: register IPv4 as B/IP")
88 } else if ip.is_unicast_link_local() {
89 Some("registered B/IPv6 refuses a link-local address: no IPv6_Zone_Index is modeled")
91 } else {
92 None
93 };
94 if let Some(message) = refused {
95 return Err(Error::Encoding(message.into()));
96 }
97 let (_, lease) = db.reserve_bip6_port_internal(oid, ip.octets(), address.port())?;
98 transport.retain_network_port_lease_internal(lease)
99}
100
101pub(super) fn publish<T: TransportPort + 'static>(
102 db: &mut ObjectDatabase,
103 network: &NetworkLayer<T>,
104 oid: Option<ObjectIdentifier>,
105 activation: bool,
106) -> Result<(), Error> {
107 let Some(oid) = oid else {
108 return Ok(());
109 };
110 let transport = network.transport();
111 let capacity = transport.local_receive_apdu_capacity() as u32;
112 if let Some(port) = transport.bip_port() {
113 db.publish_bip_port_internal(
114 oid,
115 port.endpoint.ip().octets(),
116 port.endpoint.port(),
117 capacity,
118 port.mode,
119 )?;
120 db.set_network_port_multicast_internal(oid, port.multicast)?;
122 } else {
123 let port = transport
124 .bip6_port()
125 .ok_or_else(|| Error::Encoding("registered B/IP mode changed during bind".into()))?;
126 let vmac = port
127 .vmac
128 .ok_or_else(|| Error::Encoding("started B/IPv6 port reports no VMAC".into()))?;
129 db.publish_bip6_port_internal(
130 oid,
131 port.endpoint.ip().octets(),
132 port.endpoint.port(),
133 vmac,
134 port.multicast_group.octets(),
135 capacity,
136 port.mode,
137 )?;
138 }
139 db.set_network_port_activation_internal(oid, activation)?;
141 if let Some(state) = db.network_number_internal(oid, None) {
144 network.local_network_number().publish(state);
145 }
146 Ok(())
147}
148
149pub(super) struct StartingNetwork<T: TransportPort + 'static> {
152 network: Option<NetworkLayer<T>>,
153 runtime: tokio::runtime::Handle,
154}
155impl<T: TransportPort + 'static> StartingNetwork<T> {
156 pub(super) fn new(transport: T) -> Self {
157 Self {
158 network: Some(NetworkLayer::new(transport)),
159 runtime: tokio::runtime::Handle::current(),
160 }
161 }
162 pub(super) fn from_network(network: NetworkLayer<T>) -> Self {
163 Self {
164 network: Some(network),
165 runtime: tokio::runtime::Handle::current(),
166 }
167 }
168 pub(super) fn network(&mut self) -> &mut NetworkLayer<T> {
169 self.network.as_mut().expect("startup network owned")
170 }
171 pub(super) fn finish(mut self) -> NetworkLayer<T> {
172 self.network.take().expect("startup network owned")
173 }
174 pub(super) async fn cleanup(mut self) -> Result<(), Error> {
175 let mut network = self.network.take().expect("startup network owned");
176 self.runtime
177 .spawn(async move { network.stop().await })
178 .await
179 .map_err(|error| Error::Encoding(format!("startup cleanup failed: {error}")))?
180 }
181}
182impl<T: TransportPort + 'static> Drop for StartingNetwork<T> {
183 fn drop(&mut self) {
184 if let Some(mut network) = self.network.take() {
185 self.runtime.spawn(async move {
186 if let Err(error) = network.stop().await {
187 tracing::warn!(%error, "cancelled startup cleanup failed");
188 }
189 });
190 }
191 }
192}
193
194pub(super) async fn start<T: TransportPort + 'static>(
196 db: &mut ObjectDatabase,
197 config: &ServerConfig,
198 mut transport: T,
199 audit_routes: super::audit_recipient_routes::AuditRoutes,
200) -> Result<
201 (
202 NetworkLayer<T>,
203 bacnet_network::layer::ApduReceiver,
204 Arc<super::audit_recipient_routes::AuditRoutes>,
205 Option<mpsc::Receiver<bacnet_network::layer::ReceivedNetworkControl>>,
206 ),
207 Error,
208> {
209 prepare(db, &mut transport, config.registered_network_port)?;
210 let mut starting = StartingNetwork::new(transport);
211 let started = async {
212 let controls = if starting
213 .network()
214 .transport()
215 .supports_local_nonrouter_number_controls()
216 {
217 Some(starting.network().enable_network_control_receiver()?)
218 } else {
219 None
220 };
221 let apdu_rx = starting.network().start().await?;
222 publish(
223 db,
224 starting.network(),
225 config.registered_network_port,
226 config.on_reinitialize.is_some(),
227 )?;
228 let routes = audit_routes.finish(db, config, starting.network()).await?;
229 let controls = starting
230 .network()
231 .transport()
232 .supports_local_nonrouter_number_controls()
233 .then_some(controls)
234 .flatten();
235 Ok::<_, Error>((apdu_rx, routes, controls))
236 }
237 .await;
238 match started {
239 Ok((apdu_rx, routes, controls)) => Ok((starting.finish(), apdu_rx, routes, controls)),
240 Err(error) => {
241 starting.cleanup().await?;
242 Err(error)
243 }
244 }
245}
246
247pub(super) fn spawn_number_worker<T: TransportPort + 'static>(
250 network: &Arc<NetworkLayer<T>>,
251 db: &Arc<RwLock<ObjectDatabase>>,
252 selected: Option<ObjectIdentifier>,
253 mut controls: mpsc::Receiver<bacnet_network::layer::ReceivedNetworkControl>,
254 audit: Option<std::sync::Weak<super::audit_recipient::TargetAudit<T>>>,
255) -> JoinHandle<()> {
256 let network = Arc::clone(network);
257 let mut owner =
258 crate::network_number::NetworkNumberOwner::new(selected.map(|oid| (Arc::clone(db), oid)))
259 .publishing_to(network.local_network_number().clone());
260 super::heap_futures::spawn_boxed(move || async move {
261 while let Some(control) = controls.recv().await {
262 let before = network.local_network_number().get();
263 let reply = owner.handle(control).await;
264 if network.local_network_number().get() != before {
265 if let Some(audit) = audit.as_ref().and_then(std::sync::Weak::upgrade) {
266 audit.number_changed();
267 }
268 }
269 if let Some(npdu) = reply {
270 if let Err(error) = network.transport().send_broadcast(&npdu).await {
271 tracing::debug!(%error, "Network-Number-Is broadcast failed");
272 }
273 }
274 }
275 })
276}
277
278pub(super) fn validate_apdu_capacity<T: TransportPort>(
279 config: &mut ServerConfig,
280 transport: &T,
281) -> Result<(), Error> {
282 config.device = config
283 .device
284 .resolve(transport.local_receive_apdu_capacity())?;
285 Ok(())
286}