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