Skip to content

Commit e91210e

Browse files
authored
refactor(sidecar)!: Avoid a dedicated socket for crashtracker (#2179)
This allows us doing an unified authentication of the socket on the sidecar side, before handing off to crashtracker. Usually the parent process would be the one connecting to the crashtracker, here it is not. This will allow custom handling, and one less endpoint to secure. The strategy chosen is upgrading to the crashtracker once an IPC socket is received. Currently we recreate a new socket, but we might also decide to reuse the existing IPC connection in future. The changes to the crashtracker crate itself are kept minimal, just adding a new config for a custom socket connector, and using the existing one as default. There's also a bit of refactoring on the sidecar side, to make it easier to take ownership of the asyncfd. Co-authored-by: bob.weinand <bob.weinand@datadoghq.com>
1 parent 20cc8c0 commit e91210e

23 files changed

Lines changed: 637 additions & 193 deletions

File tree

datadog-ipc-macros/src/lib.rs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ fn gen_handler_trait(
210210
quote! {
211211
fn #name(
212212
&self,
213-
peer: datadog_ipc::PeerCredentials,
214213
#(#params),*
215214
) -> impl ::std::future::Future<Output = #ret> + Send + '_;
216215
}
@@ -223,6 +222,9 @@ fn gen_handler_trait(
223222
/// The serve loop uses this to track received payloads.
224223
fn recv_counter(&self) -> &::std::sync::atomic::AtomicU64;
225224

225+
/// Storage for the connection to read from.
226+
fn connection(&self) -> &datadog_ipc::ipc_server::OwnedServerConn;
227+
226228
#(#handler_methods)*
227229
}
228230
}
@@ -260,29 +262,29 @@ fn gen_serve_fn(
260262
quote! {
261263
#[cfg(target_os = "linux")]
262264
if __pending_acks > 0 {
263-
datadog_ipc::send_acks_async(&async_fd, __pending_acks).await;
265+
datadog_ipc::send_acks_async(handler.connection().async_conn(), __pending_acks).await;
264266
__pending_acks = 0;
265267
}
266-
let result = handler.#name(peer, #(#field_names),*).await;
268+
let result = handler.#name(#(#field_names),*).await;
267269
let __resp_data = datadog_ipc::codec::encode(&result);
268-
datadog_ipc::send_raw_async(&async_fd, &__resp_data).await.ok();
270+
datadog_ipc::send_raw_async(handler.connection().async_conn(), &__resp_data).await.ok();
269271
}
270272
} else {
271273
// On Linux, buffer up to 20 acks and flush in a single
272274
// sendmmsg(2) syscall; on other platforms send each ack immediately.
273275
quote! {
274-
handler.#name(peer, #(#field_names),*).await;
276+
handler.#name(#(#field_names),*).await;
275277
#[cfg(target_os = "linux")]
276278
{
277279
__pending_acks += 1;
278280
if #force_flush || __pending_acks >= datadog_ipc::ACK_BUFFER_SIZE {
279-
datadog_ipc::send_acks_async(&async_fd, __pending_acks).await;
281+
datadog_ipc::send_acks_async(handler.connection().async_conn(), __pending_acks).await;
280282
__pending_acks = 0;
281283
}
282284
}
283285
#[cfg(not(target_os = "linux"))]
284286
// 1-byte ack: distinguishable from EOF (0 bytes from recvmsg on closed socket).
285-
datadog_ipc::send_raw_async(&async_fd, &[0u8]).await.ok();
287+
datadog_ipc::send_raw_async(handler.connection().async_conn(), &[0u8]).await.ok();
286288
}
287289
};
288290

@@ -296,23 +298,14 @@ fn gen_serve_fn(
296298

297299
quote! {
298300
pub async fn #serve_fn<H: #trait_name>(
299-
conn: datadog_ipc::SeqpacketConn,
300301
handler: ::std::sync::Arc<H>,
301302
) {
302-
let peer = conn.peer_credentials().unwrap_or_default();
303-
let async_fd = match conn.into_async_conn() {
304-
Ok(fd) => fd,
305-
Err(e) => {
306-
::tracing::error!("IPC serve: into_async_conn failed: {e}");
307-
return;
308-
}
309-
};
310303
// Pending 1-byte acks for fire-and-forget methods, flushed via sendmmsg(2) on Linux.
311304
#[cfg(target_os = "linux")]
312305
let mut __pending_acks: u32 = 0;
313306
loop {
314307
let (mut req, fds) = match datadog_ipc::recv_raw_async(
315-
&async_fd,
308+
&handler.connection().async_conn(),
316309
|buf| datadog_ipc::codec::decode::<#enum_name>(buf),
317310
).await {
318311
Ok((Ok(req), fds)) => (req, fds),
@@ -334,7 +327,7 @@ fn gen_serve_fn(
334327
break;
335328
}
336329
let recv_counter = handler.recv_counter().load(::std::sync::atomic::Ordering::Relaxed) + 1;
337-
::tracing::trace!(recv_counter, ?req, pid = peer.pid, "IPC recv");
330+
::tracing::trace!(recv_counter, ?req, pid = handler.connection().peer().pid, "IPC recv");
338331

339332
match req {
340333
#(#match_arms)*

datadog-ipc/src/example_interface.rs

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::{
1010
};
1111

1212
use super::platform::{FileBackedHandle, PlatformHandle, ShmHandle};
13+
use crate::ipc_server::OwnedServerConn;
1314

1415
extern crate self as datadog_ipc;
1516

@@ -30,6 +31,7 @@ pub trait ExampleInterface {
3031
async fn echo_len(payload: Vec<u8>) -> u32;
3132
}
3233

34+
/// Shared server state. Cloned into a per-connection [`ExampleConnectionHandler`] on accept.
3335
#[derive(Default, Clone)]
3436
pub struct ExampleServer {
3537
req_cnt: Arc<AtomicU64>,
@@ -38,70 +40,69 @@ pub struct ExampleServer {
3840

3941
impl ExampleServer {
4042
pub async fn accept_connection(self, conn: crate::SeqpacketConn) {
41-
serve_example_interface_connection(conn, Arc::new(self)).await
43+
let connection = match OwnedServerConn::new(conn) {
44+
Ok(c) => c,
45+
Err(e) => {
46+
::tracing::error!("ExampleServer: failed to set up connection: {e}");
47+
return;
48+
}
49+
};
50+
serve_example_interface_connection(Arc::new(ExampleConnectionHandler {
51+
server: self,
52+
connection,
53+
}))
54+
.await
4255
}
4356
}
4457

45-
impl ExampleInterface for ExampleServer {
58+
/// Per-connection handler: owns the connection and serves requests received on it.
59+
struct ExampleConnectionHandler {
60+
server: ExampleServer,
61+
connection: OwnedServerConn,
62+
}
63+
64+
impl ExampleInterface for ExampleConnectionHandler {
4665
fn recv_counter(&self) -> &AtomicU64 {
47-
&self.req_cnt
66+
&self.server.req_cnt
4867
}
4968

50-
fn notify(
51-
&self,
52-
_peer: datadog_ipc::PeerCredentials,
53-
) -> impl std::future::Future<Output = ()> + Send + '_ {
69+
fn connection(&self) -> &OwnedServerConn {
70+
&self.connection
71+
}
72+
73+
fn notify(&self) -> impl std::future::Future<Output = ()> + Send + '_ {
5474
std::future::ready(())
5575
}
5676

57-
fn ping(
58-
&self,
59-
_peer: datadog_ipc::PeerCredentials,
60-
) -> impl std::future::Future<Output = ()> + Send + '_ {
77+
fn ping(&self) -> impl std::future::Future<Output = ()> + Send + '_ {
6178
std::future::ready(())
6279
}
6380

64-
fn time_now(
65-
&self,
66-
_peer: datadog_ipc::PeerCredentials,
67-
) -> impl std::future::Future<Output = Duration> + Send + '_ {
81+
fn time_now(&self) -> impl std::future::Future<Output = Duration> + Send + '_ {
6882
std::future::ready(Instant::now().elapsed())
6983
}
7084

71-
fn req_cnt(
72-
&self,
73-
_peer: datadog_ipc::PeerCredentials,
74-
) -> impl std::future::Future<Output = u32> + Send + '_ {
75-
std::future::ready(self.req_cnt.load(Ordering::Relaxed) as u32)
85+
fn req_cnt(&self) -> impl std::future::Future<Output = u32> + Send + '_ {
86+
std::future::ready(self.server.req_cnt.load(Ordering::Relaxed) as u32)
7687
}
7788

7889
fn store_file(
7990
&self,
80-
_peer: datadog_ipc::PeerCredentials,
8191
file: PlatformHandle<File>,
8292
) -> impl std::future::Future<Output = ()> + Send + '_ {
8393
#[allow(clippy::unwrap_used)]
84-
self.stored_files.lock().unwrap().push(file);
94+
self.server.stored_files.lock().unwrap().push(file);
8595
std::future::ready(())
8696
}
8797

88-
async fn shm_sum(
89-
&self,
90-
_peer: datadog_ipc::PeerCredentials,
91-
handle: ShmHandle,
92-
len: usize,
93-
) -> u64 {
98+
async fn shm_sum(&self, handle: ShmHandle, len: usize) -> u64 {
9499
match handle.map() {
95100
Ok(mapped) => mapped.as_slice()[..len].iter().map(|&b| b as u64).sum(),
96101
Err(_) => u64::MAX,
97102
}
98103
}
99104

100-
fn echo_len(
101-
&self,
102-
_peer: datadog_ipc::PeerCredentials,
103-
payload: Vec<u8>,
104-
) -> impl std::future::Future<Output = u32> + Send + '_ {
105+
fn echo_len(&self, payload: Vec<u8>) -> impl std::future::Future<Output = u32> + Send + '_ {
105106
std::future::ready(payload.len() as u32)
106107
}
107108
}

datadog-ipc/src/ipc_server.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::{PeerCredentials, SeqpacketConn};
5+
use std::io;
6+
7+
pub struct OwnedServerConn {
8+
connection: crate::AsyncConn,
9+
peer: PeerCredentials,
10+
}
11+
12+
impl OwnedServerConn {
13+
pub fn new(conn: SeqpacketConn) -> io::Result<Self> {
14+
let peer = conn.peer_credentials().unwrap_or_default();
15+
let connection = conn.into_async_conn()?;
16+
Ok(Self { connection, peer })
17+
}
18+
19+
/// Construct from an already-async connection and a known peer. Useful for callers that have
20+
/// already wrapped the fd (and for tests).
21+
pub fn from_async(connection: crate::AsyncConn, peer: PeerCredentials) -> Self {
22+
Self { connection, peer }
23+
}
24+
25+
pub fn async_conn(&self) -> &crate::AsyncConn {
26+
&self.connection
27+
}
28+
29+
pub fn peer(&self) -> &PeerCredentials {
30+
&self.peer
31+
}
32+
}

datadog-ipc/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub mod shm_stats;
1818
mod atomic_option;
1919
pub mod client;
2020
pub mod codec;
21+
pub mod ipc_server;
22+
2123
pub use atomic_option::AtomicOption;
2224

2325
pub use client::IpcClientConn;

datadog-sidecar-ffi/src/lib.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use datadog_live_debugger::debugger_defs::DebuggerPayload;
1717
use datadog_sidecar::agent_remote_config::{new_reader, reader_from_shm, AgentRemoteConfigWriter};
1818
use datadog_sidecar::config;
1919
use datadog_sidecar::config::LogMethod;
20-
use datadog_sidecar::crashtracker::crashtracker_unix_socket_path;
2120
use datadog_sidecar::service::agent_info::AgentInfoReader;
2221
use datadog_sidecar::service::telemetry::InternalTelemetryAction;
2322
use datadog_sidecar::service::{
@@ -1614,21 +1613,6 @@ pub extern "C" fn ddog_sidecar_reconnect(
16141613
transport.reconnect(|| unsafe { factory() });
16151614
}
16161615

1617-
/// Return the path of the crashtracker unix domain socket.
1618-
#[no_mangle]
1619-
#[allow(clippy::missing_safety_doc)]
1620-
pub unsafe extern "C" fn ddog_sidecar_get_crashtracker_unix_socket_path() -> ffi::CharSlice<'static>
1621-
{
1622-
let socket_path = crashtracker_unix_socket_path();
1623-
let str = socket_path.to_str().unwrap_or_default();
1624-
1625-
let size = str.len();
1626-
let malloced = libc::malloc(size) as *mut u8;
1627-
let buf = slice::from_raw_parts_mut(malloced, size);
1628-
buf.copy_from_slice(str.as_bytes());
1629-
ffi::CharSlice::from_raw_parts(malloced as *mut c_char, size)
1630-
}
1631-
16321616
/// Gets an agent info reader.
16331617
#[no_mangle]
16341618
#[allow(clippy::missing_safety_doc)]

datadog-sidecar/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl AppSecConfig {
174174
pub struct FromEnv {}
175175

176176
impl FromEnv {
177-
fn ipc_mode() -> IpcMode {
177+
pub fn ipc_mode() -> IpcMode {
178178
let mode = std::env::var(ENV_SIDECAR_IPC_MODE).unwrap_or_default();
179179

180180
match mode.as_str() {

0 commit comments

Comments
 (0)