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