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