1 //! The MTU on an ATT bearer is determined either by L2CAP (if EATT) or by the
2 //! ATT_EXCHANGE_MTU procedure (if on an unenhanced bearer).
3 //!
4 //! In the latter case, the MTU may be either (1) unset, (2) pending, or (3)
5 //! set. If the MTU is pending, ATT notifications/indications may not be sent.
6 //! Refer to Core Spec 5.3 Vol 3F 3.4.2 MTU exchange for full details.
7 
8 use std::{cell::Cell, future::Future};
9 
10 use anyhow::{bail, Result};
11 use log::info;
12 use tokio::sync::OwnedMutexGuard;
13 
14 use crate::core::shared_mutex::SharedMutex;
15 
16 /// An MTU event that we have snooped
17 pub enum MtuEvent {
18     /// We have sent an MTU_REQ
19     OutgoingRequest,
20     /// We have received an MTU_RESP
21     IncomingResponse(usize),
22     /// We have received an MTU_REQ (and will immediately reply)
23     IncomingRequest(usize),
24 }
25 
26 /// The state of MTU negotiation on an unenhanced ATT bearer
27 pub struct AttMtu {
28     /// The MTU we have committed to (i.e. sent a REQ and got a RESP, or
29     /// vice-versa)
30     previous_mtu: Cell<usize>,
31     /// The MTU we have committed or are about to commit to (if a REQ is
32     /// pending)
33     stable_mtu: SharedMutex<usize>,
34     /// Lock guard held if we are currrently performing MTU negotiation
35     pending_exchange: Cell<Option<OwnedMutexGuard<usize>>>,
36 }
37 
38 // NOTE: this is only true for ATT, not EATT
39 const DEFAULT_ATT_MTU: usize = 23;
40 
41 impl AttMtu {
42     /// Constructor
new() -> Self43     pub fn new() -> Self {
44         Self {
45             previous_mtu: Cell::new(DEFAULT_ATT_MTU),
46             stable_mtu: SharedMutex::new(DEFAULT_ATT_MTU),
47             pending_exchange: Cell::new(None),
48         }
49     }
50 
51     /// Get the most recently negotiated MTU, or the default (if an MTU_REQ is
52     /// outstanding and we get an ATT_REQ)
snapshot_or_default(&self) -> usize53     pub fn snapshot_or_default(&self) -> usize {
54         self.stable_mtu.try_lock().as_deref().cloned().unwrap_or_else(|_| self.previous_mtu.get())
55     }
56 
57     /// Get the most recently negotiated MTU, or block if negotiation is ongoing
58     /// (i.e. if an MTU_REQ is outstanding)
snapshot(&self) -> impl Future<Output = Option<usize>>59     pub fn snapshot(&self) -> impl Future<Output = Option<usize>> {
60         let pending_snapshot = self.stable_mtu.lock();
61         async move { pending_snapshot.await.as_deref().cloned() }
62     }
63 
64     /// Handle an MtuEvent and update the stored MTU
handle_event(&self, event: MtuEvent) -> Result<()>65     pub fn handle_event(&self, event: MtuEvent) -> Result<()> {
66         match event {
67             MtuEvent::OutgoingRequest => self.on_outgoing_request(),
68             MtuEvent::IncomingResponse(mtu) => self.on_incoming_response(mtu),
69             MtuEvent::IncomingRequest(mtu) => {
70                 self.on_incoming_request(mtu);
71                 Ok(())
72             }
73         }
74     }
75 
on_outgoing_request(&self) -> Result<()>76     fn on_outgoing_request(&self) -> Result<()> {
77         let Ok(pending_mtu) = self.stable_mtu.try_lock() else {
78             bail!("Sent ATT_EXCHANGE_MTU_REQ while an existing MTU exchange is taking place");
79         };
80         info!("Sending MTU_REQ, pausing indications/notifications");
81         self.pending_exchange.replace(Some(pending_mtu));
82         Ok(())
83     }
84 
on_incoming_response(&self, mtu: usize) -> Result<()>85     fn on_incoming_response(&self, mtu: usize) -> Result<()> {
86         let Some(mut pending_exchange) = self.pending_exchange.take() else {
87             bail!("Got ATT_EXCHANGE_MTU_RESP when transaction not taking place");
88         };
89         info!("Got an MTU_RESP of {mtu}");
90         *pending_exchange = mtu;
91         // note: since MTU_REQ can be sent at most once, this is a no-op, as the
92         // stable_mtu will never again be blocked we do it anyway for clarity
93         self.previous_mtu.set(mtu);
94         Ok(())
95     }
96 
on_incoming_request(&self, mtu: usize)97     fn on_incoming_request(&self, mtu: usize) {
98         self.previous_mtu.set(mtu);
99         if let Ok(mut stable_mtu) = self.stable_mtu.try_lock() {
100             info!("Accepted an MTU_REQ of {mtu:?}");
101             *stable_mtu = mtu;
102         } else {
103             info!("Accepted an MTU_REQ while our own MTU_REQ was outstanding")
104         }
105     }
106 }
107 
108 #[cfg(test)]
109 mod test {
110     use crate::utils::task::{block_on_locally, try_await};
111 
112     use super::*;
113 
114     const NEW_MTU: usize = 51;
115     const ANOTHER_NEW_MTU: usize = 52;
116 
117     #[test]
test_default_mtu()118     fn test_default_mtu() {
119         let mtu = AttMtu::new();
120 
121         let stable_value = mtu.snapshot_or_default();
122         let latest_value = tokio_test::block_on(mtu.snapshot()).unwrap();
123 
124         assert_eq!(stable_value, DEFAULT_ATT_MTU);
125         assert_eq!(latest_value, DEFAULT_ATT_MTU);
126     }
127 
128     #[test]
test_guaranteed_mtu_during_client_negotiation()129     fn test_guaranteed_mtu_during_client_negotiation() {
130         // arrange
131         let mtu = AttMtu::new();
132 
133         // act: send an MTU_REQ and validate snapshotted value
134         mtu.handle_event(MtuEvent::OutgoingRequest).unwrap();
135         let stable_value = mtu.snapshot_or_default();
136 
137         // assert: we use the default MTU for requests handled
138         // while our request is pending
139         assert_eq!(stable_value, DEFAULT_ATT_MTU);
140     }
141 
142     #[test]
test_mtu_blocking_snapshot_during_client_negotiation()143     fn test_mtu_blocking_snapshot_during_client_negotiation() {
144         block_on_locally(async move {
145             // arrange
146             let mtu = AttMtu::new();
147 
148             // act: send an MTU_REQ
149             mtu.handle_event(MtuEvent::OutgoingRequest).unwrap();
150             // take snapshot of pending future
151             let pending_mtu = try_await(mtu.snapshot()).await.unwrap_err();
152             // resolve MTU_REQ
153             mtu.handle_event(MtuEvent::IncomingResponse(NEW_MTU)).unwrap();
154 
155             // assert: that the snapshot resolved with the NEW_MTU
156             assert_eq!(pending_mtu.await.unwrap(), NEW_MTU);
157         });
158     }
159 
160     #[test]
test_receive_mtu_request()161     fn test_receive_mtu_request() {
162         block_on_locally(async move {
163             // arrange
164             let mtu = AttMtu::new();
165 
166             // act: receive an MTU_REQ
167             mtu.handle_event(MtuEvent::IncomingRequest(NEW_MTU)).unwrap();
168             // take snapshot
169             let snapshot = mtu.snapshot().await;
170 
171             // assert: that the snapshot resolved with the NEW_MTU
172             assert_eq!(snapshot.unwrap(), NEW_MTU);
173         });
174     }
175 
176     #[test]
test_client_then_server_negotiation()177     fn test_client_then_server_negotiation() {
178         block_on_locally(async move {
179             // arrange
180             let mtu = AttMtu::new();
181 
182             // act: send an MTU_REQ
183             mtu.handle_event(MtuEvent::OutgoingRequest).unwrap();
184             // receive an MTU_RESP
185             mtu.handle_event(MtuEvent::IncomingResponse(NEW_MTU)).unwrap();
186             // receive an MTU_REQ
187             mtu.handle_event(MtuEvent::IncomingRequest(ANOTHER_NEW_MTU)).unwrap();
188             // take snapshot
189             let snapshot = mtu.snapshot().await;
190 
191             // assert: that the snapshot resolved with ANOTHER_NEW_MTU
192             assert_eq!(snapshot.unwrap(), ANOTHER_NEW_MTU);
193         });
194     }
195 
196     #[test]
test_server_negotiation_then_pending_client_default_value()197     fn test_server_negotiation_then_pending_client_default_value() {
198         block_on_locally(async move {
199             // arrange
200             let mtu = AttMtu::new();
201 
202             // act: receive an MTU_REQ
203             mtu.handle_event(MtuEvent::IncomingRequest(NEW_MTU)).unwrap();
204             // send a MTU_REQ
205             mtu.handle_event(MtuEvent::OutgoingRequest).unwrap();
206             // take snapshot for requests
207             let snapshot = mtu.snapshot_or_default();
208 
209             // assert: that the snapshot resolved to NEW_MTU
210             assert_eq!(snapshot, NEW_MTU);
211         });
212     }
213 
214     #[test]
test_server_negotiation_then_pending_client_finalized_value()215     fn test_server_negotiation_then_pending_client_finalized_value() {
216         block_on_locally(async move {
217             // arrange
218             let mtu = AttMtu::new();
219 
220             // act: receive an MTU_REQ
221             mtu.handle_event(MtuEvent::IncomingRequest(NEW_MTU)).unwrap();
222             // send a MTU_REQ
223             mtu.handle_event(MtuEvent::OutgoingRequest).unwrap();
224             // take snapshot of pending future
225             let snapshot = try_await(mtu.snapshot()).await.unwrap_err();
226             // receive MTU_RESP
227             mtu.handle_event(MtuEvent::IncomingResponse(ANOTHER_NEW_MTU)).unwrap();
228 
229             // assert: that the snapshot resolved to ANOTHER_NEW_MTU
230             assert_eq!(snapshot.await.unwrap(), ANOTHER_NEW_MTU);
231         });
232     }
233 
234     #[test]
test_mtu_dropped_while_pending()235     fn test_mtu_dropped_while_pending() {
236         block_on_locally(async move {
237             // arrange
238             let mtu = AttMtu::new();
239 
240             // act: send a MTU_REQ
241             mtu.handle_event(MtuEvent::OutgoingRequest).unwrap();
242             // take snapshot and store pending future
243             let pending_mtu = try_await(mtu.snapshot()).await.unwrap_err();
244             // drop the mtu (when the bearer closes)
245             drop(mtu);
246 
247             // assert: that the snapshot resolves to None since the bearer is gone
248             assert!(pending_mtu.await.is_none());
249         });
250     }
251 }
252