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]
52 pub fn new(
53 gateway_ip: Ipv4Addr,
54 gateway_mac: [u8; 6],
55 guest_ip: Ipv4Addr,
56 reply_tx: mpsc::Sender<Vec<u8>>,
57 cancel: CancellationToken,
58 mtu: usize,
59 ) -> Self {
60 let inbound = super::inbound_relay::InboundRelay::new(
61 reply_tx.clone(),
62 gateway_mac,
63 gateway_ip,
64 guest_ip,
65 mtu,
66 );
67 Self {
68 icmp: IcmpProxy::new(reply_tx.clone(), gateway_mac),
69 udp: UdpProxy::new(reply_tx.clone(), gateway_mac, gateway_ip, mtu),
70 inbound,
71 reply_tx,
72 cancel,
73 }
74 }
75
76 #[must_use]
79 pub fn reply_sender(&self) -> mpsc::Sender<Vec<u8>> {
80 self.reply_tx.clone()
81 }
82
83 pub fn set_proxy_awareness(&mut self, dns_log: DnsResolutionLog, proxy_env: ProxyEnvironment) {
90 self.udp.dns_log = Some(dns_log);
91 self.udp.proxy_env = Some(proxy_env);
92 }
93
94 pub fn handle_outbound(&mut self, frame: &[u8], guest_mac: [u8; 6]) {
99 if frame.len() < ETH_HEADER_LEN + 20 {
100 return;
101 }
102
103 if self.inbound.try_handle_reply(frame, guest_mac) {
106 return;
107 }
108
109 let protocol = frame[ETH_HEADER_LEN + 9];
110 let cancel = self.cancel.clone();
111 match protocol {
112 1 => self.icmp.proxy_icmp(frame, guest_mac, cancel),
113 17 => self.udp.proxy_udp(frame, guest_mac, cancel),
114 _ => {
115 tracing::trace!("Host egress: dropping protocol {}", protocol);
116 }
117 }
118 }
119
120 pub fn handle_inbound_command(
122 &mut self,
123 cmd: super::inbound_relay::InboundCommand,
124 guest_mac: [u8; 6],
125 ) {
126 use super::inbound_relay::InboundCommand;
127 if let InboundCommand::UdpReceived {
128 host_port,
129 data,
130 reply_tx,
131 ..
132 } = cmd
133 {
134 self.inbound
137 .inject_udp(host_port, &data, reply_tx, guest_mac);
138 }
139 }
140
141 pub fn maintenance(&mut self) {
143 self.udp.cleanup_stale_flows();
144 self.inbound.cleanup();
145 }
146
147 pub fn expire_flow(&mut self, _addr: std::net::SocketAddr) {
152 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_host_egress_creation() {
165 let (tx, _rx) = mpsc::channel(16);
166 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
167 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
168 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
169 let _egress = HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new(), 1500);
170 }
171
172 #[test]
173 fn test_host_egress_drops_unknown_protocol() {
174 let (tx, _rx) = mpsc::channel(16);
175 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
176 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
177 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
178 let mut egress =
179 HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new(), 1500);
180
181 let mut frame = vec![0u8; ETH_HEADER_LEN + 20];
183 frame[12..14].copy_from_slice(&0x0800u16.to_be_bytes());
185 frame[ETH_HEADER_LEN] = 0x45; frame[ETH_HEADER_LEN + 9] = 50; let guest_mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x99];
190 egress.handle_outbound(&frame, guest_mac);
192 }
193
194 #[test]
195 fn test_host_egress_ignores_short_frames() {
196 let (tx, _rx) = mpsc::channel(16);
197 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
198 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
199 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
200 let mut egress =
201 HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new(), 1500);
202
203 let guest_mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x99];
204 egress.handle_outbound(&[0u8; 10], guest_mac);
206 }
207
208 #[tokio::test]
210 async fn test_reply_channel_flow() {
211 let (tx, mut rx) = mpsc::channel(16);
212 let gw_ip = Ipv4Addr::new(192, 168, 64, 1);
213 let gw_mac = [0x02, 0xAB, 0xCD, 0x00, 0x00, 0x01];
214
215 let test_frame = vec![0xDE, 0xAD, 0xBE, 0xEF];
217 tx.send(test_frame.clone()).await.unwrap();
218
219 let received = rx.recv().await.unwrap();
220 assert_eq!(received, test_frame);
221
222 let guest_ip = Ipv4Addr::new(192, 168, 64, 2);
224 let _egress = HostEgress::new(gw_ip, gw_mac, guest_ip, tx, CancellationToken::new(), 1500);
225 }
226}