• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! This module handles "arbitration" of ATT packets, to determine whether they
2 //! should be handled by the primary stack or by the Rust stack
3 
4 use pdl_runtime::Packet;
5 use std::sync::{Arc, Mutex};
6 
7 use log::{error, trace, warn};
8 use std::sync::RwLock;
9 
10 use crate::do_in_rust_thread;
11 use crate::packets::att;
12 
13 use super::ffi::{InterceptAction, StoreCallbacksFromRust};
14 use super::ids::{AdvertiserId, TransportIndex};
15 use super::mtu::MtuEvent;
16 use super::opcode_types::{classify_opcode, OperationType};
17 use super::server::isolation_manager::IsolationManager;
18 
19 static ARBITER: RwLock<Option<Arc<Mutex<IsolationManager>>>> = RwLock::new(None);
20 
21 /// Initialize the Arbiter
initialize_arbiter() -> Arc<Mutex<IsolationManager>>22 pub fn initialize_arbiter() -> Arc<Mutex<IsolationManager>> {
23     let arbiter = Arc::new(Mutex::new(IsolationManager::new()));
24     let mut lock = ARBITER.write().unwrap();
25     assert!(lock.is_none(), "Rust stack should only start up once");
26     *lock = Some(arbiter.clone());
27 
28     StoreCallbacksFromRust(
29         on_le_connect,
30         on_le_disconnect,
31         intercept_packet,
32         |tcb_idx| on_mtu_event(TransportIndex(tcb_idx), MtuEvent::OutgoingRequest),
33         |tcb_idx, mtu| on_mtu_event(TransportIndex(tcb_idx), MtuEvent::IncomingResponse(mtu)),
34         |tcb_idx, mtu| on_mtu_event(TransportIndex(tcb_idx), MtuEvent::IncomingRequest(mtu)),
35     );
36 
37     arbiter
38 }
39 
40 /// Clean the Arbiter
clean_arbiter()41 pub fn clean_arbiter() {
42     let mut lock = ARBITER.write().unwrap();
43     *lock = None
44 }
45 
46 /// Acquire the mutex holding the Arbiter and provide a mutable reference to the
47 /// supplied closure
with_arbiter<T>(f: impl FnOnce(&mut IsolationManager) -> T) -> T48 pub fn with_arbiter<T>(f: impl FnOnce(&mut IsolationManager) -> T) -> T {
49     f(ARBITER.read().unwrap().as_ref().expect("Rust stack is not started").lock().as_mut().unwrap())
50 }
51 
52 /// Check if the Arbiter is initialized.
has_arbiter() -> bool53 pub fn has_arbiter() -> bool {
54     ARBITER.read().unwrap().is_some()
55 }
56 
57 /// Test to see if a buffer contains a valid ATT packet with an opcode we
58 /// are interested in intercepting (those intended for servers that are isolated)
try_parse_att_server_packet( isolation_manager: &IsolationManager, tcb_idx: TransportIndex, packet: &[u8], ) -> Option<att::Att>59 fn try_parse_att_server_packet(
60     isolation_manager: &IsolationManager,
61     tcb_idx: TransportIndex,
62     packet: &[u8],
63 ) -> Option<att::Att> {
64     isolation_manager.get_server_id(tcb_idx)?;
65 
66     let att = att::Att::decode_full(packet).ok()?;
67 
68     if att.opcode == att::AttOpcode::ExchangeMtuRequest {
69         // special case: this server opcode is handled by legacy stack, and we snoop
70         // on its handling, since the MTU is shared between the client + server
71         return None;
72     }
73 
74     match classify_opcode(att.opcode) {
75         OperationType::Command | OperationType::Request | OperationType::Confirmation => Some(att),
76         _ => None,
77     }
78 }
79 
on_le_connect(tcb_idx: u8, advertiser: u8)80 fn on_le_connect(tcb_idx: u8, advertiser: u8) {
81     let tcb_idx = TransportIndex(tcb_idx);
82     let advertiser = AdvertiserId(advertiser);
83     let is_isolated = with_arbiter(|arbiter| arbiter.is_advertiser_isolated(advertiser));
84     if is_isolated {
85         do_in_rust_thread(move |modules| {
86             if let Err(err) = modules.gatt_module.on_le_connect(tcb_idx, Some(advertiser)) {
87                 error!("{err:?}")
88             }
89         })
90     }
91 }
92 
on_le_disconnect(tcb_idx: u8)93 fn on_le_disconnect(tcb_idx: u8) {
94     // Events may be received after a FactoryReset
95     // is initiated for Bluetooth and the rust arbiter is taken
96     // down.
97     if !has_arbiter() {
98         warn!("arbiter is not yet initialized");
99         return;
100     }
101 
102     let tcb_idx = TransportIndex(tcb_idx);
103     let was_isolated = with_arbiter(|arbiter| arbiter.is_connection_isolated(tcb_idx));
104     if was_isolated {
105         do_in_rust_thread(move |modules| {
106             if let Err(err) = modules.gatt_module.on_le_disconnect(tcb_idx) {
107                 error!("{err:?}")
108             }
109         })
110     }
111 }
112 
intercept_packet(tcb_idx: u8, packet: Vec<u8>) -> InterceptAction113 fn intercept_packet(tcb_idx: u8, packet: Vec<u8>) -> InterceptAction {
114     // Events may be received after a FactoryReset
115     // is initiated for Bluetooth and the rust arbiter is taken
116     // down.
117     if !has_arbiter() {
118         warn!("arbiter is not yet initialized");
119         return InterceptAction::Drop;
120     }
121 
122     let tcb_idx = TransportIndex(tcb_idx);
123     if let Some(att) =
124         with_arbiter(|arbiter| try_parse_att_server_packet(arbiter, tcb_idx, &packet))
125     {
126         do_in_rust_thread(move |modules| {
127             trace!("pushing packet to GATT");
128             if let Some(bearer) = modules.gatt_module.get_bearer(tcb_idx) {
129                 bearer.handle_packet(att)
130             } else {
131                 error!("Bearer for {tcb_idx:?} not found");
132             }
133         });
134         InterceptAction::Drop
135     } else {
136         InterceptAction::Forward
137     }
138 }
139 
on_mtu_event(tcb_idx: TransportIndex, event: MtuEvent)140 fn on_mtu_event(tcb_idx: TransportIndex, event: MtuEvent) {
141     if with_arbiter(|arbiter| arbiter.is_connection_isolated(tcb_idx)) {
142         do_in_rust_thread(move |modules| {
143             let Some(bearer) = modules.gatt_module.get_bearer(tcb_idx) else {
144                 error!("Bearer for {tcb_idx:?} not found");
145                 return;
146             };
147             if let Err(err) = bearer.handle_mtu_event(event) {
148                 error!("{err:?}")
149             }
150         });
151     }
152 }
153 
154 #[cfg(test)]
155 mod test {
156     use super::*;
157 
158     use crate::gatt::ids::{AttHandle, ServerId};
159     use crate::packets::att;
160 
161     const TCB_IDX: TransportIndex = TransportIndex(1);
162     const ADVERTISER_ID: AdvertiserId = AdvertiserId(3);
163     const SERVER_ID: ServerId = ServerId(4);
164 
create_manager_with_isolated_connection( tcb_idx: TransportIndex, server_id: ServerId, ) -> IsolationManager165     fn create_manager_with_isolated_connection(
166         tcb_idx: TransportIndex,
167         server_id: ServerId,
168     ) -> IsolationManager {
169         let mut isolation_manager = IsolationManager::new();
170         isolation_manager.associate_server_with_advertiser(server_id, ADVERTISER_ID);
171         isolation_manager.on_le_connect(tcb_idx, Some(ADVERTISER_ID));
172         isolation_manager
173     }
174 
175     #[test]
test_packet_capture_when_isolated()176     fn test_packet_capture_when_isolated() {
177         let isolation_manager = create_manager_with_isolated_connection(TCB_IDX, SERVER_ID);
178         let packet = att::AttReadRequest { attribute_handle: AttHandle(1).into() };
179 
180         let out = try_parse_att_server_packet(
181             &isolation_manager,
182             TCB_IDX,
183             &packet.encode_to_vec().unwrap(),
184         );
185 
186         assert!(out.is_some());
187     }
188 
189     #[test]
test_packet_bypass_when_isolated()190     fn test_packet_bypass_when_isolated() {
191         let isolation_manager = create_manager_with_isolated_connection(TCB_IDX, SERVER_ID);
192         let packet = att::AttErrorResponse {
193             opcode_in_error: att::AttOpcode::ReadResponse,
194             handle_in_error: AttHandle(1).into(),
195             error_code: att::AttErrorCode::InvalidHandle,
196         };
197 
198         let out = try_parse_att_server_packet(
199             &isolation_manager,
200             TCB_IDX,
201             &packet.encode_to_vec().unwrap(),
202         );
203 
204         assert!(out.is_none());
205     }
206 
207     #[test]
test_mtu_bypass()208     fn test_mtu_bypass() {
209         let isolation_manager = create_manager_with_isolated_connection(TCB_IDX, SERVER_ID);
210         let packet = att::AttExchangeMtuRequest { mtu: 64 };
211 
212         let out = try_parse_att_server_packet(
213             &isolation_manager,
214             TCB_IDX,
215             &packet.encode_to_vec().unwrap(),
216         );
217 
218         assert!(out.is_none());
219     }
220 
221     #[test]
test_packet_bypass_when_not_isolated()222     fn test_packet_bypass_when_not_isolated() {
223         let isolation_manager = IsolationManager::new();
224         let packet = att::AttReadRequest { attribute_handle: AttHandle(1).into() };
225 
226         let out = try_parse_att_server_packet(
227             &isolation_manager,
228             TCB_IDX,
229             &packet.encode_to_vec().unwrap(),
230         );
231 
232         assert!(out.is_none());
233     }
234 }
235