From 6aefa46e8c345f8f907b2196abec60ecf3a3165b Mon Sep 17 00:00:00 2001 From: Felix Hilgers Date: Tue, 26 Nov 2024 02:45:05 +0100 Subject: [PATCH] feat: sendmsg collection and config Signed-off-by: Felix Hilgers Co-authored-by: Benedikt Zinn --- .../ziofa/bl/ConfigurationManager.kt | 4 +- .../configuration/ConfigurationViewModel.kt | 3 +- .../de/amosproj3/ziofa/client/RustClient.kt | 6 +- rust/backend/daemon/src/bin/cli.rs | 4 +- rust/backend/daemon/src/collector.rs | 135 +++++++++++++++--- rust/backend/daemon/src/ebpf_utils.rs | 10 +- rust/backend/daemon/src/features.rs | 111 +++++++++++++- rust/backend/daemon/src/server.rs | 7 +- rust/backend/daemon/tests/base.rs | 3 +- rust/client/src/bin/cli.rs | 13 +- rust/shared/build.rs | 2 + rust/shared/proto/config.proto | 8 +- rust/shared/proto/ziofa.proto | 12 +- 13 files changed, 277 insertions(+), 41 deletions(-) diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/bl/ConfigurationManager.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/bl/ConfigurationManager.kt index 2df535c9..1f89d481 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/bl/ConfigurationManager.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/bl/ConfigurationManager.kt @@ -57,7 +57,9 @@ class ConfigurationManager(val clientFactory: ClientFactory) : client!!.getConfiguration() } catch (e: ClientException) { // TODO this should be handled on the backend - client!!.setConfiguration(Configuration(vfsWrite = null, uprobes = listOf())) + client!!.setConfiguration( + Configuration(vfsWrite = null, sysSendmsg = null, uprobes = listOf()) + ) client!!.getConfiguration() } configuration.update { ConfigurationUpdate.Valid(initializedConfiguration) } diff --git a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationViewModel.kt b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationViewModel.kt index 22a8e998..ed7c2dda 100644 --- a/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationViewModel.kt +++ b/frontend/app/src/main/java/de/amosproj3/ziofa/ui/configuration/ConfigurationViewModel.kt @@ -104,6 +104,7 @@ class ConfigurationViewModel(val configurationAccess: ConfigurationAccess) : Vie VfsWriteConfig(this.vfsWriteOption.pids) } else null - return Configuration(vfsWrite = vfsConfig, uprobes = listOf()) + // TODO: sysSendmsg + return Configuration(vfsWrite = vfsConfig, sysSendmsg = null, uprobes = listOf()) } } diff --git a/frontend/client/src/mock/java/de/amosproj3/ziofa/client/RustClient.kt b/frontend/client/src/mock/java/de/amosproj3/ziofa/client/RustClient.kt index c091d8c9..8eecf5e3 100644 --- a/frontend/client/src/mock/java/de/amosproj3/ziofa/client/RustClient.kt +++ b/frontend/client/src/mock/java/de/amosproj3/ziofa/client/RustClient.kt @@ -22,7 +22,11 @@ const val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" object RustClient : Client { private var configuration: Configuration = - Configuration(vfsWrite = VfsWriteConfig(listOf(1234u, 43124u)), uprobes = listOf()) + Configuration( + vfsWrite = VfsWriteConfig(listOf(1234u, 43124u)), + sysSendmsg = null, + uprobes = listOf(), + ) override suspend fun serverCount(): Flow = flow { var ctr = 0u diff --git a/rust/backend/daemon/src/bin/cli.rs b/rust/backend/daemon/src/bin/cli.rs index 500b0b19..4b760903 100644 --- a/rust/backend/daemon/src/bin/cli.rs +++ b/rust/backend/daemon/src/bin/cli.rs @@ -1,11 +1,12 @@ // SPDX-FileCopyrightText: 2024 Benedikt Zinn +// SPDX-FileCopyrightText: 2024 Felix Hilgers // SPDX-FileCopyrightText: 2024 Robin Seidl // // SPDX-License-Identifier: MIT use clap::Parser; use shared::{ - config::{Configuration, VfsWriteConfig}, + config::{Configuration, SysSendmsgConfig, VfsWriteConfig}, ziofa::ziofa_client::ZiofaClient, }; use tonic::transport::Channel; @@ -48,6 +49,7 @@ async fn test_get_configuration(client: &mut ZiofaClient, verbose: bool Configuration { uprobes: vec![], vfs_write: Some(VfsWriteConfig { pids: vec![] }), + sys_sendmsg: Some(SysSendmsgConfig { pids: vec![] }), } } }; diff --git a/rust/backend/daemon/src/collector.rs b/rust/backend/daemon/src/collector.rs index bdc7431d..f90b238b 100644 --- a/rust/backend/daemon/src/collector.rs +++ b/rust/backend/daemon/src/collector.rs @@ -2,30 +2,134 @@ // // SPDX-License-Identifier: MIT +use std::marker::PhantomData; + use async_broadcast::Sender; +use aya::maps::ring_buf::RingBufItem; use aya::Ebpf; use aya::maps::{MapData, MapError, RingBuf}; use tokio::io::unix::AsyncFd; -use tokio::select; +use tokio::{join, select}; use tonic::Status; use tracing::error; -use backend_common::VfsWriteCall; -use shared::ziofa::{Event, VfsWriteEvent}; +use backend_common::{SysSendmsgCall, VfsWriteCall}; +use shared::ziofa::{Event, SysSendmsgEvent, VfsWriteEvent}; use shared::ziofa::event::{EventData}; -pub struct VfsWriteCollector { - map: AsyncFd> +pub trait CollectFromMap { + const MAP_NAME: &'static str; + + fn convert(item: RingBufItem<'_>) -> Result; +} + +struct VfsWriteCollect; + +impl CollectFromMap for VfsWriteCollect { + const MAP_NAME: &'static str = "VFS_WRITE_MAP"; + + fn convert(item: RingBufItem<'_>) -> Result { + let data = unsafe { &*(item.as_ptr() as *const VfsWriteCall) }; + Ok(Event { + event_data: Some(EventData::VfsWrite(VfsWriteEvent { + pid: data.pid, + tid: data.tid, + begin_time_stamp: data.begin_time_stamp, + fp: data.fp, + bytes_written: data.bytes_written as u64 + })) + }) + } +} + +struct SysSendmsgCollect; + +impl CollectFromMap for SysSendmsgCollect { + const MAP_NAME: &'static str = "SYS_SENDMSG_MAP"; + + fn convert(item: RingBufItem<'_>) -> Result { + let data = unsafe { &*(item.as_ptr() as *const SysSendmsgCall) }; + Ok(Event { + event_data: Some(EventData::SysSendmsg(SysSendmsgEvent { + pid: data.pid, + tid: data.tid, + begin_time_stamp: data.begin_time_stamp, + fd: data.fd, + duration_micro_sec: data.duration_micro_sec + })) + }) + } +} + +pub struct MultiCollector { + vfs_write: Option>, + sys_sendmsg: Option>, +} + +impl MultiCollector { + pub fn from_ebpf(ebpf: &mut Ebpf) -> Result { + let vfs_write = Collector::::from_ebpf(ebpf)?; + let sys_sendmsg = Collector::::from_ebpf(ebpf)?; + Ok(Self { vfs_write: Some(vfs_write), sys_sendmsg: Some(sys_sendmsg) }) + } + + pub async fn collect(&mut self, tx: Sender>, shutdown: tokio::sync::oneshot::Receiver<()>) -> Result<(), std::io::Error> { + + let (vfs_write_shutdown_tx, vfs_write_shutdown_rx) = tokio::sync::oneshot::channel(); + let (sys_sendmsg_shutdown_tx, sys_sendmsg_shutdown_rx) = tokio::sync::oneshot::channel(); + + let cancellation_task = async move { + if shutdown.await.is_err() { + error!("Error while waiting for shutdown signal"); + } + if vfs_write_shutdown_tx.send(()).is_err() { + error!("Error while cancelling vfs_write collector"); + } + if sys_sendmsg_shutdown_tx.send(()).is_err() { + error!("Error while cancelling sys_sendmsg collector"); + } + }; + + let vfs_write_tx = tx.clone(); + let mut vfs_write = self.vfs_write.take().expect("vfs_write should be initialized"); + let vfs_write_task = async { + vfs_write.collect(vfs_write_tx, vfs_write_shutdown_rx).await?; + Ok::<(), std::io::Error>(()) + }; + + let sys_sendmsg_tx = tx; + let mut sys_sendmsg = self.sys_sendmsg.take().expect("sys_sendmsg should be initialized"); + let sys_sendmsg_task = async { + sys_sendmsg.collect(sys_sendmsg_tx, sys_sendmsg_shutdown_rx).await?; + Ok::<(), std::io::Error>(()) + }; + + let (_, vfs_write_result, sys_sendmsg_result) = join!(cancellation_task, vfs_write_task, sys_sendmsg_task); + + self.vfs_write = Some(vfs_write); + self.sys_sendmsg = Some(sys_sendmsg); + + // TODO: multiple errors + vfs_write_result?; + sys_sendmsg_result?; + + Ok(()) + } +} + +pub struct Collector { + map: AsyncFd>, + _collector: PhantomData, } -impl VfsWriteCollector { +impl Collector { pub fn from_ebpf(ebpf: &mut Ebpf) -> Result { - let map: RingBuf<_> = ebpf.take_map("VFS_WRITE_MAP") - .ok_or(MapError::InvalidName { name: "VFS_WRITE_MAP".to_string() })? + let map: RingBuf<_> = ebpf.take_map(T::MAP_NAME) + .ok_or(MapError::InvalidName { name: T::MAP_NAME.to_string() })? .try_into()?; let map = AsyncFd::new(map)?; - Ok(Self { map }) + Ok(Self { map, _collector: PhantomData }) } pub async fn collect(&mut self, tx: Sender>, mut shutdown: tokio::sync::oneshot::Receiver<()>) -> Result<(), std::io::Error> { @@ -36,17 +140,8 @@ impl VfsWriteCollector { let rb = handle.get_inner_mut(); while let Some(item) = rb.next() { - let data = unsafe { &*(item.as_ptr() as *const VfsWriteCall) }; - let event = Event { - event_data: Some(EventData::VfsWrite(VfsWriteEvent { - pid: data.pid, - tid: data.tid, - begin_time_stamp: data.begin_time_stamp, - fp: data.fp, - bytes_written: data.bytes_written as u64 - })) - }; - match tx.broadcast(Ok(event)).await { + let event = T::convert(item); + match tx.broadcast(event).await { Ok(_) => {}, Err(async_broadcast::SendError(event)) => { error!( diff --git a/rust/backend/daemon/src/ebpf_utils.rs b/rust/backend/daemon/src/ebpf_utils.rs index 11a41c00..68b615ae 100644 --- a/rust/backend/daemon/src/ebpf_utils.rs +++ b/rust/backend/daemon/src/ebpf_utils.rs @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: 2024 Felix Hilgers // SPDX-FileCopyrightText: 2024 Franz Schlicht // SPDX-FileCopyrightText: 2024 Robin Seidl // SPDX-FileCopyrightText: 2024 Tom Weisshuhn @@ -5,9 +6,10 @@ // SPDX-License-Identifier: MIT use aya::{Ebpf, EbpfError}; +use shared::config::Configuration; use thiserror::Error; -use crate::features::VfsFeature; +use crate::features::{SysSendmsgFeature, VfsFeature}; #[derive(Debug, Error)] pub enum EbpfErrorWrapper { @@ -23,17 +25,20 @@ impl From for tonic::Status { pub struct State { vfs_write_feature: VfsFeature, + sys_sendmsg_feature: SysSendmsgFeature, } impl State { pub fn new() -> State { State { vfs_write_feature: VfsFeature::new(), + sys_sendmsg_feature: SysSendmsgFeature::new(), } } pub fn init(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { self.vfs_write_feature.create(ebpf)?; + self.sys_sendmsg_feature.create(ebpf)?; Ok(()) } @@ -41,9 +46,10 @@ impl State { pub fn update_from_config( &mut self, ebpf: &mut Ebpf, - _config_path: &str, + config: &Configuration, ) -> Result<(), EbpfError> { self.vfs_write_feature.attach(ebpf)?; + self.sys_sendmsg_feature.apply(ebpf, config.sys_sendmsg.as_ref())?; Ok(()) } diff --git a/rust/backend/daemon/src/features.rs b/rust/backend/daemon/src/features.rs index e76d0b6c..7473da21 100644 --- a/rust/backend/daemon/src/features.rs +++ b/rust/backend/daemon/src/features.rs @@ -1,11 +1,112 @@ +// SPDX-FileCopyrightText: 2024 Felix Hilgers // SPDX-FileCopyrightText: 2024 Robin Seidl // // SPDX-License-Identifier: MIT +use std::collections::BTreeSet; + use aya::{ - programs::{kprobe::KProbeLinkId, KProbe}, - Ebpf, EbpfError, + maps::HashMap, programs::{kprobe::KProbeLinkId, trace_point::TracePointLink, KProbe, TracePoint}, Ebpf, EbpfError }; +use shared::config::SysSendmsgConfig; + +pub struct SysSendmsgFeature { + sys_enter_sendmsg_id: Option, + sys_exit_sendmsg_id: Option, +} + +impl SysSendmsgFeature { + pub fn new() -> SysSendmsgFeature { + SysSendmsgFeature { + sys_enter_sendmsg_id: None, + sys_exit_sendmsg_id: None, + } + } + + fn get_tracepoint_by_name<'a>(ebpf: &'a mut Ebpf, name: &str) -> Result<&'a mut TracePoint, EbpfError> { + Ok(ebpf + .program_mut(name) + .ok_or(EbpfError::ProgramError( + aya::programs::ProgramError::InvalidName { + name: name.to_string(), + }, + ))? + .try_into()?) + } + + fn get_sys_enter_sendmsg(ebpf: &mut Ebpf) -> Result<&mut TracePoint, EbpfError> { + Self::get_tracepoint_by_name(ebpf, "sys_enter_sendmsg") + } + + fn get_sys_exit_sendmsg(ebpf: &mut Ebpf) -> Result<&mut TracePoint, EbpfError> { + Self::get_tracepoint_by_name(ebpf, "sys_exit_sendmsg") + } + + fn detach(&mut self) -> Result<(), EbpfError> { + let _ = self.sys_enter_sendmsg_id.take(); + let _ = self.sys_exit_sendmsg_id.take(); + Ok(()) + } + + fn attach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + if self.sys_enter_sendmsg_id.is_none() { + let p = SysSendmsgFeature::get_sys_enter_sendmsg(ebpf)?; + let link_id = p.attach("syscalls","sys_enter_sendmsg")?; + self.sys_enter_sendmsg_id = Some(p.take_link(link_id)?); + } + + if self.sys_exit_sendmsg_id.is_none() { + let p = SysSendmsgFeature::get_sys_exit_sendmsg(ebpf)?; + let link_id = p.attach("syscalls","sys_exit_sendmsg")?; + self.sys_exit_sendmsg_id = Some(p.take_link(link_id)?); + } + + Ok(()) + } + + fn update_pids(&mut self, ebpf: &mut Ebpf, pids: &[u32]) -> Result<(), EbpfError> { + let mut pids_to_track: HashMap<_, u32, u32> = ebpf.map_mut("PIDS_TO_TRACK") + .ok_or(EbpfError::MapError( + aya::maps::MapError::InvalidName { + name: "PIDS_TO_TRACK".to_string(), + }, + ))?.try_into()?; + + let new_keys = pids.iter().copied().collect::>(); + let existing_keys = pids_to_track.keys().collect::, _>>()?; + + for key_to_remove in existing_keys.difference(&new_keys) { + pids_to_track.remove(key_to_remove)?; + } + + for key_to_add in new_keys.difference(&existing_keys) { + pids_to_track.insert(key_to_add, 0, 0)?; + } + + Ok(()) + } + + pub fn create(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + SysSendmsgFeature::get_sys_exit_sendmsg(ebpf)?.load()?; + SysSendmsgFeature::get_sys_enter_sendmsg(ebpf)?.load()?; + Ok(()) + } + + + + pub fn apply(&mut self, ebpf: &mut Ebpf, config: Option<&SysSendmsgConfig>) -> Result<(), EbpfError> { + match config { + Some(config) => { + self.attach(ebpf)?; + self.update_pids(ebpf, &config.pids)?; + } + None => { + self.detach()?; + } + } + Ok(()) + } +} pub struct VfsFeature { vfs_write_id: Option, @@ -67,7 +168,8 @@ impl VfsFeature { Ok(()) } - pub fn detach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + // TODO: just renamed to make clippy happy + pub fn _detach(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { let vfs_write: &mut KProbe = ebpf .program_mut("vfs_write") .ok_or(EbpfError::ProgramError( @@ -115,7 +217,8 @@ impl VfsFeature { // // will be discussed by Felix and Beni // } - pub fn destroy(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { + // TODO: just renamed to make clippy happy + pub fn _destroy(&mut self, ebpf: &mut Ebpf) -> Result<(), EbpfError> { // TODO Error handling let vfs_write: &mut KProbe = ebpf .program_mut("vfs_write") diff --git a/rust/backend/daemon/src/server.rs b/rust/backend/daemon/src/server.rs index 0b1a0f5d..381b02e3 100644 --- a/rust/backend/daemon/src/server.rs +++ b/rust/backend/daemon/src/server.rs @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2024 Benedikt Zinn +// SPDX-FileCopyrightText: 2024 Felix Hilgers // SPDX-FileCopyrightText: 2024 Franz Schlicht // SPDX-FileCopyrightText: 2024 Robin Seidl // @@ -21,13 +22,13 @@ use shared::{ use tokio::sync::Mutex; use tonic::{transport::Server, Request, Response, Status}; use shared::ziofa::Event; +use crate::collector::MultiCollector; use crate::{ configuration, constants, counter::Counter, ebpf_utils::{EbpfErrorWrapper, State}, procfs_utils::{list_processes, ProcErrorWrapper}, }; -use crate::collector::VfsWriteCollector; pub struct ZiofaImpl { // tx: Option>>, @@ -89,7 +90,7 @@ impl Ziofa for ZiofaImpl { // TODO: set config path state_guard - .update_from_config(ebpf_guard.deref_mut(), "ziofa.json") + .update_from_config(ebpf_guard.deref_mut(), &config) .map_err(EbpfErrorWrapper::from)?; Ok(Response::new(SetConfigurationResponse { response_type: 0 })) @@ -114,7 +115,7 @@ pub async fn serve_forever() { EbpfLogger::init(&mut ebpf).unwrap(); - let mut collector = VfsWriteCollector::from_ebpf(&mut ebpf).unwrap(); + let mut collector = MultiCollector::from_ebpf(&mut ebpf).unwrap(); let channel = Arc::new(Channel::new()); let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel(); diff --git a/rust/backend/daemon/tests/base.rs b/rust/backend/daemon/tests/base.rs index 10b63e26..f961692d 100644 --- a/rust/backend/daemon/tests/base.rs +++ b/rust/backend/daemon/tests/base.rs @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT -use shared::config::{Configuration, VfsWriteConfig}; +use shared::config::{Configuration, SysSendmsgConfig, VfsWriteConfig}; use shared::ziofa::{process::Cmd, ziofa_client::ZiofaClient}; use tonic::transport::Channel; @@ -64,6 +64,7 @@ async fn set_get_configuration() { let default_config = Configuration { uprobes: vec![], vfs_write: Some(VfsWriteConfig { pids: vec![] }), + sys_sendmsg: Some(SysSendmsgConfig { pids: vec![] }), }; assert_eq!( client diff --git a/rust/client/src/bin/cli.rs b/rust/client/src/bin/cli.rs index 5d77d088..4318fdbc 100644 --- a/rust/client/src/bin/cli.rs +++ b/rust/client/src/bin/cli.rs @@ -4,14 +4,16 @@ use clap::Parser; use client::{Client, ClientError}; -use shared::config::{Configuration, VfsWriteConfig}; +use shared::config::{Configuration, SysSendmsgConfig}; use tokio::{join, select, signal::ctrl_c, sync::oneshot}; use tokio_stream::StreamExt; #[derive(Debug, Clone, Parser)] struct Cli { - #[clap(short, long, help = "interface where packets should be counted")] - iface: String, + //#[clap(short, long, help = "interface where packets should be counted")] + //iface: String, + #[clap(short, long, help = "pid for which to track sendmsg calls")] + pid: u32 } pub async fn counter_cli(mut client: Client, iface: String) -> anyhow::Result<()> { @@ -61,14 +63,15 @@ pub async fn counter_cli(mut client: Client, iface: String) -> anyhow::Result<() #[tokio::main] pub async fn main() -> anyhow::Result<()> { - let Cli { .. } = Cli::parse(); + let Cli { pid } = Cli::parse(); let mut client = Client::connect("http://[::1]:50051".to_owned()).await?; client .set_configuration(Configuration { uprobes: vec![], - vfs_write: Some(VfsWriteConfig { pids: vec![] }), + vfs_write: None, + sys_sendmsg: Some(SysSendmsgConfig { pids: vec![pid] }) }) .await?; diff --git a/rust/shared/build.rs b/rust/shared/build.rs index f279720c..5348e90a 100644 --- a/rust/shared/build.rs +++ b/rust/shared/build.rs @@ -19,7 +19,9 @@ static UNIFFI_RECORDS: LazyLock> = LazyLock::new(|| { "UprobeConfig", "Event", "VfsWriteEvent", + "SysSendmsgEvent", "VfsWriteConfig", + "SysSendmsgConfig", ] } else { vec![] diff --git a/rust/shared/proto/config.proto b/rust/shared/proto/config.proto index c923b0ea..dc96b5fe 100644 --- a/rust/shared/proto/config.proto +++ b/rust/shared/proto/config.proto @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2024 Benedikt Zinn +// SPDX-FileCopyrightText: 2024 Felix Hilgers // SPDX-FileCopyrightText: 2024 Franz Schlicht // SPDX-FileCopyrightText: 2024 Robin Seidl // @@ -17,9 +18,14 @@ message UprobeConfig { message Configuration { VfsWriteConfig vfs_write = 1; - repeated UprobeConfig uprobes = 2; + SysSendmsgConfig sys_sendmsg = 2; + repeated UprobeConfig uprobes = 3; } message VfsWriteConfig { repeated uint32 pids = 1; } + +message SysSendmsgConfig { + repeated uint32 pids = 1; +} \ No newline at end of file diff --git a/rust/shared/proto/ziofa.proto b/rust/shared/proto/ziofa.proto index 15278d40..e24760b5 100644 --- a/rust/shared/proto/ziofa.proto +++ b/rust/shared/proto/ziofa.proto @@ -1,3 +1,4 @@ +// SPDX-FileCopyrightText: 2024 Felix Hilgers // SPDX-FileCopyrightText: 2024 Franz Schlicht // SPDX-FileCopyrightText: 2024 Robin Seidl <68117355+Mr-Kanister@users.noreply.github.com> // @@ -50,6 +51,7 @@ message SetConfigurationResponse{ message Event { oneof event_data { VfsWriteEvent vfs_write = 1; + SysSendmsgEvent sys_sendmsg = 2; } } @@ -59,4 +61,12 @@ message VfsWriteEvent { uint64 begin_time_stamp = 3; uint64 fp = 4; uint64 bytes_written = 5; -} \ No newline at end of file +} + +message SysSendmsgEvent { + uint32 pid = 1; + uint32 tid = 2; + uint64 begin_time_stamp = 3; + int32 fd = 4; + uint64 duration_micro_sec = 5; +}