1 ///! Rule group for general information.
2 use chrono::NaiveDateTime;
3 use std::cmp::Ordering;
4 use std::collections::{HashMap, HashSet};
5 use std::convert::Into;
6 use std::fmt;
7 use std::hash::Hash;
8 use std::io::Write;
9
10 use crate::engine::{Rule, RuleGroup, Signal};
11 use crate::parser::{get_acl_content, AclContent, Packet, PacketChild};
12 use hcidoc_packets::hci::{
13 Address, CommandChild, DisconnectReason, ErrorCode, EventChild, GapData, GapDataType,
14 LeMetaEventChild,
15 };
16 use hcidoc_packets::l2cap::{ConnectionResponseResult, ControlChild};
17
18 /// Valid values are in the range 0x0000-0x0EFF.
19 type ConnectionHandle = u16;
20
21 type Psm = u16;
22 type Cid = u16;
23
24 const INVALID_TS: NaiveDateTime = NaiveDateTime::MAX;
25
print_timestamps_and_initiator( start: NaiveDateTime, start_initiator: InitiatorType, end: NaiveDateTime, end_initiator: InitiatorType, ) -> String26 fn print_timestamps_and_initiator(
27 start: NaiveDateTime,
28 start_initiator: InitiatorType,
29 end: NaiveDateTime,
30 end_initiator: InitiatorType,
31 ) -> String {
32 fn print_time_initiator(ts: NaiveDateTime, initiator: InitiatorType) -> String {
33 if ts == INVALID_TS {
34 return "N/A".to_owned();
35 }
36 return format!("{} ({})", ts.time(), initiator);
37 }
38
39 if start == end && start != INVALID_TS {
40 return format!("{} ({}) - Failed", start.time(), start_initiator);
41 }
42 return format!(
43 "{} to {}",
44 print_time_initiator(start, start_initiator),
45 print_time_initiator(end, end_initiator)
46 );
47 }
48
49 #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
50 enum AddressType {
51 None,
52 BREDR,
53 LE,
54 Dual,
55 }
56
57 impl fmt::Display for AddressType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 let str = match self {
60 AddressType::None => "Unknown type",
61 AddressType::BREDR => "BR/EDR",
62 AddressType::LE => "LE",
63 AddressType::Dual => "Dual",
64 };
65 write!(f, "{}", str)
66 }
67 }
68
69 impl AddressType {
update(&mut self, new_type: AddressType)70 fn update(&mut self, new_type: AddressType) {
71 *self = match self {
72 AddressType::None => new_type,
73 AddressType::Dual => AddressType::Dual,
74 AddressType::BREDR => match new_type {
75 AddressType::Dual | AddressType::LE => AddressType::Dual,
76 _ => AddressType::BREDR,
77 },
78 AddressType::LE => match new_type {
79 AddressType::Dual | AddressType::BREDR => AddressType::Dual,
80 _ => AddressType::LE,
81 },
82 }
83 }
84 }
85
86 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
87 enum Transport {
88 Unknown,
89 BREDR,
90 LE,
91 }
92
93 impl fmt::Display for Transport {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 let str = match self {
96 Transport::Unknown => "??",
97 Transport::BREDR => "BR",
98 Transport::LE => "LE",
99 };
100 write!(f, "{}", str)
101 }
102 }
103
104 #[derive(Clone, Copy, PartialEq)]
105 enum InitiatorType {
106 Unknown,
107 Host,
108 Peer,
109 }
110
111 impl fmt::Display for InitiatorType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 let str = match self {
114 InitiatorType::Unknown => "by ??",
115 InitiatorType::Host => "by host",
116 InitiatorType::Peer => "by peer",
117 };
118 write!(f, "{}", str)
119 }
120 }
121
122 #[derive(Copy, Clone)]
123 enum AclState {
124 None,
125 Initiating,
126 Accepting,
127 Connected,
128 }
129
130 impl AclState {
get_connection_initiator(&self) -> InitiatorType131 fn get_connection_initiator(&self) -> InitiatorType {
132 match self {
133 AclState::Initiating => InitiatorType::Host,
134 AclState::Accepting => InitiatorType::Peer,
135 _ => InitiatorType::Unknown,
136 }
137 }
138 }
139
140 /// Information about a specific device address
141 struct DeviceInformation {
142 names: HashSet<String>,
143 address: Address,
144 address_type: AddressType,
145 acls: HashMap<Transport, Vec<AclInformation>>,
146 acl_state: HashMap<Transport, AclState>,
147 }
148
149 impl DeviceInformation {
new(address: Address) -> Self150 pub fn new(address: Address) -> Self {
151 DeviceInformation {
152 names: HashSet::new(),
153 address: address,
154 address_type: AddressType::None,
155 acls: HashMap::from([(Transport::BREDR, vec![]), (Transport::LE, vec![])]),
156 acl_state: HashMap::from([
157 (Transport::BREDR, AclState::None),
158 (Transport::LE, AclState::None),
159 ]),
160 }
161 }
162
is_connection_active(&self, transport: Transport) -> bool163 fn is_connection_active(&self, transport: Transport) -> bool {
164 if transport == Transport::Unknown {
165 return false;
166 }
167
168 // not empty and last connection's end time is not set.
169 return !self.acls[&transport].is_empty()
170 && self.acls[&transport].last().unwrap().end_time == INVALID_TS;
171 }
172
get_or_allocate_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation173 fn get_or_allocate_connection(
174 &mut self,
175 handle: ConnectionHandle,
176 transport: Transport,
177 ) -> &mut AclInformation {
178 assert_ne!(transport, Transport::Unknown, "device allocating unknown transport");
179 if !self.is_connection_active(transport) {
180 let acl = AclInformation::new(handle, transport);
181 self.acls.get_mut(&transport).unwrap().push(acl);
182 }
183 return self.acls.get_mut(&transport).unwrap().last_mut().unwrap();
184 }
185
report_connection_start( &mut self, handle: ConnectionHandle, transport: Transport, ts: NaiveDateTime, )186 fn report_connection_start(
187 &mut self,
188 handle: ConnectionHandle,
189 transport: Transport,
190 ts: NaiveDateTime,
191 ) {
192 if transport == Transport::Unknown {
193 return;
194 }
195
196 let mut acl = AclInformation::new(handle, transport);
197 let initiator = self.acl_state[&transport].get_connection_initiator();
198 acl.report_start(initiator, ts);
199 self.acls.get_mut(&transport).unwrap().push(acl);
200 self.acl_state.insert(transport, AclState::Connected);
201 }
202
report_connection_end( &mut self, handle: ConnectionHandle, initiator: InitiatorType, ts: NaiveDateTime, )203 fn report_connection_end(
204 &mut self,
205 handle: ConnectionHandle,
206 initiator: InitiatorType,
207 ts: NaiveDateTime,
208 ) {
209 for transport in [Transport::BREDR, Transport::LE] {
210 if self.is_connection_active(transport) {
211 if self.acls[&transport].last().unwrap().handle == handle {
212 self.acls
213 .get_mut(&transport)
214 .unwrap()
215 .last_mut()
216 .unwrap()
217 .report_end(initiator, ts);
218 self.acl_state.insert(transport, AclState::None);
219 return;
220 }
221 }
222 }
223
224 eprintln!(
225 "device {} receive disconnection of handle {} without corresponding connection at {}",
226 self.address, handle, ts
227 );
228 }
229
print_names(names: &HashSet<String>) -> String230 fn print_names(names: &HashSet<String>) -> String {
231 if names.len() > 1 {
232 format!("{:?}", names)
233 } else {
234 names.iter().next().unwrap_or(&String::from("<Unknown name>")).to_owned()
235 }
236 }
237 }
238
239 impl fmt::Display for DeviceInformation {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 let _ = writeln!(
242 f,
243 "{address} ({address_type}, {device_names}), {num_connections} connections",
244 address = self.address,
245 address_type = self.address_type,
246 device_names = DeviceInformation::print_names(&self.names),
247 num_connections = self.acls.len()
248 );
249 for acl in &self.acls[&Transport::BREDR] {
250 let _ = write!(f, "{}", acl);
251 }
252 for acl in &self.acls[&Transport::LE] {
253 let _ = write!(f, "{}", acl);
254 }
255
256 Ok(())
257 }
258 }
259
260 #[derive(Debug)]
261 enum CidState {
262 Pending(Psm),
263 Connected(Cid, Psm),
264 }
265
266 /// Information for an ACL connection session
267 struct AclInformation {
268 start_time: NaiveDateTime,
269 end_time: NaiveDateTime,
270 handle: ConnectionHandle,
271 transport: Transport,
272 start_initiator: InitiatorType,
273 end_initiator: InitiatorType,
274 active_profiles: HashMap<ProfileId, ProfileInformation>,
275 inactive_profiles: Vec<ProfileInformation>,
276 host_cids: HashMap<Cid, CidState>,
277 peer_cids: HashMap<Cid, CidState>,
278 }
279
280 impl AclInformation {
new(handle: ConnectionHandle, transport: Transport) -> Self281 pub fn new(handle: ConnectionHandle, transport: Transport) -> Self {
282 AclInformation {
283 start_time: INVALID_TS,
284 end_time: INVALID_TS,
285 handle,
286 transport,
287 start_initiator: InitiatorType::Unknown,
288 end_initiator: InitiatorType::Unknown,
289 active_profiles: HashMap::new(),
290 inactive_profiles: vec![],
291 host_cids: HashMap::new(),
292 peer_cids: HashMap::new(),
293 }
294 }
295
report_start(&mut self, initiator: InitiatorType, ts: NaiveDateTime)296 fn report_start(&mut self, initiator: InitiatorType, ts: NaiveDateTime) {
297 self.start_initiator = initiator;
298 self.start_time = ts;
299 }
300
report_end(&mut self, initiator: InitiatorType, ts: NaiveDateTime)301 fn report_end(&mut self, initiator: InitiatorType, ts: NaiveDateTime) {
302 // disconnect the active profiles
303 for (_, mut profile) in self.active_profiles.drain() {
304 profile.report_end(initiator, ts);
305 self.inactive_profiles.push(profile);
306 }
307 self.end_initiator = initiator;
308 self.end_time = ts;
309 }
310
report_profile_start( &mut self, profile_type: ProfileType, profile_id: ProfileId, initiator: InitiatorType, ts: NaiveDateTime, )311 fn report_profile_start(
312 &mut self,
313 profile_type: ProfileType,
314 profile_id: ProfileId,
315 initiator: InitiatorType,
316 ts: NaiveDateTime,
317 ) {
318 let mut profile = ProfileInformation::new(profile_type, profile_id);
319 profile.report_start(initiator, ts);
320 let old_profile = self.active_profiles.insert(profile_id, profile);
321 if let Some(profile) = old_profile {
322 self.inactive_profiles.push(profile);
323 }
324 }
325
report_profile_end( &mut self, profile_type: ProfileType, profile_id: ProfileId, initiator: InitiatorType, ts: NaiveDateTime, )326 fn report_profile_end(
327 &mut self,
328 profile_type: ProfileType,
329 profile_id: ProfileId,
330 initiator: InitiatorType,
331 ts: NaiveDateTime,
332 ) {
333 let mut profile = self
334 .active_profiles
335 .remove(&profile_id)
336 .unwrap_or(ProfileInformation::new(profile_type, profile_id));
337 profile.report_end(initiator, ts);
338 self.inactive_profiles.push(profile);
339 }
340
report_l2cap_conn_req( &mut self, psm: Psm, cid: Cid, initiator: InitiatorType, _ts: NaiveDateTime, )341 fn report_l2cap_conn_req(
342 &mut self,
343 psm: Psm,
344 cid: Cid,
345 initiator: InitiatorType,
346 _ts: NaiveDateTime,
347 ) {
348 if initiator == InitiatorType::Host {
349 self.host_cids.insert(cid, CidState::Pending(psm));
350 } else if initiator == InitiatorType::Peer {
351 self.peer_cids.insert(cid, CidState::Pending(psm));
352 }
353 }
354
355 // For pending connections, we report whether the PSM successfully connected and
356 // store the profile as started at this time.
report_l2cap_conn_rsp( &mut self, status: ConnectionResponseResult, cid_info: CidInformation, initiator: InitiatorType, ts: NaiveDateTime, )357 fn report_l2cap_conn_rsp(
358 &mut self,
359 status: ConnectionResponseResult,
360 cid_info: CidInformation,
361 initiator: InitiatorType,
362 ts: NaiveDateTime,
363 ) {
364 let host_cid = cid_info.host_cid;
365 let peer_cid = cid_info.peer_cid;
366 let cid_state_option = match initiator {
367 InitiatorType::Host => self.host_cids.get(&host_cid),
368 InitiatorType::Peer => self.peer_cids.get(&peer_cid),
369 _ => None,
370 };
371
372 let psm_option = match cid_state_option {
373 Some(cid_state) => match cid_state {
374 CidState::Pending(psm) => Some(*psm),
375 _ => None,
376 },
377 None => None,
378 };
379
380 if let Some(psm) = psm_option {
381 let profile_option = ProfileType::from_psm(psm);
382 let profile_id = ProfileId::L2capCid(cid_info);
383 if status == ConnectionResponseResult::Success {
384 self.host_cids.insert(host_cid, CidState::Connected(peer_cid, psm));
385 self.peer_cids.insert(peer_cid, CidState::Connected(host_cid, psm));
386 if let Some(profile) = profile_option {
387 self.report_profile_start(profile, profile_id, initiator, ts);
388 }
389 } else {
390 // On failure, report start and end on the same time.
391 if let Some(profile) = profile_option {
392 self.report_profile_start(profile, profile_id, initiator, ts);
393 self.report_profile_end(profile, profile_id, initiator, ts);
394 }
395 }
396 } // TODO: debug on the else case.
397 }
398
399 // L2cap disconnected so report profile connection closed if we were tracking it.
report_l2cap_disconn_rsp( &mut self, cid_info: CidInformation, initiator: InitiatorType, ts: NaiveDateTime, )400 fn report_l2cap_disconn_rsp(
401 &mut self,
402 cid_info: CidInformation,
403 initiator: InitiatorType,
404 ts: NaiveDateTime,
405 ) {
406 let host_cid = cid_info.host_cid;
407 let host_cid_state_option = self.host_cids.remove(&host_cid);
408 let host_psm = match host_cid_state_option {
409 Some(cid_state) => match cid_state {
410 // TODO: assert that the peer cids match.
411 CidState::Connected(_peer_cid, psm) => Some(psm),
412 _ => None, // TODO: assert that state is connected.
413 },
414 None => None,
415 };
416
417 let peer_cid = cid_info.peer_cid;
418 let peer_cid_state_option = self.peer_cids.remove(&peer_cid);
419 let peer_psm = match peer_cid_state_option {
420 Some(cid_state) => match cid_state {
421 // TODO: assert that the host cids match.
422 CidState::Connected(_host_cid, psm) => Some(psm),
423 _ => None, // TODO: assert that state is connected.
424 },
425 None => None,
426 };
427
428 if host_psm != peer_psm {
429 eprintln!(
430 "psm for host and peer mismatches at l2cap disc for handle {} at {}",
431 self.handle, ts
432 );
433 }
434 let psm = match host_psm.or(peer_psm) {
435 Some(psm) => psm,
436 None => return, // No recorded PSM, no need to report.
437 };
438
439 let profile_option = ProfileType::from_psm(psm);
440 if let Some(profile) = profile_option {
441 let profile_id = ProfileId::L2capCid(cid_info);
442 self.report_profile_end(profile, profile_id, initiator, ts)
443 }
444 }
445 }
446
447 impl fmt::Display for AclInformation {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 let _ = writeln!(
450 f,
451 " Handle: {handle} ({transport}), {timestamp_initiator_info}",
452 transport = self.transport,
453 handle = self.handle,
454 timestamp_initiator_info = print_timestamps_and_initiator(
455 self.start_time,
456 self.start_initiator,
457 self.end_time,
458 self.end_initiator
459 ),
460 );
461
462 for profile in self.inactive_profiles.iter() {
463 let _ = write!(f, "{}", profile);
464 }
465 for (_, profile) in self.active_profiles.iter() {
466 let _ = write!(f, "{}", profile);
467 }
468
469 Ok(())
470 }
471 }
472
473 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
474 enum ProfileType {
475 Att,
476 Avctp,
477 Avdtp,
478 Eatt,
479 Hfp,
480 HidCtrl,
481 HidIntr,
482 Rfcomm,
483 Sdp,
484 }
485
486 impl fmt::Display for ProfileType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result487 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488 let str = match self {
489 ProfileType::Att => "ATT",
490 ProfileType::Avctp => "AVCTP",
491 ProfileType::Avdtp => "AVDTP",
492 ProfileType::Eatt => "EATT",
493 ProfileType::Hfp => "HFP",
494 ProfileType::HidCtrl => "HID CTRL",
495 ProfileType::HidIntr => "HID INTR",
496 ProfileType::Rfcomm => "RFCOMM",
497 ProfileType::Sdp => "SDP",
498 };
499 write!(f, "{}", str)
500 }
501 }
502
503 impl ProfileType {
from_psm(psm: Psm) -> Option<Self>504 fn from_psm(psm: Psm) -> Option<Self> {
505 match psm {
506 1 => Some(ProfileType::Sdp),
507 3 => Some(ProfileType::Rfcomm),
508 17 => Some(ProfileType::HidCtrl),
509 19 => Some(ProfileType::HidIntr),
510 23 => Some(ProfileType::Avctp),
511 25 => Some(ProfileType::Avdtp),
512 31 => Some(ProfileType::Att),
513 39 => Some(ProfileType::Eatt),
514 _ => None,
515 }
516 }
517 }
518
519 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
520 struct CidInformation {
521 host_cid: Cid,
522 peer_cid: Cid,
523 }
524
525 // Use to distinguish between the same profiles within one ACL connection.
526 // Later we can add RFCOMM's DLCI, for example.
527 // This is used as the key of the map of active profiles in AclInformation.
528 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
529 enum ProfileId {
530 OnePerConnection(ProfileType),
531 L2capCid(CidInformation),
532 }
533
534 impl fmt::Display for ProfileId {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result535 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536 let str = match self {
537 ProfileId::OnePerConnection(_) => "".to_string(),
538 ProfileId::L2capCid(cid_info) => {
539 format!("(CID: host={}, peer={})", cid_info.host_cid, cid_info.peer_cid)
540 }
541 };
542 write!(f, "{}", str)
543 }
544 }
545
546 struct ProfileInformation {
547 start_time: NaiveDateTime,
548 end_time: NaiveDateTime,
549 profile_type: ProfileType,
550 start_initiator: InitiatorType,
551 end_initiator: InitiatorType,
552 profile_id: ProfileId,
553 }
554
555 impl ProfileInformation {
new(profile_type: ProfileType, profile_id: ProfileId) -> Self556 pub fn new(profile_type: ProfileType, profile_id: ProfileId) -> Self {
557 ProfileInformation {
558 start_time: INVALID_TS,
559 end_time: INVALID_TS,
560 profile_type: profile_type,
561 start_initiator: InitiatorType::Unknown,
562 end_initiator: InitiatorType::Unknown,
563 profile_id: profile_id,
564 }
565 }
566
report_start(&mut self, initiator: InitiatorType, ts: NaiveDateTime)567 fn report_start(&mut self, initiator: InitiatorType, ts: NaiveDateTime) {
568 self.start_initiator = initiator;
569 self.start_time = ts;
570 }
571
report_end(&mut self, initiator: InitiatorType, ts: NaiveDateTime)572 fn report_end(&mut self, initiator: InitiatorType, ts: NaiveDateTime) {
573 self.end_initiator = initiator;
574 self.end_time = ts;
575 }
576 }
577
578 impl fmt::Display for ProfileInformation {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580 writeln!(
581 f,
582 " {profile}, {timestamp_initiator_info} {profile_id}",
583 profile = self.profile_type,
584 timestamp_initiator_info = print_timestamps_and_initiator(
585 self.start_time,
586 self.start_initiator,
587 self.end_time,
588 self.end_initiator
589 ),
590 profile_id = self.profile_id,
591 )
592 }
593 }
594
595 /// This rule prints devices names and connection/disconnection time.
596 struct InformationalRule {
597 devices: HashMap<Address, DeviceInformation>,
598 handles: HashMap<ConnectionHandle, Address>,
599 sco_handles: HashMap<ConnectionHandle, ConnectionHandle>,
600 /// unknownConnections store connections which is initiated before btsnoop starts.
601 unknown_connections: HashMap<ConnectionHandle, AclInformation>,
602 /// Store the pending disconnection so we can retrieve who initiates it upon report.
603 /// This needs its own map instead of reusing the AclState, because that requires us to have the
604 /// address of the peer device, but on disconnection we are given only the handle - the address
605 /// might be unknown, or clash in case of a SCO connection.
606 /// Also, when powering off, the controller might or might not reply the disconnection request.
607 /// Therefore also store this information so we can correctly handle both scenario.
608 pending_disconnections: HashMap<ConnectionHandle, bool>, // is powering off?
609 }
610
611 impl InformationalRule {
new() -> Self612 pub fn new() -> Self {
613 InformationalRule {
614 devices: HashMap::new(),
615 handles: HashMap::new(),
616 sco_handles: HashMap::new(),
617 unknown_connections: HashMap::new(),
618 pending_disconnections: HashMap::new(),
619 }
620 }
621
get_or_allocate_device(&mut self, address: &Address) -> &mut DeviceInformation622 fn get_or_allocate_device(&mut self, address: &Address) -> &mut DeviceInformation {
623 if !self.devices.contains_key(address) {
624 self.devices.insert(*address, DeviceInformation::new(*address));
625 }
626 return self.devices.get_mut(address).unwrap();
627 }
628
get_or_allocate_unknown_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation629 fn get_or_allocate_unknown_connection(
630 &mut self,
631 handle: ConnectionHandle,
632 transport: Transport,
633 ) -> &mut AclInformation {
634 if !self.unknown_connections.contains_key(&handle) {
635 self.unknown_connections.insert(handle, AclInformation::new(handle, transport));
636 }
637 return self.unknown_connections.get_mut(&handle).unwrap();
638 }
639
get_or_allocate_connection( &mut self, handle: ConnectionHandle, transport: Transport, ) -> &mut AclInformation640 fn get_or_allocate_connection(
641 &mut self,
642 handle: ConnectionHandle,
643 transport: Transport,
644 ) -> &mut AclInformation {
645 if !self.handles.contains_key(&handle) || transport == Transport::Unknown {
646 let conn = self.get_or_allocate_unknown_connection(handle, transport);
647 return conn;
648 }
649
650 let address = &self.handles.get(&handle).unwrap().clone();
651 let device = self.get_or_allocate_device(address);
652 return device.get_or_allocate_connection(handle, transport);
653 }
654
report_address_type(&mut self, address: &Address, address_type: AddressType)655 fn report_address_type(&mut self, address: &Address, address_type: AddressType) {
656 let device = self.get_or_allocate_device(address);
657 device.address_type.update(address_type);
658 }
659
report_name(&mut self, address: &Address, name: &String)660 fn report_name(&mut self, address: &Address, name: &String) {
661 let device = self.get_or_allocate_device(address);
662 device.names.insert(name.into());
663 }
664
report_acl_state(&mut self, address: &Address, transport: Transport, state: AclState)665 fn report_acl_state(&mut self, address: &Address, transport: Transport, state: AclState) {
666 let device = self.get_or_allocate_device(address);
667 device.acl_state.insert(transport, state);
668 }
669
report_connection_start( &mut self, address: &Address, handle: ConnectionHandle, transport: Transport, ts: NaiveDateTime, )670 fn report_connection_start(
671 &mut self,
672 address: &Address,
673 handle: ConnectionHandle,
674 transport: Transport,
675 ts: NaiveDateTime,
676 ) {
677 let device = self.get_or_allocate_device(address);
678 device.report_connection_start(handle, transport, ts);
679 self.handles.insert(handle, *address);
680 self.pending_disconnections.remove(&handle);
681 }
682
report_sco_connection_start( &mut self, address: &Address, handle: ConnectionHandle, ts: NaiveDateTime, )683 fn report_sco_connection_start(
684 &mut self,
685 address: &Address,
686 handle: ConnectionHandle,
687 ts: NaiveDateTime,
688 ) {
689 if !self.devices.contains_key(address) {
690 // To simplify things, let's not process unknown devices
691 return;
692 }
693
694 let device = self.devices.get_mut(address).unwrap();
695 if !device.is_connection_active(Transport::BREDR) {
696 // SCO is connected, but ACL is not. This is weird, but let's ignore for simplicity.
697 eprintln!("[{}] SCO is connected, but ACL is not.", address);
698 return;
699 }
700
701 // Whatever handle value works here - we aren't allocating a new one.
702 let acl = device.get_or_allocate_connection(0, Transport::BREDR);
703 let acl_handle = acl.handle;
704 // We need to listen the HCI commands to determine the correct initiator.
705 // Here we just assume host for simplicity.
706 acl.report_profile_start(
707 ProfileType::Hfp,
708 ProfileId::OnePerConnection(ProfileType::Hfp),
709 InitiatorType::Host,
710 ts,
711 );
712
713 self.sco_handles.insert(handle, acl_handle);
714 }
715
report_connection_end(&mut self, handle: ConnectionHandle, ts: NaiveDateTime)716 fn report_connection_end(&mut self, handle: ConnectionHandle, ts: NaiveDateTime) {
717 let initiator = match self.pending_disconnections.contains_key(&handle) {
718 true => InitiatorType::Host,
719 false => InitiatorType::Peer,
720 };
721
722 // This might be a SCO disconnection event, so check that first
723 if self.sco_handles.contains_key(&handle) {
724 let acl_handle = self.sco_handles[&handle];
725 let conn = self.get_or_allocate_connection(acl_handle, Transport::BREDR);
726 // in case of HFP failure, the initiator here would be set to peer, which is incorrect,
727 // but when printing we detect by the timestamp that it was a failure anyway.
728 conn.report_profile_end(
729 ProfileType::Hfp,
730 ProfileId::OnePerConnection(ProfileType::Hfp),
731 initiator,
732 ts,
733 );
734 return;
735 }
736
737 // Not recognized as SCO, assume it's an ACL handle.
738 if let Some(address) = self.handles.get(&handle) {
739 // This device is known
740 let device: &mut DeviceInformation = self.devices.get_mut(address).unwrap();
741 device.report_connection_end(handle, initiator, ts);
742 self.handles.remove(&handle);
743
744 // remove the associated SCO handle, if any
745 self.sco_handles.retain(|_sco_handle, acl_handle| *acl_handle != handle);
746 } else {
747 // Unknown device.
748 let conn = self.get_or_allocate_unknown_connection(handle, Transport::Unknown);
749 conn.report_end(initiator, ts);
750 }
751 }
752
report_reset(&mut self, ts: NaiveDateTime)753 fn report_reset(&mut self, ts: NaiveDateTime) {
754 // report_connection_end removes the entries from the map, so store all the keys first.
755 let handles: Vec<ConnectionHandle> = self.handles.keys().cloned().collect();
756 for handle in handles {
757 self.report_connection_end(handle, ts);
758 }
759 self.sco_handles.clear();
760 self.pending_disconnections.clear();
761 }
762
process_gap_data(&mut self, address: &Address, data: &GapData)763 fn process_gap_data(&mut self, address: &Address, data: &GapData) {
764 match data.data_type {
765 GapDataType::CompleteLocalName | GapDataType::ShortenedLocalName => {
766 let name = String::from_utf8_lossy(data.data.as_slice()).into_owned();
767 self.report_name(address, &name);
768 }
769
770 _ => {}
771 }
772 }
773
process_raw_gap_data(&mut self, address: &Address, data: &[u8])774 fn process_raw_gap_data(&mut self, address: &Address, data: &[u8]) {
775 let mut offset = 0;
776 while offset < data.len() {
777 match GapData::parse(&data[offset..]) {
778 Ok(gap_data) => {
779 self.process_gap_data(&address, &gap_data);
780 // advance data len + 2 (size = 1, type = 1)
781 offset += gap_data.data.len() + 2;
782 }
783 Err(err) => {
784 eprintln!("[{}] GAP data is not parsed correctly: {}", address, err);
785 break;
786 }
787 }
788 if offset >= data.len() {
789 break;
790 }
791 }
792 }
793
report_l2cap_conn_req( &mut self, handle: ConnectionHandle, psm: Psm, cid: Cid, initiator: InitiatorType, ts: NaiveDateTime, )794 fn report_l2cap_conn_req(
795 &mut self,
796 handle: ConnectionHandle,
797 psm: Psm,
798 cid: Cid,
799 initiator: InitiatorType,
800 ts: NaiveDateTime,
801 ) {
802 let conn = self.get_or_allocate_connection(handle, Transport::BREDR);
803 conn.report_l2cap_conn_req(psm, cid, initiator, ts);
804 }
805
report_l2cap_conn_rsp( &mut self, handle: ConnectionHandle, status: ConnectionResponseResult, host_cid: Cid, peer_cid: Cid, initiator: InitiatorType, ts: NaiveDateTime, )806 fn report_l2cap_conn_rsp(
807 &mut self,
808 handle: ConnectionHandle,
809 status: ConnectionResponseResult,
810 host_cid: Cid,
811 peer_cid: Cid,
812 initiator: InitiatorType,
813 ts: NaiveDateTime,
814 ) {
815 if status == ConnectionResponseResult::Pending {
816 return;
817 }
818 let conn = self.get_or_allocate_connection(handle, Transport::BREDR);
819 let cid_info = CidInformation { host_cid, peer_cid };
820 conn.report_l2cap_conn_rsp(status, cid_info, initiator, ts);
821 }
822
report_l2cap_disconn_rsp( &mut self, handle: ConnectionHandle, host_cid: Cid, peer_cid: Cid, initiator: InitiatorType, ts: NaiveDateTime, )823 fn report_l2cap_disconn_rsp(
824 &mut self,
825 handle: ConnectionHandle,
826 host_cid: Cid,
827 peer_cid: Cid,
828 initiator: InitiatorType,
829 ts: NaiveDateTime,
830 ) {
831 let conn = self.get_or_allocate_connection(handle, Transport::BREDR);
832 let cid_info = CidInformation { host_cid, peer_cid };
833 conn.report_l2cap_disconn_rsp(cid_info, initiator, ts);
834 }
835 }
836
837 impl Rule for InformationalRule {
process(&mut self, packet: &Packet)838 fn process(&mut self, packet: &Packet) {
839 match &packet.inner {
840 PacketChild::HciEvent(ev) => match ev.specialize() {
841 EventChild::ConnectionComplete(ev) => {
842 self.report_connection_start(
843 &ev.get_bd_addr(),
844 ev.get_connection_handle(),
845 Transport::BREDR,
846 packet.ts,
847 );
848
849 // If failed, assume it's the end of connection.
850 if ev.get_status() != ErrorCode::Success {
851 self.report_connection_end(ev.get_connection_handle(), packet.ts);
852 }
853 }
854
855 EventChild::SynchronousConnectionComplete(ev) => {
856 self.report_sco_connection_start(
857 &ev.get_bd_addr(),
858 ev.get_connection_handle(),
859 packet.ts,
860 );
861 // If failed, assume it's the end of connection.
862 if ev.get_status() != ErrorCode::Success {
863 self.report_connection_end(ev.get_connection_handle(), packet.ts);
864 }
865 }
866
867 EventChild::DisconnectionComplete(ev) => {
868 // If disconnected because host is powering off, the event has been processed.
869 // We can't just query the reason here because it's different across vendors.
870 let handle = ev.get_connection_handle();
871 if !self.pending_disconnections.get(&handle).unwrap_or(&false) {
872 self.report_connection_end(handle, packet.ts);
873 }
874 self.pending_disconnections.remove(&handle);
875 }
876
877 EventChild::ExtendedInquiryResult(ev) => {
878 self.process_raw_gap_data(
879 &ev.get_address(),
880 ev.get_extended_inquiry_response(),
881 );
882 self.report_address_type(&ev.get_address(), AddressType::BREDR);
883 }
884
885 EventChild::RemoteNameRequestComplete(ev) => {
886 if ev.get_status() != ErrorCode::Success {
887 return;
888 }
889 let name = String::from_utf8_lossy(ev.get_remote_name());
890 let name = name.trim_end_matches(char::from(0));
891 self.report_name(&ev.get_bd_addr(), &name.to_owned());
892 self.report_address_type(&ev.get_bd_addr(), AddressType::BREDR);
893 }
894
895 EventChild::LeMetaEvent(ev) => match ev.specialize() {
896 LeMetaEventChild::LeConnectionComplete(ev) => {
897 if ev.get_status() != ErrorCode::Success {
898 return;
899 }
900
901 // Determining LE initiator is complex, for simplicity assume host inits.
902 self.report_acl_state(
903 &ev.get_peer_address(),
904 Transport::LE,
905 AclState::Initiating,
906 );
907 self.report_connection_start(
908 &ev.get_peer_address(),
909 ev.get_connection_handle(),
910 Transport::LE,
911 packet.ts,
912 );
913 self.report_address_type(&ev.get_peer_address(), AddressType::LE);
914 }
915
916 LeMetaEventChild::LeEnhancedConnectionComplete(ev) => {
917 if ev.get_status() != ErrorCode::Success {
918 return;
919 }
920
921 // Determining LE initiator is complex, for simplicity assume host inits.
922 self.report_acl_state(
923 &ev.get_peer_address(),
924 Transport::LE,
925 AclState::Initiating,
926 );
927 self.report_connection_start(
928 &ev.get_peer_address(),
929 ev.get_connection_handle(),
930 Transport::LE,
931 packet.ts,
932 );
933 self.report_address_type(&ev.get_peer_address(), AddressType::LE);
934 }
935
936 LeMetaEventChild::LeAdvertisingReport(ev) => {
937 for resp in ev.get_responses() {
938 self.process_raw_gap_data(&resp.address, &resp.advertising_data);
939 self.report_address_type(&resp.address, AddressType::LE);
940 }
941 }
942
943 LeMetaEventChild::LeExtendedAdvertisingReport(ev) => {
944 for resp in ev.get_responses() {
945 self.process_raw_gap_data(&resp.address, &resp.advertising_data);
946 self.report_address_type(&resp.address, AddressType::LE);
947 }
948 }
949
950 // EventChild::LeMetaEvent(ev).specialize()
951 _ => {}
952 },
953
954 // PacketChild::HciEvent(ev) => match ev.specialize()
955 _ => {}
956 },
957
958 PacketChild::HciCommand(cmd) => match cmd.specialize() {
959 CommandChild::Reset(_cmd) => {
960 self.report_reset(packet.ts);
961 }
962 CommandChild::CreateConnection(cmd) => {
963 self.report_acl_state(
964 &cmd.get_bd_addr(),
965 Transport::BREDR,
966 AclState::Initiating,
967 );
968 self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR);
969 }
970 CommandChild::AcceptConnectionRequest(cmd) => {
971 self.report_acl_state(
972 &cmd.get_bd_addr(),
973 Transport::BREDR,
974 AclState::Accepting,
975 );
976 self.report_address_type(&cmd.get_bd_addr(), AddressType::BREDR);
977 }
978 CommandChild::Disconnect(cmd) => {
979 // If reason is power off, the host might not wait for connection complete event
980 let is_power_off = cmd.get_reason()
981 == DisconnectReason::RemoteDeviceTerminatedConnectionPowerOff;
982 let handle = cmd.get_connection_handle();
983 self.pending_disconnections.insert(handle, is_power_off);
984 if is_power_off {
985 self.report_connection_end(handle, packet.ts);
986 }
987 }
988
989 // PacketChild::HciCommand(cmd).specialize()
990 _ => {}
991 },
992
993 PacketChild::AclTx(tx) => {
994 let content = get_acl_content(tx);
995 match content {
996 AclContent::Control(control) => match control.specialize() {
997 ControlChild::ConnectionRequest(creq) => {
998 self.report_l2cap_conn_req(
999 tx.get_handle(),
1000 creq.get_psm(),
1001 creq.get_source_cid(),
1002 InitiatorType::Host,
1003 packet.ts,
1004 );
1005 }
1006 ControlChild::ConnectionResponse(crsp) => {
1007 self.report_l2cap_conn_rsp(
1008 tx.get_handle(),
1009 crsp.get_result(),
1010 crsp.get_destination_cid(),
1011 crsp.get_source_cid(),
1012 InitiatorType::Peer,
1013 packet.ts,
1014 );
1015 }
1016 ControlChild::DisconnectionResponse(drsp) => {
1017 self.report_l2cap_disconn_rsp(
1018 tx.get_handle(),
1019 drsp.get_destination_cid(),
1020 drsp.get_source_cid(),
1021 InitiatorType::Peer,
1022 packet.ts,
1023 );
1024 }
1025
1026 // AclContent::Control.specialize()
1027 _ => {}
1028 },
1029
1030 // PacketChild::AclTx(tx).specialize()
1031 _ => {}
1032 }
1033 }
1034
1035 PacketChild::AclRx(rx) => {
1036 let content = get_acl_content(rx);
1037 match content {
1038 AclContent::Control(control) => match control.specialize() {
1039 ControlChild::ConnectionRequest(creq) => {
1040 self.report_l2cap_conn_req(
1041 rx.get_handle(),
1042 creq.get_psm(),
1043 creq.get_source_cid(),
1044 InitiatorType::Peer,
1045 packet.ts,
1046 );
1047 }
1048 ControlChild::ConnectionResponse(crsp) => {
1049 self.report_l2cap_conn_rsp(
1050 rx.get_handle(),
1051 crsp.get_result(),
1052 crsp.get_source_cid(),
1053 crsp.get_destination_cid(),
1054 InitiatorType::Host,
1055 packet.ts,
1056 );
1057 }
1058 ControlChild::DisconnectionResponse(drsp) => {
1059 self.report_l2cap_disconn_rsp(
1060 rx.get_handle(),
1061 drsp.get_source_cid(),
1062 drsp.get_destination_cid(),
1063 InitiatorType::Host,
1064 packet.ts,
1065 );
1066 }
1067
1068 // AclContent::Control.specialize()
1069 _ => {}
1070 },
1071
1072 // PacketChild::AclRx(rx).specialize()
1073 _ => {}
1074 }
1075 }
1076
1077 // End packet.inner match
1078 _ => (),
1079 }
1080 }
1081
report(&self, writer: &mut dyn Write)1082 fn report(&self, writer: &mut dyn Write) {
1083 /* Sort when displaying the addresses, from the most to the least important:
1084 * (1) Device with connections > Device without connections
1085 * (2) Device with known name > Device with unknown name
1086 * (3) BREDR > LE > Dual
1087 * (4) Name, lexicographically (case sensitive)
1088 * (5) Address, alphabetically
1089 */
1090 fn sort_addresses(a: &DeviceInformation, b: &DeviceInformation) -> Ordering {
1091 let a_empty = a.acls[&Transport::BREDR].is_empty() && a.acls[&Transport::LE].is_empty();
1092 let b_empty = b.acls[&Transport::BREDR].is_empty() && b.acls[&Transport::LE].is_empty();
1093 let connection_order = a_empty.cmp(&b_empty);
1094 if connection_order != Ordering::Equal {
1095 return connection_order;
1096 }
1097
1098 let known_name_order = a.names.is_empty().cmp(&b.names.is_empty());
1099 if known_name_order != Ordering::Equal {
1100 return known_name_order;
1101 }
1102
1103 let address_type_order = a.address_type.cmp(&b.address_type);
1104 if address_type_order != Ordering::Equal {
1105 return address_type_order;
1106 }
1107
1108 let a_name = format!("{}", DeviceInformation::print_names(&a.names));
1109 let b_name = format!("{}", DeviceInformation::print_names(&b.names));
1110 let name_order = a_name.cmp(&b_name);
1111 if name_order != Ordering::Equal {
1112 return name_order;
1113 }
1114
1115 let a_address = <[u8; 6]>::from(a.address);
1116 let b_address = <[u8; 6]>::from(b.address);
1117 for i in (0..6).rev() {
1118 let address_order = a_address[i].cmp(&b_address[i]);
1119 if address_order != Ordering::Equal {
1120 return address_order;
1121 }
1122 }
1123 // This shouldn't be executed
1124 return Ordering::Equal;
1125 }
1126
1127 if self.devices.is_empty() && self.unknown_connections.is_empty() {
1128 return;
1129 }
1130
1131 let mut addresses: Vec<Address> = self.devices.keys().cloned().collect();
1132 addresses.sort_unstable_by(|a, b| sort_addresses(&self.devices[a], &self.devices[b]));
1133
1134 let _ = writeln!(writer, "InformationalRule report:");
1135 if !self.unknown_connections.is_empty() {
1136 let _ = writeln!(
1137 writer,
1138 "Connections initiated before snoop start, {} connections",
1139 self.unknown_connections.len()
1140 );
1141 for (_, acl) in &self.unknown_connections {
1142 let _ = write!(writer, "{}", acl);
1143 }
1144 }
1145 for address in addresses {
1146 let _ = write!(writer, "{}", self.devices[&address]);
1147 }
1148 }
1149
report_signals(&self) -> &[Signal]1150 fn report_signals(&self) -> &[Signal] {
1151 &[]
1152 }
1153 }
1154
1155 /// Get a rule group with collision rules.
get_informational_group() -> RuleGroup1156 pub fn get_informational_group() -> RuleGroup {
1157 let mut group = RuleGroup::new();
1158 group.add_rule(Box::new(InformationalRule::new()));
1159
1160 group
1161 }
1162