@@ -26,7 +26,7 @@ use hyperlight_unikraft::pyhl::{
2626 copy_replace, discover_source_artifacts, extract_from_ghcr, GHCR_INITRD_IMAGE ,
2727 GHCR_KERNEL_IMAGE ,
2828} ;
29- use hyperlight_unikraft:: { AllowList , BlockList , NetworkPolicy , Preopen , Sandbox } ;
29+ use hyperlight_unikraft:: { AllowList , BlockList , ListenPorts , NetworkPolicy , Preopen , Sandbox } ;
3030use std:: fs;
3131use std:: path:: { Path , PathBuf } ;
3232use std:: time:: Instant ;
@@ -41,6 +41,7 @@ fn build_network_policy(
4141 net : bool ,
4242 net_allow : & [ String ] ,
4343 net_block : & [ String ] ,
44+ has_ports : bool ,
4445) -> Result < Option < NetworkPolicy > > {
4546 if !net_allow. is_empty ( ) {
4647 Ok ( Some ( NetworkPolicy :: AllowList ( AllowList :: from_hosts (
@@ -50,13 +51,21 @@ fn build_network_policy(
5051 Ok ( Some ( NetworkPolicy :: BlockList ( BlockList :: from_hosts (
5152 net_block,
5253 ) ?) ) )
53- } else if net {
54+ } else if net || has_ports {
5455 Ok ( Some ( NetworkPolicy :: AllowAll ) )
5556 } else {
5657 Ok ( None )
5758 }
5859}
5960
61+ fn build_listen_ports ( ports : & [ u16 ] ) -> Option < ListenPorts > {
62+ if ports. is_empty ( ) {
63+ None
64+ } else {
65+ Some ( ListenPorts :: from_ports ( ports. iter ( ) . copied ( ) ) )
66+ }
67+ }
68+
6069/// Keep in sync with `py_initialize_once` in examples/python-agent-driver/
6170/// hl_pydriver.c. These modules are imported during `pyhl setup`'s warmup
6271/// so they're already in `sys.modules` in every `pyhl run` invocation —
@@ -187,6 +196,12 @@ struct SetupArgs {
187196 conflicts_with = "net_allow"
188197 ) ]
189198 net_block : Vec < String > ,
199+
200+ /// Allow the guest to bind (listen) on the given port. Implies --net.
201+ /// Without this flag, `net_bind` is rejected (outbound-only).
202+ /// Repeatable: `--port 8080 --port 3000`.
203+ #[ arg( long, value_name = "PORT" ) ]
204+ port : Vec < u16 > ,
190205}
191206
192207#[ derive( Args ) ]
@@ -236,6 +251,12 @@ struct RunArgs {
236251 ) ]
237252 net_block : Vec < String > ,
238253
254+ /// Allow the guest to bind (listen) on the given port. Implies --net.
255+ /// Without this flag, `net_bind` is rejected (outbound-only).
256+ /// Repeatable: `--port 8080 --port 3000`.
257+ #[ arg( long, value_name = "PORT" ) ]
258+ port : Vec < u16 > ,
259+
239260 /// Print evolve/warmup/per-run timing to stderr. Off by default so the
240261 /// user's script output is clean.
241262 #[ arg( short = 'v' , long = "verbose" ) ]
@@ -393,7 +414,13 @@ fn cmd_setup(args: SetupArgs) -> Result<()> {
393414 . iter ( )
394415 . map ( |m| parse_mount ( m) )
395416 . collect :: < Result < _ > > ( ) ?;
396- let network = build_network_policy ( args. net , & args. net_allow , & args. net_block ) ?;
417+ let listen_ports = build_listen_ports ( & args. port ) ;
418+ let network = build_network_policy (
419+ args. net ,
420+ & args. net_allow ,
421+ & args. net_block ,
422+ listen_ports. is_some ( ) ,
423+ ) ?;
397424
398425 eprintln ! ( "pyhl: warming up Python and persisting snapshot…" ) ;
399426 let t_warm = Instant :: now ( ) ;
@@ -407,6 +434,9 @@ fn cmd_setup(args: SetupArgs) -> Result<()> {
407434 if let Some ( ref policy) = network {
408435 builder = builder. network ( policy. clone ( ) ) ;
409436 }
437+ if let Some ( ref lp) = listen_ports {
438+ builder = builder. listen_ports ( lp. clone ( ) ) ;
439+ }
410440 let mut sbox = builder. build ( ) ?;
411441 sbox. restore ( ) ?;
412442 let _: ( ) = sbox. call_named ( "run" , "pass" . to_string ( ) ) ?;
@@ -531,7 +561,13 @@ fn cmd_run(args: RunArgs) -> Result<()> {
531561 . iter ( )
532562 . map ( |m| parse_mount ( m) )
533563 . collect :: < Result < _ > > ( ) ?;
534- let network = build_network_policy ( args. net , & args. net_allow , & args. net_block ) ?;
564+ let listen_ports = build_listen_ports ( & args. port ) ;
565+ let network = build_network_policy (
566+ args. net ,
567+ & args. net_allow ,
568+ & args. net_block ,
569+ listen_ports. is_some ( ) ,
570+ ) ?;
535571
536572 let initrd = home. join ( INITRD_FILE ) ;
537573
@@ -546,6 +582,7 @@ fn cmd_run(args: RunArgs) -> Result<()> {
546582 & run_preopens,
547583 initrd_ref,
548584 network. as_ref ( ) ,
585+ listen_ports. as_ref ( ) ,
549586 ) ?;
550587 if args. verbose {
551588 eprintln ! (
0 commit comments