1pub mod config;
2mod device;
3mod dns;
4mod proxy;
5mod secrets;
6mod stack;
7mod stream;
8mod substitute;
9mod tls;
10
11pub use config::ProxyConfig;
12
13use std::collections::HashMap;
14use std::net::Ipv4Addr;
15use std::num::NonZeroUsize;
16use std::os::unix::io::RawFd;
17use std::sync::{Arc, RwLock};
18
19use lru::LruCache;
20use proxy::ProxyEngine;
21use stack::NetworkStack;
22use tls::CertificateAuthority;
23use tokio::sync::mpsc;
24use tracing::info;
25
26pub type AllowedIps = Arc<RwLock<LruCache<Ipv4Addr, ()>>>;
34
35const ALLOWED_IPS_CAPACITY: usize = 1024;
38
39pub struct ProxyHandle {
41 _stack_thread: std::thread::JoinHandle<()>,
42 _runtime_thread: std::thread::JoinHandle<()>,
43 pub placeholders: HashMap<String, String>,
45 pub ca_cert_pem: Vec<u8>,
47}
48
49fn generate_placeholder() -> String {
51 use std::sync::atomic::{AtomicU64, Ordering};
52 use std::time::{SystemTime, UNIX_EPOCH};
53 static COUNTER: AtomicU64 = AtomicU64::new(0);
54 let ts = SystemTime::now()
55 .duration_since(UNIX_EPOCH)
56 .unwrap()
57 .as_nanos() as u64;
58 let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
59 format!("hanzo_tok_{:016x}{:04x}", ts, seq)
60}
61
62pub fn create_socketpair() -> anyhow::Result<(RawFd, RawFd)> {
65 let mut fds = [0i32; 2];
66 let ret = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_DGRAM, 0, fds.as_mut_ptr()) };
67 if ret != 0 {
68 return Err(anyhow::anyhow!(
69 "socketpair failed: {}",
70 std::io::Error::last_os_error()
71 ));
72 }
73
74 let host_fd = fds[1];
75
76 unsafe {
78 let sndbuf: libc::c_int = 1024 * 1024;
79 let rcvbuf: libc::c_int = 4 * 1024 * 1024;
80 libc::setsockopt(
81 host_fd,
82 libc::SOL_SOCKET,
83 libc::SO_SNDBUF,
84 &sndbuf as *const _ as _,
85 std::mem::size_of::<libc::c_int>() as _,
86 );
87 libc::setsockopt(
88 host_fd,
89 libc::SOL_SOCKET,
90 libc::SO_RCVBUF,
91 &rcvbuf as *const _ as _,
92 std::mem::size_of::<libc::c_int>() as _,
93 );
94 }
95
96 Ok((fds[0], fds[1]))
97}
98
99pub fn start(host_fd: RawFd, config: ProxyConfig) -> anyhow::Result<ProxyHandle> {
104 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
106
107 let ca = CertificateAuthority::new()?;
108 let ca_cert_pem = ca.ca_cert_pem();
109
110 let mut placeholders = HashMap::new();
112 for name in config.secrets.keys() {
113 placeholders.insert(name.clone(), generate_placeholder());
114 }
115
116 let allowed_ips: AllowedIps = Arc::new(RwLock::new(LruCache::new(
117 NonZeroUsize::new(ALLOWED_IPS_CAPACITY).expect("non-zero capacity"),
118 )));
119
120 let (event_tx, event_rx) = mpsc::unbounded_channel();
121 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
122
123 let stack_thread = std::thread::Builder::new()
124 .name("vm-netstack".into())
125 .spawn(move || {
126 let mut stack = NetworkStack::new(host_fd, event_tx, cmd_rx);
127 stack.run();
128 })?;
129
130 let proxy_config = config;
131 let proxy_placeholders = placeholders.clone();
132 let proxy_allowed_ips = allowed_ips.clone();
133 let runtime_thread = std::thread::Builder::new()
134 .name("vm-proxy".into())
135 .spawn(move || {
136 let rt = tokio::runtime::Builder::new_multi_thread()
137 .worker_threads(2)
138 .enable_all()
139 .build()
140 .expect("failed to create tokio runtime for proxy");
141
142 rt.block_on(async move {
143 let mut engine = ProxyEngine::new(
144 proxy_config,
145 event_rx,
146 cmd_tx,
147 ca,
148 proxy_placeholders,
149 proxy_allowed_ips,
150 );
151 engine.run().await;
152 });
153 })?;
154
155 info!("proxy started");
156
157 Ok(ProxyHandle {
158 _stack_thread: stack_thread,
159 _runtime_thread: runtime_thread,
160 placeholders,
161 ca_cert_pem,
162 })
163}