bacnet_server/server/
network_port.rs1use super::*;
7use bacnet_network::layer::ReceivedApdu;
8
9impl<T: TransportPort + 'static> ServerBuilder<T> {
10 pub fn registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
15 self.config.registered_network_port = Some(oid);
16 self
17 }
18}
19impl BipServerBuilder {
20 pub fn registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
22 self.config.registered_network_port = Some(oid);
23 self
24 }
25}
26
27pub(super) fn prepare<T: TransportPort>(
28 db: &mut ObjectDatabase,
29 transport: &mut T,
30 oid: Option<ObjectIdentifier>,
31) -> Result<(), Error> {
32 let Some(oid) = oid else {
33 return Ok(());
34 };
35 let address = match transport.bip_port() {
36 Some(port) => port.endpoint,
37 None if transport.bip_broadcast_endpoint().is_some() => {
39 return Err(Error::Encoding(
40 "registered Network Port refused: a BBMD that also registers as a foreign \
41 device has no single B/IP mode"
42 .into(),
43 ))
44 }
45 None => return Err(Error::Encoding("registered port requires B/IP".into())),
46 };
47 let ip = *address.ip();
48 if ip.is_unspecified()
49 || ip.is_multicast()
50 || ip.is_broadcast()
51 || transport
52 .bip_broadcast_endpoint()
53 .is_some_and(|broadcast| *broadcast.ip() == ip)
54 {
55 return Err(Error::Encoding(
56 "registered B/IP requires a concrete unicast interface".into(),
57 ));
58 }
59 let (_, lease) = db.reserve_bip_port_internal(oid, ip.octets(), address.port())?;
60 transport.retain_network_port_lease_internal(lease)
61}
62
63pub(super) fn publish<T: TransportPort + 'static>(
64 db: &mut ObjectDatabase,
65 network: &NetworkLayer<T>,
66 oid: Option<ObjectIdentifier>,
67) -> Result<(), Error> {
68 let Some(oid) = oid else {
69 return Ok(());
70 };
71 let port = network
72 .transport()
73 .bip_port()
74 .ok_or_else(|| Error::Encoding("registered B/IP mode changed during bind".into()))?;
75 db.publish_bip_port_internal(
76 oid,
77 port.endpoint.ip().octets(),
78 port.endpoint.port(),
79 network.transport().local_receive_apdu_capacity() as u32,
80 port.mode,
81 )?;
82 if let Some(state) = db.network_number_internal(oid, None) {
85 network.local_network_number().publish(state);
86 }
87 Ok(())
88}
89
90pub(super) struct StartingNetwork<T: TransportPort + 'static> {
93 network: Option<NetworkLayer<T>>,
94 runtime: tokio::runtime::Handle,
95}
96impl<T: TransportPort + 'static> StartingNetwork<T> {
97 pub(super) fn new(transport: T) -> Self {
98 Self {
99 network: Some(NetworkLayer::new(transport)),
100 runtime: tokio::runtime::Handle::current(),
101 }
102 }
103 pub(super) fn from_network(network: NetworkLayer<T>) -> Self {
104 Self {
105 network: Some(network),
106 runtime: tokio::runtime::Handle::current(),
107 }
108 }
109 pub(super) fn network(&mut self) -> &mut NetworkLayer<T> {
110 self.network.as_mut().expect("startup network owned")
111 }
112 pub(super) fn finish(mut self) -> NetworkLayer<T> {
113 self.network.take().expect("startup network owned")
114 }
115 pub(super) async fn cleanup(mut self) -> Result<(), Error> {
116 let mut network = self.network.take().expect("startup network owned");
117 self.runtime
118 .spawn(async move { network.stop().await })
119 .await
120 .map_err(|error| Error::Encoding(format!("startup cleanup failed: {error}")))?
121 }
122}
123impl<T: TransportPort + 'static> Drop for StartingNetwork<T> {
124 fn drop(&mut self) {
125 if let Some(mut network) = self.network.take() {
126 self.runtime.spawn(async move {
127 if let Err(error) = network.stop().await {
128 tracing::warn!(%error, "cancelled startup cleanup failed");
129 }
130 });
131 }
132 }
133}
134
135pub(super) async fn start<T: TransportPort + 'static>(
137 db: &mut ObjectDatabase,
138 config: &ServerConfig,
139 mut transport: T,
140 audit_routes: super::audit_recipient_routes::AuditRoutes,
141) -> Result<
142 (
143 NetworkLayer<T>,
144 mpsc::Receiver<ReceivedApdu>,
145 Arc<super::audit_recipient_routes::AuditRoutes>,
146 Option<mpsc::Receiver<bacnet_network::layer::ReceivedNetworkControl>>,
147 ),
148 Error,
149> {
150 prepare(db, &mut transport, config.registered_network_port)?;
151 let mut starting = StartingNetwork::new(transport);
152 let started = async {
153 let controls = if starting
154 .network()
155 .transport()
156 .supports_local_nonrouter_number_controls()
157 {
158 Some(starting.network().enable_network_control_receiver()?)
159 } else {
160 None
161 };
162 let apdu_rx = starting.network().start().await?;
163 publish(db, starting.network(), config.registered_network_port)?;
164 let routes = audit_routes.finish(db, config, starting.network()).await?;
165 let controls = starting
166 .network()
167 .transport()
168 .supports_local_nonrouter_number_controls()
169 .then_some(controls)
170 .flatten();
171 Ok::<_, Error>((apdu_rx, routes, controls))
172 }
173 .await;
174 match started {
175 Ok((apdu_rx, routes, controls)) => Ok((starting.finish(), apdu_rx, routes, controls)),
176 Err(error) => {
177 starting.cleanup().await?;
178 Err(error)
179 }
180 }
181}
182
183pub(super) fn spawn_number_worker<T: TransportPort + 'static>(
186 network: &Arc<NetworkLayer<T>>,
187 db: &Arc<RwLock<ObjectDatabase>>,
188 selected: Option<ObjectIdentifier>,
189 mut controls: mpsc::Receiver<bacnet_network::layer::ReceivedNetworkControl>,
190 audit: Option<std::sync::Weak<super::audit_recipient::TargetAudit<T>>>,
191) -> JoinHandle<()> {
192 let network = Arc::clone(network);
193 let mut owner =
194 crate::network_number::NetworkNumberOwner::new(selected.map(|oid| (Arc::clone(db), oid)))
195 .publishing_to(network.local_network_number().clone());
196 super::heap_futures::spawn_boxed(move || async move {
197 while let Some(control) = controls.recv().await {
198 let before = network.local_network_number().get();
199 let reply = owner.handle(control).await;
200 if network.local_network_number().get() != before {
201 if let Some(audit) = audit.as_ref().and_then(std::sync::Weak::upgrade) {
202 audit.number_changed();
203 }
204 }
205 if let Some(npdu) = reply {
206 if let Err(error) = network.transport().send_broadcast(&npdu).await {
207 tracing::debug!(%error, "Network-Number-Is broadcast failed");
208 }
209 }
210 }
211 })
212}
213
214pub(super) fn validate_apdu_capacity<T: TransportPort>(
215 config: &mut ServerConfig,
216 transport: &T,
217) -> Result<(), Error> {
218 let transport_max = transport.local_receive_apdu_capacity() as u32;
219 config.max_apdu_length = config.max_apdu_length.min(transport_max);
220 if config.max_apdu_length < u32::from(apdu::MINIMUM_MESSAGE_SIZE) {
221 return Err(Error::Encoding(format!(
222 "local max_apdu_length {} is below 50",
223 config.max_apdu_length
224 )));
225 }
226 Ok(())
227}