summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Rahul Arya <aryarahul@google.com> 2023-05-08 21:13:39 +0000
committer Rahul Arya <aryarahul@google.com> 2023-05-08 21:13:39 +0000
commitfc4deea428620bb9f1d4f46e1af37eacb300d7bc (patch)
tree7ba9b680042991988f95ad23ecb45038959409f6
parentc0c40140e83d449f254e8a0a20e61b6dfb580293 (diff)
[GATT Server] Split up Arbiter logic and FFI
The IsolationManager logic is separate from the FFI forwarding, so put it in a separate module. The idea is that the IsolationManager will ultimately live in the GATT server module directly, and peers will see either the global GATT db, or a server selected by the IsolationManager. FFI will always send connections straight to the GATT module, even if they are not isolated. Test: unit Bug: 274945531 Change-Id: I470d40e7a7c093ff55994aeb6a6935aa854c6970
-rw-r--r--system/rust/src/gatt/arbiter.rs378
-rw-r--r--system/rust/src/gatt/server.rs1
-rw-r--r--system/rust/src/gatt/server/isolation_manager.rs242
3 files changed, 301 insertions, 320 deletions
diff --git a/system/rust/src/gatt/arbiter.rs b/system/rust/src/gatt/arbiter.rs
index 26a8d3319e..d4a113f6b1 100644
--- a/system/rust/src/gatt/arbiter.rs
+++ b/system/rust/src/gatt/arbiter.rs
@@ -1,9 +1,9 @@
//! This module handles "arbitration" of ATT packets, to determine whether they
-//! should be handled by the primary stack or by the "Private GATT" stack
+//! should be handled by the primary stack or by the Rust stack
-use std::{collections::HashMap, sync::Mutex};
+use std::sync::Mutex;
-use log::{error, info, trace};
+use log::{error, trace};
use crate::{
do_in_rust_thread,
@@ -12,25 +12,17 @@ use crate::{
use super::{
ffi::{InterceptAction, StoreCallbacksFromRust},
- ids::{AdvertiserId, ConnectionId, ServerId, TransportIndex},
+ ids::{AdvertiserId, TransportIndex},
mtu::MtuEvent,
opcode_types::{classify_opcode, OperationType},
+ server::isolation_manager::IsolationManager,
};
-static ARBITER: Mutex<Option<Arbiter>> = Mutex::new(None);
-
-/// This class is responsible for tracking which connections and advertising we
-/// own, and using this information to decide what packets should be
-/// intercepted, and which should be forwarded to the legacy stack.
-#[derive(Default)]
-pub struct Arbiter {
- advertiser_to_server: HashMap<AdvertiserId, ServerId>,
- transport_to_owned_connection: HashMap<TransportIndex, ConnectionId>,
-}
+static ARBITER: Mutex<Option<IsolationManager>> = Mutex::new(None);
/// Initialize the Arbiter
pub fn initialize_arbiter() {
- *ARBITER.lock().unwrap() = Some(Arbiter::new());
+ *ARBITER.lock().unwrap() = Some(IsolationManager::new());
StoreCallbacksFromRust(
on_le_connect,
@@ -44,108 +36,30 @@ pub fn initialize_arbiter() {
/// Acquire the mutex holding the Arbiter and provide a mutable reference to the
/// supplied closure
-pub fn with_arbiter<T>(f: impl FnOnce(&mut Arbiter) -> T) -> T {
+pub fn with_arbiter<T>(f: impl FnOnce(&mut IsolationManager) -> T) -> T {
f(ARBITER.lock().unwrap().as_mut().unwrap())
}
-impl Arbiter {
- /// Constructor
- pub fn new() -> Self {
- Arbiter {
- advertiser_to_server: HashMap::new(),
- transport_to_owned_connection: HashMap::new(),
- }
- }
-
- /// Link a given GATT server to an LE advertising set, so incoming
- /// connections to this advertiser will be visible only by the linked
- /// server
- pub fn associate_server_with_advertiser(
- &mut self,
- server_id: ServerId,
- advertiser_id: AdvertiserId,
- ) {
- info!("associating server {server_id:?} with advertising set {advertiser_id:?}");
- let old = self.advertiser_to_server.insert(advertiser_id, server_id);
- if let Some(old) = old {
- error!("new server {server_id:?} associated with same advertiser {advertiser_id:?}, displacing old server {old:?}");
- }
- }
+/// Test to see if a buffer contains a valid ATT packet with an opcode we
+/// are interested in intercepting (those intended for servers that are isolated)
+fn try_parse_att_server_packet(
+ isolation_manager: &IsolationManager,
+ tcb_idx: TransportIndex,
+ packet: Box<[u8]>,
+) -> Option<OwnedAttView> {
+ isolation_manager.get_conn_id(tcb_idx)?;
- /// Remove all linked advertising sets from the provided server
- pub fn clear_server(&mut self, server_id: ServerId) {
- info!("clearing advertisers associated with {server_id:?}");
- self.advertiser_to_server.retain(|_, server| *server != server_id);
- }
+ let att = OwnedAttView::try_parse(packet).ok()?;
- /// Clear the server associated with this advertiser, if one exists
- pub fn clear_advertiser(&mut self, advertiser_id: AdvertiserId) {
- info!("removing server (if any) associated with advertiser {advertiser_id:?}");
- self.advertiser_to_server.remove(&advertiser_id);
+ if att.view().get_opcode() == AttOpcode::EXCHANGE_MTU_REQUEST {
+ // special case: this server opcode is handled by legacy stack, and we snoop
+ // on its handling, since the MTU is shared between the client + server
+ return None;
}
- /// Check if this conn_id is currently owned by the Rust stack
- pub fn is_connection_isolated(&self, conn_id: ConnectionId) -> bool {
- self.transport_to_owned_connection.values().any(|owned_conn_id| *owned_conn_id == conn_id)
- }
-
- /// Test to see if a buffer contains a valid ATT packet with an opcode we
- /// are interested in intercepting (those intended for servers that are isolated)
- pub fn try_parse_att_server_packet(
- &self,
- tcb_idx: TransportIndex,
- packet: Box<[u8]>,
- ) -> Option<OwnedAttView> {
- if !self.transport_to_owned_connection.contains_key(&tcb_idx) {
- return None;
- }
-
- let att = OwnedAttView::try_parse(packet).ok()?;
-
- if att.view().get_opcode() == AttOpcode::EXCHANGE_MTU_REQUEST {
- // special case: this server opcode is handled by legacy stack, and we snoop
- // on its handling, since the MTU is shared between the client + server
- return None;
- }
-
- match classify_opcode(att.view().get_opcode()) {
- OperationType::Command | OperationType::Request | OperationType::Confirmation => {
- Some(att)
- }
- _ => None,
- }
- }
-
- /// Check if an incoming connection should be intercepted and, if so, on
- /// what conn_id
- pub fn on_le_connect(
- &mut self,
- tcb_idx: TransportIndex,
- advertiser: AdvertiserId,
- ) -> Option<ConnectionId> {
- info!(
- "processing incoming connection on transport {tcb_idx:?} to advertiser {advertiser:?}"
- );
- let server_id = *self.advertiser_to_server.get(&advertiser)?;
- info!("connection is isolated to server {server_id:?}");
-
- let conn_id = ConnectionId::new(tcb_idx, server_id);
- let old = self.transport_to_owned_connection.insert(tcb_idx, conn_id);
- if old.is_some() {
- error!("new server {server_id:?} on transport {tcb_idx:?} displacing existing registered connection {conn_id:?}")
- }
- Some(conn_id)
- }
-
- /// Handle a disconnection, if any, and return whether the disconnection was registered
- pub fn on_le_disconnect(&mut self, tcb_idx: TransportIndex) -> bool {
- info!("processing disconnection on transport {tcb_idx:?}");
- self.transport_to_owned_connection.remove(&tcb_idx).is_some()
- }
-
- /// Look up the conn_id for a given tcb_idx, if present
- pub fn get_conn_id(&self, tcb_idx: TransportIndex) -> Option<ConnectionId> {
- self.transport_to_owned_connection.get(&tcb_idx).copied()
+ match classify_opcode(att.view().get_opcode()) {
+ OperationType::Command | OperationType::Request | OperationType::Confirmation => Some(att),
+ _ => None,
}
}
@@ -175,7 +89,7 @@ fn on_le_disconnect(tcb_idx: u8) {
fn intercept_packet(tcb_idx: u8, packet: Vec<u8>) -> InterceptAction {
let tcb_idx = TransportIndex(tcb_idx);
if let Some(att) = with_arbiter(|arbiter| {
- arbiter.try_parse_att_server_packet(tcb_idx, packet.into_boxed_slice())
+ try_parse_att_server_packet(arbiter, tcb_idx, packet.into_boxed_slice())
}) {
do_in_rust_thread(move |modules| {
trace!("pushing packet to GATT");
@@ -210,7 +124,7 @@ mod test {
use super::*;
use crate::{
- gatt::ids::AttHandle,
+ gatt::ids::{AttHandle, ServerId},
packets::{
AttBuilder, AttExchangeMtuRequestBuilder, AttOpcode, AttReadRequestBuilder,
Serializable,
@@ -218,260 +132,84 @@ mod test {
};
const TCB_IDX: TransportIndex = TransportIndex(1);
- const ANOTHER_TCB_IDX: TransportIndex = TransportIndex(2);
const ADVERTISER_ID: AdvertiserId = AdvertiserId(3);
const SERVER_ID: ServerId = ServerId(4);
- const CONN_ID: ConnectionId = ConnectionId::new(TCB_IDX, SERVER_ID);
-
- const ANOTHER_ADVERTISER_ID: AdvertiserId = AdvertiserId(5);
-
- #[test]
- fn test_non_isolated_connect() {
- let mut arbiter = Arbiter::new();
-
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- assert!(conn_id.is_none())
- }
-
- #[test]
- fn test_isolated_connect() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
-
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- assert_eq!(conn_id, Some(CONN_ID));
- }
-
- #[test]
- fn test_non_isolated_connect_with_isolated_advertiser() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
-
- let conn_id = arbiter.on_le_connect(TCB_IDX, ANOTHER_ADVERTISER_ID);
-
- assert!(conn_id.is_none())
- }
-
- #[test]
- fn test_non_isolated_disconnect() {
- let mut arbiter = Arbiter::new();
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- let ok = arbiter.on_le_disconnect(TCB_IDX);
-
- assert!(!ok)
- }
-
- #[test]
- fn test_isolated_disconnect() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- let ok = arbiter.on_le_disconnect(TCB_IDX);
-
- assert!(ok)
- }
-
- #[test]
- fn test_advertiser_id_reuse() {
- let mut arbiter = Arbiter::new();
- // start an advertiser associated with the server, then kill the advertiser
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.clear_advertiser(ADVERTISER_ID);
-
- // a new advertiser appeared with the same ID and got a connection
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- // but we should not be isolated since this is a new advertiser reusing the old
- // ID
- assert!(conn_id.is_none())
- }
-
- #[test]
- fn test_server_closed() {
- let mut arbiter = Arbiter::new();
- // start an advertiser associated with the server, then kill the server
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.clear_server(SERVER_ID);
-
- // then afterwards we get a connection to this advertiser
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- // since the server is gone, we should not capture the connection
- assert!(conn_id.is_none())
- }
-
- #[test]
- fn test_connection_isolated() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID).unwrap();
-
- let is_isolated = arbiter.is_connection_isolated(conn_id);
-
- assert!(is_isolated)
- }
-
- #[test]
- fn test_connection_isolated_after_advertiser_stops() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID).unwrap();
- arbiter.clear_advertiser(ADVERTISER_ID);
-
- let is_isolated = arbiter.is_connection_isolated(conn_id);
-
- assert!(is_isolated)
- }
-
- #[test]
- fn test_connection_isolated_after_server_stops() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID).unwrap();
- arbiter.clear_server(SERVER_ID);
-
- let is_isolated = arbiter.is_connection_isolated(conn_id);
-
- assert!(is_isolated)
+ fn create_manager_with_isolated_connection(
+ tcb_idx: TransportIndex,
+ server_id: ServerId,
+ ) -> IsolationManager {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(server_id, ADVERTISER_ID);
+ isolation_manager.on_le_connect(tcb_idx, ADVERTISER_ID);
+ isolation_manager
}
#[test]
fn test_packet_capture_when_isolated() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
+ let isolation_manager = create_manager_with_isolated_connection(TCB_IDX, SERVER_ID);
let packet = AttBuilder {
opcode: AttOpcode::READ_REQUEST,
_child_: AttReadRequestBuilder { attribute_handle: AttHandle(1).into() }.into(),
};
- let out = arbiter.try_parse_att_server_packet(TCB_IDX, packet.to_vec().unwrap().into());
+ let out = try_parse_att_server_packet(
+ &isolation_manager,
+ TCB_IDX,
+ packet.to_vec().unwrap().into(),
+ );
assert!(out.is_some());
}
#[test]
fn test_packet_bypass_when_isolated() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
+ let isolation_manager = create_manager_with_isolated_connection(TCB_IDX, SERVER_ID);
let packet = AttBuilder {
opcode: AttOpcode::ERROR_RESPONSE,
_child_: AttReadRequestBuilder { attribute_handle: AttHandle(1).into() }.into(),
};
- let out = arbiter.try_parse_att_server_packet(TCB_IDX, packet.to_vec().unwrap().into());
+ let out = try_parse_att_server_packet(
+ &isolation_manager,
+ TCB_IDX,
+ packet.to_vec().unwrap().into(),
+ );
assert!(out.is_none());
}
#[test]
fn test_mtu_bypass() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
+ let isolation_manager = create_manager_with_isolated_connection(TCB_IDX, SERVER_ID);
let packet = AttBuilder {
opcode: AttOpcode::EXCHANGE_MTU_REQUEST,
_child_: AttExchangeMtuRequestBuilder { mtu: 64 }.into(),
};
- let out = arbiter.try_parse_att_server_packet(TCB_IDX, packet.to_vec().unwrap().into());
+ let out = try_parse_att_server_packet(
+ &isolation_manager,
+ TCB_IDX,
+ packet.to_vec().unwrap().into(),
+ );
assert!(out.is_none());
}
#[test]
fn test_packet_bypass_when_not_isolated() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ANOTHER_ADVERTISER_ID);
- let packet = AttBuilder {
- opcode: AttOpcode::READ_REQUEST,
- _child_: AttReadRequestBuilder { attribute_handle: AttHandle(1).into() }.into(),
- };
-
- let out = arbiter.try_parse_att_server_packet(TCB_IDX, packet.to_vec().unwrap().into());
-
- assert!(out.is_none());
- }
-
- #[test]
- fn test_packet_bypass_when_different_connection() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
- arbiter.on_le_connect(ANOTHER_TCB_IDX, ANOTHER_ADVERTISER_ID);
+ let isolation_manager = IsolationManager::new();
let packet = AttBuilder {
opcode: AttOpcode::READ_REQUEST,
_child_: AttReadRequestBuilder { attribute_handle: AttHandle(1).into() }.into(),
};
- let out =
- arbiter.try_parse_att_server_packet(ANOTHER_TCB_IDX, packet.to_vec().unwrap().into());
+ let out = try_parse_att_server_packet(
+ &isolation_manager,
+ TCB_IDX,
+ packet.to_vec().unwrap().into(),
+ );
assert!(out.is_none());
}
-
- #[test]
- fn test_packet_capture_when_isolated_after_advertiser_closes() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
- let packet = AttBuilder {
- opcode: AttOpcode::READ_REQUEST,
- _child_: AttReadRequestBuilder { attribute_handle: AttHandle(1).into() }.into(),
- };
- arbiter.clear_advertiser(ADVERTISER_ID);
-
- let out = arbiter.try_parse_att_server_packet(TCB_IDX, packet.to_vec().unwrap().into());
-
- assert!(out.is_some());
- }
-
- #[test]
- fn test_packet_capture_when_isolated_after_server_closes() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
- let packet = AttBuilder {
- opcode: AttOpcode::READ_REQUEST,
- _child_: AttReadRequestBuilder { attribute_handle: AttHandle(1).into() }.into(),
- };
- arbiter.clear_server(SERVER_ID);
-
- let out = arbiter.try_parse_att_server_packet(TCB_IDX, packet.to_vec().unwrap().into());
-
- assert!(out.is_some());
- }
-
- #[test]
- fn test_not_isolated_after_disconnection() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- arbiter.on_le_disconnect(TCB_IDX);
- let is_isolated = arbiter.is_connection_isolated(CONN_ID);
-
- assert!(!is_isolated);
- }
-
- #[test]
- fn test_tcb_idx_reuse_after_isolated() {
- let mut arbiter = Arbiter::new();
- arbiter.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
- arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
- arbiter.clear_advertiser(ADVERTISER_ID);
- arbiter.on_le_disconnect(TCB_IDX);
-
- let conn_id = arbiter.on_le_connect(TCB_IDX, ADVERTISER_ID);
-
- assert!(conn_id.is_none());
- assert!(!arbiter.is_connection_isolated(CONN_ID));
- }
}
diff --git a/system/rust/src/gatt/server.rs b/system/rust/src/gatt/server.rs
index 09fd87964b..132779cf08 100644
--- a/system/rust/src/gatt/server.rs
+++ b/system/rust/src/gatt/server.rs
@@ -10,6 +10,7 @@ pub mod services;
mod transactions;
mod command_handler;
+pub mod isolation_manager;
#[cfg(test)]
mod test;
diff --git a/system/rust/src/gatt/server/isolation_manager.rs b/system/rust/src/gatt/server/isolation_manager.rs
new file mode 100644
index 0000000000..ea7fbc24c0
--- /dev/null
+++ b/system/rust/src/gatt/server/isolation_manager.rs
@@ -0,0 +1,242 @@
+//! This module determines which GATT server should be exposed to a given connection.
+
+use std::collections::HashMap;
+
+use log::{error, info};
+
+use crate::gatt::ids::{AdvertiserId, ConnectionId, ServerId, TransportIndex};
+
+/// This class is responsible for tracking which connections and advertising we
+/// own, and using this information to decide what servers should be exposed to
+/// a given connetion.
+#[derive(Default)]
+pub struct IsolationManager {
+ advertiser_to_server: HashMap<AdvertiserId, ServerId>,
+ transport_to_owned_connection: HashMap<TransportIndex, ConnectionId>,
+}
+
+impl IsolationManager {
+ /// Constructor
+ pub fn new() -> Self {
+ IsolationManager {
+ advertiser_to_server: HashMap::new(),
+ transport_to_owned_connection: HashMap::new(),
+ }
+ }
+
+ /// Link a given GATT server to an LE advertising set, so incoming
+ /// connections to this advertiser will be visible only by the linked
+ /// server
+ pub fn associate_server_with_advertiser(
+ &mut self,
+ server_id: ServerId,
+ advertiser_id: AdvertiserId,
+ ) {
+ info!("associating server {server_id:?} with advertising set {advertiser_id:?}");
+ let old = self.advertiser_to_server.insert(advertiser_id, server_id);
+ if let Some(old) = old {
+ error!("new server {server_id:?} associated with same advertiser {advertiser_id:?}, displacing old server {old:?}");
+ }
+ }
+
+ /// Remove all linked advertising sets from the provided server
+ pub fn clear_server(&mut self, server_id: ServerId) {
+ info!("clearing advertisers associated with {server_id:?}");
+ self.advertiser_to_server.retain(|_, server| *server != server_id);
+ }
+
+ /// Clear the server associated with this advertiser, if one exists
+ pub fn clear_advertiser(&mut self, advertiser_id: AdvertiserId) {
+ info!("removing server (if any) associated with advertiser {advertiser_id:?}");
+ self.advertiser_to_server.remove(&advertiser_id);
+ }
+
+ /// Check if this conn_id is currently owned by the Rust stack
+ pub fn is_connection_isolated(&self, conn_id: ConnectionId) -> bool {
+ self.transport_to_owned_connection.values().any(|owned_conn_id| *owned_conn_id == conn_id)
+ }
+
+ /// Look up the conn_id for a given tcb_idx, if present
+ pub fn get_conn_id(&self, tcb_idx: TransportIndex) -> Option<ConnectionId> {
+ self.transport_to_owned_connection.get(&tcb_idx).copied()
+ }
+
+ /// Handles an incoming connection, and reports if it should be isolated to a given connection ID
+ pub fn on_le_connect(
+ &mut self,
+ tcb_idx: TransportIndex,
+ advertiser: AdvertiserId,
+ ) -> Option<ConnectionId> {
+ info!(
+ "processing incoming connection on transport {tcb_idx:?} to advertiser {advertiser:?}"
+ );
+ let server_id = *self.advertiser_to_server.get(&advertiser)?;
+ info!("connection is isolated to server {server_id:?}");
+
+ let conn_id = ConnectionId::new(tcb_idx, server_id);
+ let old = self.transport_to_owned_connection.insert(tcb_idx, conn_id);
+ if old.is_some() {
+ error!("new server {server_id:?} on transport {tcb_idx:?} displacing existing registered connection {conn_id:?}")
+ }
+ Some(conn_id)
+ }
+
+ /// Handle a disconnection, if any, and return whether the disconnection was registered
+ pub fn on_le_disconnect(&mut self, tcb_idx: TransportIndex) -> bool {
+ info!("processing disconnection on transport {tcb_idx:?}");
+ self.transport_to_owned_connection.remove(&tcb_idx).is_some()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ const TCB_IDX: TransportIndex = TransportIndex(1);
+ const ADVERTISER_ID: AdvertiserId = AdvertiserId(3);
+ const SERVER_ID: ServerId = ServerId(4);
+
+ const CONN_ID: ConnectionId = ConnectionId::new(TCB_IDX, SERVER_ID);
+
+ const ANOTHER_ADVERTISER_ID: AdvertiserId = AdvertiserId(5);
+
+ #[test]
+ fn test_non_isolated_connect() {
+ let mut isolation_manager = IsolationManager::new();
+
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ assert!(conn_id.is_none())
+ }
+
+ #[test]
+ fn test_isolated_connect() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ assert_eq!(conn_id, Some(CONN_ID));
+ }
+
+ #[test]
+ fn test_non_isolated_connect_with_isolated_advertiser() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ANOTHER_ADVERTISER_ID);
+
+ assert!(conn_id.is_none())
+ }
+
+ #[test]
+ fn test_non_isolated_disconnect() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ let ok = isolation_manager.on_le_disconnect(TCB_IDX);
+
+ assert!(!ok)
+ }
+
+ #[test]
+ fn test_isolated_disconnect() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ let ok = isolation_manager.on_le_disconnect(TCB_IDX);
+
+ assert!(ok)
+ }
+
+ #[test]
+ fn test_advertiser_id_reuse() {
+ let mut isolation_manager = IsolationManager::new();
+ // start an advertiser associated with the server, then kill the advertiser
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ isolation_manager.clear_advertiser(ADVERTISER_ID);
+
+ // a new advertiser appeared with the same ID and got a connection
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ // but we should not be isolated since this is a new advertiser reusing the old
+ // ID
+ assert!(conn_id.is_none())
+ }
+
+ #[test]
+ fn test_server_closed() {
+ let mut isolation_manager = IsolationManager::new();
+ // start an advertiser associated with the server, then kill the server
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ isolation_manager.clear_server(SERVER_ID);
+
+ // then afterwards we get a connection to this advertiser
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ // since the server is gone, we should not capture the connection
+ assert!(conn_id.is_none())
+ }
+
+ #[test]
+ fn test_connection_isolated() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID).unwrap();
+
+ let is_isolated = isolation_manager.is_connection_isolated(conn_id);
+
+ assert!(is_isolated)
+ }
+
+ #[test]
+ fn test_connection_isolated_after_advertiser_stops() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID).unwrap();
+ isolation_manager.clear_advertiser(ADVERTISER_ID);
+
+ let is_isolated = isolation_manager.is_connection_isolated(conn_id);
+
+ assert!(is_isolated)
+ }
+
+ #[test]
+ fn test_connection_isolated_after_server_stops() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID).unwrap();
+ isolation_manager.clear_server(SERVER_ID);
+
+ let is_isolated = isolation_manager.is_connection_isolated(conn_id);
+
+ assert!(is_isolated)
+ }
+
+ #[test]
+ fn test_not_isolated_after_disconnection() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ isolation_manager.on_le_disconnect(TCB_IDX);
+ let is_isolated = isolation_manager.is_connection_isolated(CONN_ID);
+
+ assert!(!is_isolated);
+ }
+
+ #[test]
+ fn test_tcb_idx_reuse_after_isolated() {
+ let mut isolation_manager = IsolationManager::new();
+ isolation_manager.associate_server_with_advertiser(SERVER_ID, ADVERTISER_ID);
+ isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+ isolation_manager.clear_advertiser(ADVERTISER_ID);
+ isolation_manager.on_le_disconnect(TCB_IDX);
+
+ let conn_id = isolation_manager.on_le_connect(TCB_IDX, ADVERTISER_ID);
+
+ assert!(conn_id.is_none());
+ assert!(!isolation_manager.is_connection_isolated(CONN_ID));
+ }
+}