use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::time::{Duration, Instant};
use rustc_hash::FxHashMap;
use ed25519_dalek::{Verifier, VerifyingKey};
use melin_app::Application;
use melin_app::auth::AuthorizedKeys;
use melin_app::auth::Permission;
use melin_app::decoder::RequestDecoder;
use melin_app::unix_epoch_nanos;
use melin_dpdk::transport::DpdkTransport;
use melin_pipeline::ring;
use melin_transport_core::pipeline::InputSlot;
#[cfg(feature = "latency-trace")]
use melin_transport_core::trace::mono_trace_ns;
use melin_wire_protocol::control::ConnectionId;
use melin_wire_protocol::control::TransportResponse;
use melin_wire_protocol::control_codec;
use rand::RngExt;
use melin_dpdk::SocketHandle;
use tracing::{debug, warn};
use crate::dpdk_response::{ControlEvent, TxFrame};
use crate::client_frames::MAX_FRAME_SIZE;
const AUTH_TIMEOUT: Duration = Duration::from_secs(5);
enum AuthState {
WaitingForResponse {
nonce: [u8; 32],
accepted_at: Instant,
},
Authenticated { permission: Permission },
}
struct ConnectionState {
connection_id: ConnectionId,
addr: SocketAddr,
handle: SocketHandle,
auth: AuthState,
key_hash: u64,
parse_buf: Vec<u8>,
last_activity: Instant,
}
#[allow(clippy::too_many_arguments)]
pub fn run_dpdk_poll<A: Application>(
mut transport: DpdkTransport,
mut producer: ring::Producer<InputSlot<A::Event>>,
decoder: Arc<dyn RequestDecoder<Event = A::Event>>,
control_tx: mpsc::Sender<ControlEvent>,
mut tx_rx: melin_pipeline::spsc::Consumer<TxFrame>,
shutdown: &AtomicBool,
authorized_keys: Arc<AuthorizedKeys>,
connection_timeout: Option<Duration>,
tick_cadence: Option<Duration>,
max_connections: u64,
active_connections: Arc<std::sync::atomic::AtomicU64>,
thread_id: u8,
repl_driver: Option<crate::replication::DpdkReplicationDriver<A>>,
repl_listen_port: u16,
) {
let mut repl_driver = repl_driver;
let mut connections: Vec<Option<ConnectionState>> =
(0..melin_dpdk::MAX_CONNECTIONS).map(|_| None).collect();
let mut id_to_handle: FxHashMap<u64, SocketHandle> =
FxHashMap::with_capacity_and_hasher(256, Default::default());
let mut next_connection_id: u64 = 1;
let mut connection_count: usize = 0;
let mut conn_range_end: usize = 0;
#[cfg(feature = "latency-trace")]
let mut publish_rec =
melin_transport_core::trace::register_stage("dpdk: publish (decode → disruptor publish)");
#[cfg(feature = "tick-to-trade")]
let mut ingest_rec =
melin_transport_core::trace::register_stage("reader: ingest (recv_ts → publish complete)");
let mut parse_buf_pool: Vec<Vec<u8>> = (0..256)
.map(|_| Vec::with_capacity(MAX_FRAME_SIZE + 4))
.collect();
let mut rng = rand::rng();
let tick_enabled = tick_cadence.is_some();
let cadence = tick_cadence.unwrap_or(Duration::ZERO);
let mut next_tick_deadline = Instant::now() + cadence;
let mut last_tick_ns: u64 = 0;
let mut tick_check_counter: u32 = 0;
const TICK_CHECK_INTERVAL: u32 = 4096;
let mut slow_check_counter: u32 = 0;
const SLOW_CHECK_INTERVAL: u32 = 1024 * 1024;
if tick_enabled {
tracing::info!(
cadence_ms = cadence.as_millis() as u64,
thread_id,
"tick generator integrated into DPDK poll thread"
);
}
#[cfg(feature = "latency-trace")]
let mut poll_iter_rec = melin_transport_core::trace::register_stage(
"dpdk poll: outer iteration (work-iterations only)",
);
#[cfg(feature = "latency-trace")]
let mut poll_iter_start = mono_trace_ns();
loop {
if shutdown.load(Ordering::Relaxed) {
break;
}
#[cfg(feature = "latency-trace")]
let mut work_done_this_iter = false;
if tick_enabled {
tick_check_counter = tick_check_counter.wrapping_add(1);
if tick_check_counter >= TICK_CHECK_INTERVAL {
tick_check_counter = 0;
let now = Instant::now();
if now >= next_tick_deadline {
let raw_now_ns = unix_epoch_nanos();
let now_ns =
melin_transport_core::tick::clamp_monotonic(raw_now_ns, last_tick_ns);
last_tick_ns = now_ns;
melin_transport_core::tick::publish_tick(&mut producer, now_ns);
let elapsed = Instant::now().saturating_duration_since(next_tick_deadline);
next_tick_deadline = if elapsed > cadence {
Instant::now() + cadence
} else {
next_tick_deadline + cadence
};
}
}
}
macro_rules! drain_tx {
() => {
while let Some((_seq, frame)) = tx_rx.try_consume() {
if let Some(&handle) = id_to_handle.get(&frame.connection_id)
&& !transport.queue_send(handle, frame.as_bytes())
{
debug!(
connection_id = frame.connection_id,
"DPDK: TX queue overflow, dropping connection"
);
transport.close(handle);
let _ = control_tx.send(ControlEvent::Disconnected {
connection_id: frame.connection_id,
});
id_to_handle.remove(&frame.connection_id);
if let Some(mut removed) = connections[handle.index()].take() {
removed.parse_buf.clear();
parse_buf_pool.push(removed.parse_buf);
connection_count -= 1;
}
}
}
};
}
drain_tx!();
transport.poll();
for accepted in transport.take_accepted() {
if let Some(ref mut driver) = repl_driver
&& accepted.listen_port == repl_listen_port
{
driver.accept_connection(accepted.peer, accepted.handle, &mut transport);
continue;
}
if max_connections > 0 && connection_count as u64 >= max_connections {
warn!(
peer = %accepted.peer,
"DPDK: connection rejected: max_connections reached"
);
transport.close(accepted.handle);
continue;
}
let conn_id = ConnectionId((thread_id as u64) << 56 | next_connection_id);
next_connection_id += 1;
debug!(
connection_id = conn_id.0,
peer = %accepted.peer,
"DPDK: new connection, starting auth"
);
let nonce: [u8; 32] = rng.random();
let mut challenge_buf = [0u8; 128];
let written = control_codec::encode_transport_response(
&TransportResponse::Challenge { nonce },
&mut challenge_buf,
)
.expect("challenge encodes");
transport.queue_send(accepted.handle, &challenge_buf[..written]);
let accepted_idx = accepted.handle.index();
if accepted_idx + 1 > conn_range_end {
conn_range_end = accepted_idx + 1;
}
connections[accepted_idx] = Some(ConnectionState {
connection_id: conn_id,
addr: accepted.peer,
handle: accepted.handle,
auth: AuthState::WaitingForResponse {
nonce,
accepted_at: Instant::now(),
},
key_hash: 0,
parse_buf: parse_buf_pool
.pop()
.unwrap_or_else(|| Vec::with_capacity(MAX_FRAME_SIZE + 4)),
last_activity: Instant::now(),
});
connection_count += 1;
}
const POLL_EVERY_N_CONNS: usize = 4;
let mut batch_wall_ns: Option<u64> = None;
slow_check_counter = slow_check_counter.wrapping_add(1);
let do_slow_checks = slow_check_counter.is_multiple_of(SLOW_CHECK_INTERVAL);
let mut active_idx: usize = 0;
#[allow(clippy::needless_range_loop)]
for idx in 0..conn_range_end {
if connections[idx].is_none() {
continue;
}
if active_idx > 0 && active_idx.is_multiple_of(POLL_EVERY_N_CONNS) {
drain_tx!();
transport.poll();
if let Some(ref mut driver) = repl_driver {
driver.tick(&mut transport, shutdown);
}
if connections[idx].is_none() {
continue;
}
}
active_idx += 1;
let conn = match connections[idx].as_mut() {
Some(c) => c,
None => continue,
};
let conn_handle = conn.handle;
if do_slow_checks
&& let AuthState::WaitingForResponse { accepted_at, .. } = &conn.auth
&& accepted_at.elapsed() > AUTH_TIMEOUT
{
debug!(
connection_id = conn.connection_id.0,
addr = %conn.addr,
"DPDK: auth timeout, dropping connection"
);
transport.close(conn.handle);
if let Some(mut removed) = connections[idx].take() {
removed.parse_buf.clear();
parse_buf_pool.push(removed.parse_buf);
connection_count -= 1;
}
continue;
}
const MAX_PARSE_BUF: usize = 65536;
if conn.parse_buf.len() >= MAX_PARSE_BUF {
debug!(
connection_id = conn.connection_id.0,
buf_len = conn.parse_buf.len(),
"parse buffer exceeded limit, dropping connection"
);
transport.close(conn.handle);
let _ = control_tx.send(ControlEvent::Disconnected {
connection_id: conn.connection_id.0,
});
id_to_handle.remove(&conn.connection_id.0);
if let Some(mut removed) = connections[idx].take() {
removed.parse_buf.clear();
parse_buf_pool.push(removed.parse_buf);
connection_count -= 1;
}
continue;
}
let n = transport.recv_into_vec(conn.handle, &mut conn.parse_buf);
if n > 0 {
conn.last_activity = Instant::now();
#[cfg(feature = "latency-trace")]
{
work_done_this_iter = true;
}
}
if n == 0 {
if !transport.is_active(conn.handle) {
debug!(
connection_id = conn.connection_id.0,
addr = %conn.addr,
"DPDK: connection closed"
);
if matches!(conn.auth, AuthState::Authenticated { .. }) {
let _ = control_tx.send(ControlEvent::Disconnected {
connection_id: conn.connection_id.0,
});
}
transport.close(conn.handle);
id_to_handle.remove(&conn.connection_id.0);
if let Some(mut removed) = connections[idx].take() {
removed.parse_buf.clear();
parse_buf_pool.push(removed.parse_buf);
connection_count -= 1;
}
}
else if do_slow_checks
&& let Some(timeout) = connection_timeout
&& matches!(conn.auth, AuthState::Authenticated { .. })
&& conn.last_activity.elapsed() > timeout
{
debug!(
connection_id = conn.connection_id.0,
addr = %conn.addr,
"DPDK: idle timeout, dropping connection"
);
transport.close(conn.handle);
let _ = control_tx.send(ControlEvent::Disconnected {
connection_id: conn.connection_id.0,
});
id_to_handle.remove(&conn.connection_id.0);
if let Some(mut removed) = connections[idx].take() {
removed.parse_buf.clear();
parse_buf_pool.push(removed.parse_buf);
connection_count -= 1;
}
}
continue;
}
let was_waiting = matches!(conn.auth, AuthState::WaitingForResponse { .. });
match &conn.auth {
AuthState::WaitingForResponse { .. } => {
process_auth_frame(
conn,
&mut transport,
&authorized_keys,
&control_tx,
&mut id_to_handle,
conn_handle,
);
}
AuthState::Authenticated { permission } => {
use crate::client_frames::{FrameAction, process_client_frames};
let permission = *permission;
let action = process_client_frames(
&mut conn.parse_buf,
conn.connection_id.0,
conn.key_hash,
permission,
&mut producer,
&*decoder,
*batch_wall_ns.get_or_insert_with(unix_epoch_nanos),
#[cfg(feature = "latency-trace")]
&mut publish_rec,
#[cfg(feature = "tick-to-trade")]
&mut ingest_rec,
);
if matches!(action, FrameAction::Disconnect | FrameAction::PipelineFull) {
if matches!(action, FrameAction::PipelineFull) {
debug!(
connection_id = conn.connection_id.0,
"DPDK: pipeline full, dropping connection"
);
}
transport.close(conn.handle);
let _ = control_tx.send(ControlEvent::Disconnected {
connection_id: conn.connection_id.0,
});
id_to_handle.remove(&conn.connection_id.0);
if let Some(mut removed) = connections[idx].take() {
removed.parse_buf.clear();
parse_buf_pool.push(removed.parse_buf);
connection_count -= 1;
}
continue;
}
}
}
if was_waiting && matches!(conn.auth, AuthState::Authenticated { .. }) {
active_connections.fetch_add(1, Ordering::Relaxed);
}
}
drain_tx!();
transport.poll();
if let Some(ref mut driver) = repl_driver {
driver.tick(&mut transport, shutdown);
}
#[cfg(feature = "latency-trace")]
{
let now = mono_trace_ns();
if work_done_this_iter && !shutdown.load(Ordering::Relaxed) {
poll_iter_rec.record_elapsed(poll_iter_start, now);
}
poll_iter_start = now;
}
}
}
fn process_auth_frame(
conn: &mut ConnectionState,
transport: &mut DpdkTransport,
authorized_keys: &AuthorizedKeys,
control_tx: &mpsc::Sender<ControlEvent>,
id_to_handle: &mut FxHashMap<u64, SocketHandle>,
handle: SocketHandle,
) {
if conn.parse_buf.len() < 4 {
return;
}
let frame_len = u32::from_le_bytes([
conn.parse_buf[0],
conn.parse_buf[1],
conn.parse_buf[2],
conn.parse_buf[3],
]) as usize;
if frame_len > 256 {
debug!(
connection_id = conn.connection_id.0,
frame_len, "DPDK: auth frame too large"
);
send_auth_failed(conn, transport);
return;
}
if conn.parse_buf.len() < 4 + frame_len {
return; }
let consumed = 4 + frame_len;
let (_seq, cr) = match control_codec::decode_challenge_response(&conn.parse_buf[4..consumed]) {
Ok(pair) => pair,
Err(e) => {
debug!(
connection_id = conn.connection_id.0,
error = %e,
"DPDK: auth decode error"
);
let remaining = conn.parse_buf.len() - consumed;
conn.parse_buf.copy_within(consumed.., 0);
conn.parse_buf.truncate(remaining);
send_auth_failed(conn, transport);
return;
}
};
let remaining = conn.parse_buf.len() - consumed;
conn.parse_buf.copy_within(consumed.., 0);
conn.parse_buf.truncate(remaining);
let (signature_bytes, public_key_bytes) = (cr.signature, cr.public_key);
let permission = match authorized_keys.lookup(&public_key_bytes) {
Some(perm) => perm,
None => {
debug!(
connection_id = conn.connection_id.0,
"DPDK: unknown public key"
);
send_auth_failed(conn, transport);
return;
}
};
let nonce = match &conn.auth {
AuthState::WaitingForResponse { nonce, .. } => *nonce,
_ => unreachable!("process_auth_frame called in wrong state"),
};
let verifying_key = match VerifyingKey::from_bytes(&public_key_bytes) {
Ok(k) => k,
Err(_) => {
debug!(
connection_id = conn.connection_id.0,
"DPDK: invalid public key"
);
send_auth_failed(conn, transport);
return;
}
};
let signature = ed25519_dalek::Signature::from_bytes(&signature_bytes);
if verifying_key.verify(&nonce, &signature).is_err() {
debug!(
connection_id = conn.connection_id.0,
"DPDK: signature verification failed"
);
send_auth_failed(conn, transport);
return;
}
let mut buf = [0u8; 16];
let written =
control_codec::encode_transport_response(&TransportResponse::ServerReady, &mut buf)
.expect("ServerReady encodes");
transport.queue_send(conn.handle, &buf[..written]);
debug!(
connection_id = conn.connection_id.0,
addr = %conn.addr,
permission = ?permission,
"DPDK: authenticated"
);
use std::hash::{Hash, Hasher};
let key_hash = {
let mut hasher = rustc_hash::FxHasher::default();
public_key_bytes.hash(&mut hasher);
hasher.finish()
};
conn.key_hash = key_hash;
conn.auth = AuthState::Authenticated { permission };
id_to_handle.insert(conn.connection_id.0, handle);
let _ = control_tx.send(ControlEvent::Connected {
connection_id: conn.connection_id.0,
});
}
fn send_auth_failed(conn: &ConnectionState, transport: &mut DpdkTransport) {
let mut buf = [0u8; 16];
if let Ok(written) =
control_codec::encode_transport_response(&TransportResponse::AuthFailed, &mut buf)
{
transport.queue_send(conn.handle, &buf[..written]);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::num::NonZeroU64;
use melin_types::types::*;
#[derive(Debug, PartialEq)]
enum FrameResult<'a> {
Complete(&'a [u8]),
Incomplete,
Oversized(usize),
}
fn try_extract_frame(buf: &[u8]) -> FrameResult<'_> {
if buf.len() < 4 {
return FrameResult::Incomplete;
}
let frame_len = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) as usize;
if frame_len > MAX_FRAME_SIZE {
return FrameResult::Oversized(frame_len);
}
if buf.len() < 4 + frame_len {
return FrameResult::Incomplete;
}
FrameResult::Complete(&buf[4..4 + frame_len])
}
#[test]
fn extract_frame_empty_buffer() {
assert_eq!(try_extract_frame(&[]), FrameResult::Incomplete);
}
#[test]
fn extract_frame_partial_length() {
assert_eq!(try_extract_frame(&[0x05, 0x00]), FrameResult::Incomplete);
}
#[test]
fn extract_frame_length_only_no_payload() {
assert_eq!(
try_extract_frame(&[0x05, 0x00, 0x00, 0x00]),
FrameResult::Incomplete
);
}
#[test]
fn extract_frame_partial_payload() {
assert_eq!(
try_extract_frame(&[0x05, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC]),
FrameResult::Incomplete
);
}
#[test]
fn extract_frame_complete() {
let buf = [0x03, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC];
assert_eq!(
try_extract_frame(&buf),
FrameResult::Complete(&[0xAA, 0xBB, 0xCC])
);
}
#[test]
fn extract_frame_complete_with_trailing_data() {
let buf = [0x03, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xFF, 0xFF];
assert_eq!(
try_extract_frame(&buf),
FrameResult::Complete(&[0xAA, 0xBB, 0xCC])
);
}
#[test]
fn extract_frame_zero_length() {
let buf = [0x00, 0x00, 0x00, 0x00];
assert_eq!(try_extract_frame(&buf), FrameResult::Complete(&[]));
}
#[test]
fn extract_frame_oversized() {
let len = (MAX_FRAME_SIZE + 1) as u32;
let buf = len.to_le_bytes();
assert_eq!(
try_extract_frame(&buf),
FrameResult::Oversized(MAX_FRAME_SIZE + 1)
);
}
#[test]
fn extract_frame_exactly_max_size() {
let len = MAX_FRAME_SIZE as u32;
let mut buf = Vec::from(len.to_le_bytes().as_slice());
buf.extend(vec![0u8; MAX_FRAME_SIZE]);
assert!(matches!(try_extract_frame(&buf), FrameResult::Complete(_)));
}
fn make_order(id: u64, account: u32, side: Side) -> Order {
Order {
id: OrderId(id),
account: AccountId(account),
side,
order_type: OrderType::Limit {
price: Price(NonZeroU64::new(100).unwrap()),
post_only: false,
},
quantity: Quantity(NonZeroU64::new(10).unwrap()),
time_in_force: TimeInForce::GTC,
stp: SelfTradeProtection::CancelNewest,
expiry_ns: 0,
}
}
#[test]
fn wire_round_trip_submit_order() {
let order = make_order(99, 1, Side::Sell);
let req = Request::SubmitOrder {
symbol: Symbol(5),
order,
};
let mut buf = [0u8; 256];
let written = codec::encode_request(&req, 0, &mut buf).unwrap();
let frame = try_extract_frame(&buf[..written]);
match frame {
FrameResult::Complete(payload) => {
let (_, decoded) = codec::decode_request(payload).unwrap();
assert!(
matches!(decoded, Request::SubmitOrder { symbol, .. } if symbol == Symbol(5))
);
}
other => panic!("expected Complete, got {other:?}"),
}
}
#[test]
fn wire_round_trip_cancel() {
let req = Request::CancelOrder {
symbol: Symbol(1),
account: AccountId(2),
order_id: OrderId(3),
};
let mut buf = [0u8; 256];
let written = codec::encode_request(&req, 0, &mut buf).unwrap();
let frame = try_extract_frame(&buf[..written]);
match frame {
FrameResult::Complete(payload) => {
let (_, decoded) = codec::decode_request(payload).unwrap();
assert!(
matches!(decoded, Request::CancelOrder { order_id, .. } if order_id == OrderId(3))
);
}
other => panic!("expected Complete, got {other:?}"),
}
}
#[test]
fn incremental_frame_accumulation() {
let req = Request::Heartbeat;
let mut wire = [0u8; 64];
let written = codec::encode_request(&req, 0, &mut wire).unwrap();
let wire = &wire[..written];
let mut parse_buf = Vec::new();
for (i, &byte) in wire.iter().enumerate() {
parse_buf.push(byte);
let result = try_extract_frame(&parse_buf);
if i < written - 1 {
assert_eq!(result, FrameResult::Incomplete, "byte {i}");
} else {
assert!(
matches!(result, FrameResult::Complete(_)),
"expected Complete at final byte"
);
}
}
}
#[test]
fn multiple_frames_in_buffer() {
let req1 = Request::Heartbeat;
let req2 = Request::QueryStats;
let mut buf1 = [0u8; 64];
let mut buf2 = [0u8; 64];
let w1 = codec::encode_request(&req1, 0, &mut buf1).unwrap();
let w2 = codec::encode_request(&req2, 0, &mut buf2).unwrap();
let mut combined = Vec::new();
combined.extend_from_slice(&buf1[..w1]);
combined.extend_from_slice(&buf2[..w2]);
let result1 = try_extract_frame(&combined);
let payload1_len = match result1 {
FrameResult::Complete(p) => {
let (_, decoded) = codec::decode_request(p).unwrap();
assert!(matches!(decoded, Request::Heartbeat));
p.len()
}
other => panic!("expected Complete, got {other:?}"),
};
let remaining = &combined[4 + payload1_len..];
let result2 = try_extract_frame(remaining);
match result2 {
FrameResult::Complete(p) => {
let (_, decoded) = codec::decode_request(p).unwrap();
assert!(matches!(decoded, Request::QueryStats));
}
other => panic!("expected Complete, got {other:?}"),
}
}
}