1mod icmp;
18mod udp;
19
20use std::net::Ipv4Addr;
21
22use tokio::sync::mpsc;
23use tokio_util::sync::CancellationToken;
24
25use arcbox_fakeip::dns_log::DnsResolutionLog;
26use arcbox_fakeip::proxy_detect::ProxyEnvironment;
27use arcbox_packet::ethernet::ETH_HEADER_LEN;
28
29use icmp::IcmpProxy;
30use udp::UdpProxy;
31
32pub struct HostEgress {
35 icmp: IcmpProxy,
36 udp: UdpProxy,
37 inbound: super::inbound_relay::InboundRelay,
39 reply_tx: mpsc::Sender<Vec<u8>>,
41 cancel: CancellationToken,
43}
44
45impl HostEgress {
46 #[must_use]
51 pub fn new(
52 gateway_ip: Ipv4Addr,
53 gateway_mac: [u8; 6],
54 guest_ip: Ipv4Addr,
55 reply_tx: mpsc::Sender<Vec<u8>>,
56 cancel: CancellationToken,
57 ) -> Self {
58 let inbound = super::inbound_relay::InboundRelay::new(
59 reply_tx.clone(),
60 gateway_mac,
61 gateway_ip,
62 guest_ip,
63 );
64 Self {
65 icmp: IcmpProxy::new(reply_tx.clone(), gateway_mac),
66 udp: UdpProxy::new(reply_tx.clone(), gateway_mac, gateway_ip),
67 inbound,
68 reply_tx,
69 cancel,
70 }
71 }
72
73 #[must_use]
76 pub fn reply_sender(&self) -> mpsc::Sender<Vec<u8>> {
77 self.reply_tx.clone()
78 }
79
80 pub fn set_proxy_awareness(&mut self, dns_log: DnsResolutionLog, proxy_env: ProxyEnvironment) {
87 self.udp.dns_log = Some(dns_log);
88 self.udp.proxy_env = Some(proxy_env);
89 }
90
91 pub fn handle_outbound(&mut self, frame: &[u8], guest_mac: [u8; 6]) {
96 if frame.len() < ETH_HEADER_LEN + 20 {
97 return;
98 }
99
100 if self.inbound.try_handle_reply(frame, guest_mac) {
103 return;
104 }
105
106 let protocol = frame[ETH_HEADER_LEN + 9];
107 let cancel = self.cancel.clone();
108 match protocol {
109 1 => self.icmp.proxy_icmp(frame, guest_mac, cancel),
110 17 => self.udp.proxy_udp(frame, guest_mac, cancel),
111 _ => {
112 tracing::trace!("Host egress: dropping protocol {}", protocol);
113 }
114 }
115 }
116
117 pub fn handle_inbound_command(
119 &mut self,
120 cmd: super::inbound_relay::InboundCommand,
121 guest_mac: [u8; 6],
122 ) {
123 use super::inbound_relay::InboundCommand;
124 if let InboundCommand::UdpReceived {
125 host_port,
126 data,
127 reply_tx,
128 ..
129 } = cmd
130 {
131 self.inbound
134 .inject_udp(host_port, &data, reply_tx, guest_mac);
135 }
136 }
137
138 pub fn maintenance(&mut self) {
140 self.udp.cleanup_stale_flows();
141 self.inbound.cleanup();
142 }
143
144 pub fn expire_flow(&mut self, _addr: std::net::SocketAddr) {
149 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_host_egress_creation() {
162 let (tx, _rx) = mpsc::channel(16);
163 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
164 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
165 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
166 let _egress = HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new());
167 }
168
169 #[test]
170 fn test_host_egress_drops_unknown_protocol() {
171 let (tx, _rx) = mpsc::channel(16);
172 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
173 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
174 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
175 let mut egress = HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new());
176
177 let mut frame = vec![0u8; ETH_HEADER_LEN + 20];
179 frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
181 frame[ETH_HEADER_LEN] = 0x45; frame[ETH_HEADER_LEN + 9] = 50; let guest_mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x99];
186 egress.handle_outbound(&frame, guest_mac);
188 }
189
190 #[test]
191 fn test_host_egress_ignores_short_frames() {
192 let (tx, _rx) = mpsc::channel(16);
193 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
194 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
195 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
196 let mut egress = HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new());
197
198 let guest_mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x99];
199 egress.handle_outbound(&[0u8; 10], guest_mac);
201 }
202
203 #[tokio::test]
205 async fn test_reply_channel_flow() {
206 let (tx, mut rx) = mpsc::channel(16);
207 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
208 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
209
210 let test_frame = vec![0xDE, 0xAD, 0xBE, 0xEF];
212 tx.send(test_frame.clone()).await.unwrap();
213
214 let received = rx.recv().await.unwrap();
215 assert_eq!(received, test_frame);
216
217 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
219 let _egress = HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new());
220 }
221}