@@ -160,6 +160,31 @@ pub fn resolve_gpus(gpu_cfg: &rpc::GpuConfig) -> Result<GpuConfig> {
160160 }
161161}
162162
163+ fn port_mappings_conflict ( left : & PortMapping , right : & PortMapping ) -> bool {
164+ left. protocol . as_str ( ) == right. protocol . as_str ( )
165+ && left. from == right. from
166+ && ( left. address == right. address
167+ || left. address . is_unspecified ( )
168+ || right. address . is_unspecified ( ) )
169+ }
170+
171+ fn validate_unique_port_mappings ( mappings : & [ PortMapping ] ) -> Result < ( ) > {
172+ for ( index, mapping) in mappings. iter ( ) . enumerate ( ) {
173+ if mappings[ ..index]
174+ . iter ( )
175+ . any ( |other| port_mappings_conflict ( mapping, other) )
176+ {
177+ bail ! (
178+ "duplicate host port mapping: {} {}:{}" ,
179+ mapping. protocol. as_str( ) ,
180+ mapping. address,
181+ mapping. from
182+ ) ;
183+ }
184+ }
185+ Ok ( ( ) )
186+ }
187+
163188// Shared function to create manifest from VM configuration
164189pub fn create_manifest_from_vm_config (
165190 request : VmConfiguration ,
@@ -194,6 +219,7 @@ pub fn create_manifest_from_vm_config(
194219 } )
195220 } )
196221 . collect :: < Result < Vec < _ > > > ( ) ?;
222+ validate_unique_port_mappings ( & port_map) ?;
197223
198224 let app_id = match & request. app_id {
199225 Some ( id) => id. strip_prefix ( "0x" ) . unwrap_or ( id) . to_lowercase ( ) ,
@@ -398,6 +424,38 @@ fn networks_from_vm_config(
398424}
399425
400426impl RpcHandler {
427+ fn validate_port_mapping_conflicts (
428+ & self ,
429+ vm_id : Option < & str > ,
430+ mappings : & [ PortMapping ] ,
431+ ) -> Result < ( ) > {
432+ validate_unique_port_mappings ( mappings) ?;
433+ let state = self . app . lock ( ) ;
434+ for vm in state. iter_vms ( ) {
435+ if vm_id == Some ( vm. config . manifest . id . as_str ( ) ) {
436+ continue ;
437+ }
438+ for mapping in mappings {
439+ if vm
440+ . config
441+ . manifest
442+ . port_map
443+ . iter ( )
444+ . any ( |existing| port_mappings_conflict ( mapping, existing) )
445+ {
446+ bail ! (
447+ "host port mapping conflicts with VM {}: {} {}:{}" ,
448+ vm. config. manifest. id,
449+ mapping. protocol. as_str( ) ,
450+ mapping. address,
451+ mapping. from
452+ ) ;
453+ }
454+ }
455+ }
456+ Ok ( ( ) )
457+ }
458+
401459 fn resolve_gpus ( & self , gpu_cfg : & rpc:: GpuConfig ) -> Result < GpuConfig > {
402460 resolve_gpus_with_config ( gpu_cfg, & self . app . config . cvm )
403461 }
@@ -461,6 +519,7 @@ impl RpcHandler {
461519impl VmmRpc for RpcHandler {
462520 async fn create_vm ( self , request : VmConfiguration ) -> Result < Id > {
463521 let manifest = create_manifest_from_vm_config ( request. clone ( ) , & self . app . config . cvm ) ?;
522+ self . validate_port_mapping_conflicts ( None , & manifest. port_map ) ?;
464523 let id = manifest. id . clone ( ) ;
465524 let app_id = manifest. app_id . clone ( ) ;
466525 let vm_work_dir = self . app . work_dir ( & id) ;
@@ -590,7 +649,7 @@ impl VmmRpc for RpcHandler {
590649 manifest. no_tee = no_tee;
591650 }
592651 if request. update_ports {
593- manifest . port_map = request
652+ let port_map = request
594653 . ports
595654 . iter ( )
596655 . map ( |p| {
@@ -602,6 +661,8 @@ impl VmmRpc for RpcHandler {
602661 } )
603662 } )
604663 . collect :: < Result < Vec < _ > > > ( ) ?;
664+ self . validate_port_mapping_conflicts ( Some ( & request. id ) , & port_map) ?;
665+ manifest. port_map = port_map;
605666 }
606667 if request. update_kms_urls {
607668 manifest. kms_urls = request. kms_urls . clone ( ) ;
0 commit comments