blob: a04cbea8bda28a2ba9e976b543bdbd866d859a27 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The internal structure of CaptureInfo and CaptureMaps
//!
//! CaptureInfo is the internal structure of any Capture that includes
//! the protobuf structure. CaptureMaps contains mappings of ChipId
//! and FacadeId to CaptureInfo.
use std::collections::btree_map::{Iter, Values};
use std::collections::{BTreeMap, HashMap};
use std::fs::{File, OpenOptions};
use std::io::Result;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
use frontend_proto::{
common::ChipKind,
model::{Capture as ProtoCapture, State},
};
use protobuf::well_known_types::timestamp::Timestamp;
use crate::ffi::get_facade_id;
use super::pcap_util::write_pcap_header;
pub type ChipId = i32;
pub type FacadeId = i32;
pub struct CaptureInfo {
facade_id: FacadeId,
pub file: Option<File>,
// Following items will be returned as ProtoCapture. (state: file.is_some())
id: ChipId,
pub chip_kind: ChipKind,
pub device_name: String,
pub size: usize,
pub records: i32,
pub seconds: i64,
pub nanos: i32,
pub valid: bool,
}
// Captures contains a recent copy of all chips and their ChipKind, chip_id,
// and owning device name. Information for any recent or ongoing captures is
// also stored in the ProtoCapture.
// facade_key_to_capture allows for fast lookups when handle_request, handle_response
// is invoked from packet_hub.
pub struct Captures {
pub facade_key_to_capture: HashMap<(ChipKind, FacadeId), Arc<Mutex<CaptureInfo>>>,
// BTreeMap is used for chip_id_to_capture, so that the CaptureInfo can always be
// ordered by ChipId. ListCaptureResponse will produce a ordered list of CaptureInfos.
pub chip_id_to_capture: BTreeMap<ChipId, Arc<Mutex<CaptureInfo>>>,
}
impl CaptureInfo {
pub fn new(chip_kind: ChipKind, chip_id: ChipId, device_name: String) -> Self {
CaptureInfo {
facade_id: get_facade_id(chip_id),
id: chip_id,
chip_kind,
device_name,
size: 0,
records: 0,
seconds: 0,
nanos: 0,
valid: true,
file: None,
}
}
// Creates a pcap file with headers and store it under temp directory
// The lifecycle of the file is NOT tied to the lifecycle of the struct
// Format: /tmp/netsim-pcaps/{chip_id}-{device_name}-{chip_kind}.pcap
pub fn start_capture(&mut self) -> Result<()> {
if self.file.is_some() {
return Ok(());
}
let mut filename = std::env::temp_dir();
filename.push("netsim-pcaps");
std::fs::create_dir_all(&filename)?;
filename.push(format!("{:?}-{:}-{:?}.pcap", self.id, self.device_name, self.chip_kind));
let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(filename)?;
let size = write_pcap_header(&mut file)?;
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
self.size = size;
self.records = 0;
self.seconds = timestamp.as_secs() as i64;
self.nanos = timestamp.subsec_nanos() as i32;
self.file = Some(file);
Ok(())
}
// Closes file by removing ownership of self.file
// Capture info will still retain the size and record count
// So it can be downloaded easily when GetCapture is invoked.
pub fn stop_capture(&mut self) {
self.file = None;
}
pub fn new_facade_key(kind: ChipKind, facade_id: FacadeId) -> (ChipKind, FacadeId) {
(kind, facade_id)
}
pub fn get_facade_key(&self) -> (ChipKind, FacadeId) {
CaptureInfo::new_facade_key(self.chip_kind, self.facade_id)
}
pub fn get_capture_proto(&self) -> ProtoCapture {
let timestamp =
Timestamp { seconds: self.seconds, nanos: self.nanos, ..Default::default() };
ProtoCapture {
id: self.id,
chip_kind: self.chip_kind.into(),
device_name: self.device_name.clone(),
state: match self.file.is_some() {
true => State::ON.into(),
false => State::OFF.into(),
},
size: self.size as i32,
records: self.records,
timestamp: Some(timestamp).into(),
valid: self.valid,
..Default::default()
}
}
}
impl Captures {
pub fn new() -> Self {
Captures {
facade_key_to_capture: HashMap::<(ChipKind, FacadeId), Arc<Mutex<CaptureInfo>>>::new(),
chip_id_to_capture: BTreeMap::<ChipId, Arc<Mutex<CaptureInfo>>>::new(),
}
}
pub fn contains(&self, key: ChipId) -> bool {
self.chip_id_to_capture.contains_key(&key)
}
pub fn get(&mut self, key: ChipId) -> Option<&mut Arc<Mutex<CaptureInfo>>> {
self.chip_id_to_capture.get_mut(&key)
}
pub fn insert(&mut self, capture: CaptureInfo) {
let chip_id = capture.id;
let facade_key = capture.get_facade_key();
let arc_capture = Arc::new(Mutex::new(capture));
self.chip_id_to_capture.insert(chip_id, arc_capture.clone());
self.facade_key_to_capture.insert(facade_key, arc_capture);
}
pub fn is_empty(&self) -> bool {
self.chip_id_to_capture.is_empty()
}
pub fn iter(&self) -> Iter<ChipId, Arc<Mutex<CaptureInfo>>> {
self.chip_id_to_capture.iter()
}
// When Capture is removed, remove from each map and also invoke closing of files.
pub fn remove(&mut self, key: &ChipId) {
if let Some(arc_capture) = self.chip_id_to_capture.get(key) {
if let Ok(mut capture) = arc_capture.lock() {
self.facade_key_to_capture.remove(&capture.get_facade_key());
capture.stop_capture();
}
} else {
println!("key does not exist in Captures");
return;
}
self.chip_id_to_capture.remove(key);
}
pub fn values(&self) -> Values<ChipId, Arc<Mutex<CaptureInfo>>> {
self.chip_id_to_capture.values()
}
}