@@ -180,16 +180,35 @@ async fn make_channel(
180180 }
181181}
182182
183- /// Tower service that connects to a Unix domain socket .
183+ /// Tower service that connects to a Firecracker vsock UDS .
184184///
185- /// Used as a custom tonic connector to reach the Firecracker vsock UDS
186- /// proxy. The URI parameter is ignored — all connections go to the
187- /// configured socket path.
185+ /// Firecracker exposes guest vsock ports via a single Unix domain socket
186+ /// on the host. To reach guest port N, the host must:
187+ /// 1. Connect to the base UDS (e.g. `vsock.sock`)
188+ /// 2. Send `CONNECT N\n`
189+ /// 3. Wait for `OK N\n`
190+ ///
191+ /// The `path` field is in the format `{base_uds}_{port}` (e.g. `vsock.sock_52`).
192+ /// This connector parses the port, connects to the base socket, and performs
193+ /// the CONNECT handshake before handing the stream to tonic.
188194#[ derive( Clone ) ]
189195struct UdsConnector {
190196 path : String ,
191197}
192198
199+ impl UdsConnector {
200+ /// Parse `vsock.sock_52` into (`vsock.sock`, 52).
201+ fn parse_path_and_port ( & self ) -> ( String , u32 ) {
202+ if let Some ( idx) = self . path . rfind ( '_' ) {
203+ if let Ok ( port) = self . path [ idx + 1 ..] . parse :: < u32 > ( ) {
204+ return ( self . path [ ..idx] . to_string ( ) , port) ;
205+ }
206+ }
207+ // Fallback: treat the whole path as the socket (shouldn't happen)
208+ ( self . path . clone ( ) , DEFAULT_AGENT_VSOCK_PORT )
209+ }
210+ }
211+
193212impl tower:: Service < http:: Uri > for UdsConnector {
194213 type Response = hyper_util:: rt:: TokioIo < tokio:: net:: UnixStream > ;
195214 type Error = std:: io:: Error ;
@@ -204,9 +223,28 @@ impl tower::Service<http::Uri> for UdsConnector {
204223 }
205224
206225 fn call ( & mut self , _uri : http:: Uri ) -> Self :: Future {
207- let path = self . path . clone ( ) ;
226+ let ( base_path , port ) = self . parse_path_and_port ( ) ;
208227 Box :: pin ( async move {
209- let stream = tokio:: net:: UnixStream :: connect ( & path) . await ?;
228+ use tokio:: io:: { AsyncReadExt , AsyncWriteExt } ;
229+
230+ // Connect to the Firecracker vsock UDS
231+ let mut stream = tokio:: net:: UnixStream :: connect ( & base_path) . await ?;
232+
233+ // Perform the CONNECT handshake
234+ let connect_cmd = format ! ( "CONNECT {}\n " , port) ;
235+ stream. write_all ( connect_cmd. as_bytes ( ) ) . await ?;
236+
237+ // Read the response (e.g. "OK 52\n")
238+ let mut buf = [ 0u8 ; 64 ] ;
239+ let n = stream. read ( & mut buf) . await ?;
240+ let response = std:: str:: from_utf8 ( & buf[ ..n] ) . unwrap_or ( "" ) ;
241+ if !response. starts_with ( "OK " ) {
242+ return Err ( std:: io:: Error :: new (
243+ std:: io:: ErrorKind :: ConnectionRefused ,
244+ format ! ( "vsock CONNECT handshake failed: {}" , response. trim( ) ) ,
245+ ) ) ;
246+ }
247+
210248 Ok ( hyper_util:: rt:: TokioIo :: new ( stream) )
211249 } )
212250 }
0 commit comments