@@ -7,14 +7,82 @@ use anyhow::{Context, Result};
77use log:: debug;
88use rustix:: io:: dup;
99
10+ struct PublishSpec < ' a > {
11+ udp : bool ,
12+ guest_range : ( u32 , u32 ) ,
13+ host_range : ( u32 , u32 ) ,
14+ ip : & ' a str ,
15+ }
16+
17+ fn parse_range ( r : & str ) -> Result < ( u32 , u32 ) > {
18+ Ok ( if let Some ( pos) = r. find ( '-' ) {
19+ ( r[ ..pos] . parse ( ) ?, r[ pos + 1 ..] . parse ( ) ?)
20+ } else {
21+ let val = r. parse ( ) ?;
22+ ( val, val)
23+ } )
24+ }
25+
26+ impl PublishSpec < ' _ > {
27+ fn parse ( mut arg : & str ) -> Result < PublishSpec > {
28+ let mut udp = false ;
29+ if arg. ends_with ( "/udp" ) {
30+ udp = true ;
31+ }
32+ if let Some ( pos) = arg. rfind ( '/' ) {
33+ arg = & arg[ ..pos] ;
34+ }
35+ let guest_range_start = arg. rfind ( ':' ) ;
36+ let guest_range = parse_range ( & arg[ guest_range_start. map ( |x| x + 1 ) . unwrap_or ( 0 ) ..] ) ?;
37+ let mut ip = "" ;
38+ let host_range = match guest_range_start {
39+ None => guest_range,
40+ Some ( guest_range_start) => {
41+ arg = & arg[ ..guest_range_start] ;
42+ let ip_start = arg. rfind ( ':' ) ;
43+ if let Some ( ip_start) = ip_start {
44+ ip = & arg[ ..ip_start] ;
45+ arg = & arg[ ip_start + 1 ..] ;
46+ }
47+ if arg. is_empty ( ) {
48+ guest_range
49+ } else {
50+ parse_range ( arg) ?
51+ }
52+ } ,
53+ } ;
54+ Ok ( PublishSpec {
55+ ip,
56+ host_range,
57+ guest_range,
58+ udp,
59+ } )
60+ }
61+ fn to_args ( & self ) -> [ String ; 2 ] {
62+ let optslash = if self . ip . is_empty ( ) { "" } else { "/" } ;
63+ [
64+ if self . udp { "-u" } else { "-t" } . to_owned ( ) ,
65+ format ! (
66+ "{}{}{}-{}:{}-{}" ,
67+ self . ip,
68+ optslash,
69+ self . host_range. 0 ,
70+ self . host_range. 1 ,
71+ self . guest_range. 0 ,
72+ self . guest_range. 1
73+ ) ,
74+ ]
75+ }
76+ }
77+
1078pub fn connect_to_passt < P > ( passt_socket_path : P ) -> Result < UnixStream >
1179where
1280 P : AsRef < Path > ,
1381{
1482 Ok ( UnixStream :: connect ( passt_socket_path) ?)
1583}
1684
17- pub fn start_passt ( ) -> Result < UnixStream > {
85+ pub fn start_passt ( publish_ports : & [ String ] ) -> Result < UnixStream > {
1886 // SAFETY: The child process should not inherit the file descriptor of
1987 // `parent_socket`. There is no documented guarantee of this, but the
2088 // implementation as of writing atomically sets `SOCK_CLOEXEC`.
@@ -35,13 +103,16 @@ pub fn start_passt() -> Result<UnixStream> {
35103
36104 debug ! ( fd = child_fd. as_raw_fd( ) ; "passing fd to passt" ) ;
37105
106+ let mut cmd = Command :: new ( "passt" ) ;
38107 // SAFETY: `child_fd` is an `OwnedFd` and consumed to prevent closing on drop,
39108 // as it will now be owned by the child process.
40109 // See https://doc.rust-lang.org/std/io/index.html#io-safety
41- let child = Command :: new ( "passt" )
42- . args ( [ "-q" , "-f" , "--fd" ] )
43- . arg ( format ! ( "{}" , child_fd. into_raw_fd( ) ) )
44- . spawn ( ) ;
110+ cmd. args ( [ "-q" , "-f" , "--fd" ] )
111+ . arg ( format ! ( "{}" , child_fd. into_raw_fd( ) ) ) ;
112+ for spec in publish_ports {
113+ cmd. args ( PublishSpec :: parse ( spec) ?. to_args ( ) ) ;
114+ }
115+ let child = cmd. spawn ( ) ;
45116 if let Err ( err) = child {
46117 return Err ( err) . context ( "Failed to execute `passt` as child process" ) ;
47118 }
0 commit comments