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