use rkyv::{Archive, Deserialize, Serialize};
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct ProxyFrame {
pub conn_id: u64,
pub rip: [u8; 16],
pub rport: u16,
pub payload: Vec<u8>,
pub uuid: [u8; 16],
pub timestamp: u64,
pub checksum: u32,
pub flags: FrameFlags,
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, Copy, Default, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct FrameFlags {
pub is_control: bool,
pub is_compressed: bool,
pub is_final: bool,
pub needs_ack: bool,
pub is_ack: bool,
}
impl ProxyFrame {
pub fn new_data(conn_id: u64, rip: [u8; 16], rport: u16, payload: Vec<u8>) -> Self {
let checksum = crc32fast::hash(&payload);
let uuid = uuid::Uuid::new_v4().into_bytes();
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
Self {
conn_id,
rip,
rport,
payload,
uuid,
timestamp,
checksum,
flags: FrameFlags::default(),
}
}
pub fn new_control(payload: Vec<u8>) -> Self {
let mut frame = Self::new_data(0, [0; 16], 0, payload);
frame.flags.is_control = true;
frame
}
pub fn new_close(conn_id: u64) -> Self {
let mut frame = Self::new_data(conn_id, [0; 16], 0, vec![]);
frame.flags.is_final = true;
frame
}
pub fn verify_checksum(&self) -> bool {
crc32fast::hash(&self.payload) == self.checksum
}
pub fn ipv4_to_mapped(ipv4: [u8; 4]) -> [u8; 16] {
let mut mapped = [0u8; 16];
mapped[10] = 0xff;
mapped[11] = 0xff;
mapped[12..16].copy_from_slice(&ipv4);
mapped
}
pub fn mapped_to_ipv4(mapped: &[u8; 16]) -> Option<[u8; 4]> {
if mapped[..10] == [0; 10] && mapped[10] == 0xff && mapped[11] == 0xff {
let mut ipv4 = [0u8; 4];
ipv4.copy_from_slice(&mapped[12..16]);
Some(ipv4)
} else {
None
}
}
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct GroupInfo {
pub group_id: i32,
pub name: String,
pub node_count: u32,
pub load: u8,
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub enum ControlMessage {
DohQuery { query: Vec<u8> },
DohResponse { response: Vec<u8> },
Ping { nonce: u64 },
Pong { nonce: u64 },
KeyRotation {
new_pk: [u8; 32],
valid_from: u64,
valid_until: u64,
},
Emergency {
level: EmergencyLevel,
trigger_after: u64,
},
GroupList { groups: Vec<GroupInfo> },
GroupSelect { group_id: i32 },
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub enum EmergencyLevel {
Warning,
Stop,
Shutdown,
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub struct PlainPacket {
pub magic: u32,
pub conn_id: u64,
pub handler_id: u64,
pub rip: [u8; 16],
pub rport: u16,
pub payload: Vec<u8>,
pub checksum: u32,
pub is_response: bool,
}
impl PlainPacket {
pub const MAGIC: u32 = 0xDEADBEEF;
pub fn from_frame(frame: &ProxyFrame, handler_id: u64) -> Self {
Self {
magic: Self::MAGIC,
conn_id: frame.conn_id,
handler_id,
rip: frame.rip,
rport: frame.rport,
payload: frame.payload.clone(),
checksum: frame.checksum,
is_response: false,
}
}
pub fn response(conn_id: u64, handler_id: u64, payload: Vec<u8>) -> Self {
let checksum = crc32fast::hash(&payload);
Self {
magic: Self::MAGIC,
conn_id,
handler_id,
rip: [0; 16],
rport: 0,
payload,
checksum,
is_response: true,
}
}
pub fn is_valid(&self) -> bool {
self.magic == Self::MAGIC && crc32fast::hash(&self.payload) == self.checksum
}
}
#[derive(Archive, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[rkyv(compare(PartialEq), derive(Debug))]
pub enum RaftCommand {
Upsert {
conn_id: u64,
txid: u64,
client_addr: [u8; 16],
nat_entry: (u16, u16),
assigned_pod: u32,
},
Delete { conn_id: u64 },
Cleanup { before_timestamp: u64 },
Noop,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_creation() {
let payload = vec![1, 2, 3, 4, 5];
let frame = ProxyFrame::new_data(
42,
ProxyFrame::ipv4_to_mapped([192, 168, 1, 1]),
8080,
payload.clone(),
);
assert_eq!(frame.conn_id, 42);
assert_eq!(frame.rport, 8080);
assert_eq!(frame.payload, payload);
assert!(frame.verify_checksum());
}
#[test]
fn test_ipv4_mapping() {
let ipv4 = [192, 168, 1, 1];
let mapped = ProxyFrame::ipv4_to_mapped(ipv4);
let extracted = ProxyFrame::mapped_to_ipv4(&mapped);
assert_eq!(extracted, Some(ipv4));
}
#[test]
fn test_serialization() {
let frame = ProxyFrame::new_data(1, [0; 16], 443, vec![0xDE, 0xAD, 0xBE, 0xEF]);
let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&frame).unwrap();
let archived = rkyv::access::<ArchivedProxyFrame, rkyv::rancor::Error>(&bytes).unwrap();
assert_eq!(archived.conn_id, 1);
assert_eq!(archived.rport, 443);
}
}