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