@@ -4387,6 +4387,67 @@ func (n *ovn) InstanceDevicePortStart(opts *OVNInstanceNICSetupOpts, securityACL
43874387 checkAndStoreIP (net .ParseIP (staticIP ))
43884388 }
43894389
4390+ // Apply device specific external address if any.
4391+ for _ , keyPrefix := range []string {"ipv4" , "ipv6" } {
4392+ // Check if the address is present.
4393+ value := opts .DeviceConfig [fmt .Sprintf ("%s.address.external" , keyPrefix )]
4394+ if value == "" {
4395+ continue
4396+ }
4397+
4398+ // Check if the family is configured.
4399+ if keyPrefix == "ipv4" && ipv4 == "" {
4400+ continue
4401+ }
4402+
4403+ if keyPrefix == "ipv6" && ipv6 == "" {
4404+ continue
4405+ }
4406+
4407+ // Parse the internal address.
4408+ var intNet * net.IPNet
4409+ if keyPrefix == "ipv4" {
4410+ _ , intNet , err = net .ParseCIDR (fmt .Sprintf ("%s/32" , ipv4 ))
4411+ if err != nil {
4412+ return "" , nil , fmt .Errorf ("Invalid internal address %q: %w" , ipv4 , err )
4413+ }
4414+ } else {
4415+ _ , intNet , err = net .ParseCIDR (fmt .Sprintf ("%s/128" , ipv6 ))
4416+ if err != nil {
4417+ return "" , nil , fmt .Errorf ("Invalid internal address %q: %w" , ipv6 , err )
4418+ }
4419+ }
4420+
4421+ // Parse the external address.
4422+ extIP := net .ParseIP (value )
4423+ if extIP == nil {
4424+ return "" , nil , fmt .Errorf ("Invalid external address %q" , value )
4425+ }
4426+
4427+ if err := n .ovnnb .CreateLogicalRouterNAT (
4428+ context .TODO (),
4429+ n .getRouterName (),
4430+ "snat" ,
4431+ intNet ,
4432+ extIP ,
4433+ nil ,
4434+ false ,
4435+ true ,
4436+ ); err != nil {
4437+ return "" , nil , fmt .Errorf ("Failed to add SNAT %q: %w" , value , err )
4438+ }
4439+
4440+ reverter .Add (func () {
4441+ _ = n .ovnnb .DeleteLogicalRouterNAT (
4442+ context .TODO (),
4443+ n .getRouterName (),
4444+ "snat" ,
4445+ false ,
4446+ extIP ,
4447+ )
4448+ })
4449+ }
4450+
43904451 // Get dynamic IPs for switch port if any IPs not assigned statically.
43914452 if (ipv4 != "none" && dnsIPv4 == nil ) || (ipv6 != "none" && dnsIPv6 == nil ) {
43924453 var dynamicIPs []net.IP
@@ -4916,6 +4977,27 @@ func (n *ovn) InstanceDevicePortStop(ovsExternalOVNPort networkOVN.OVNSwitchPort
49164977 }
49174978 }
49184979
4980+ // Tear down per‑NIC egress SNAT rules (ipv4/ipv6.address.external)
4981+ for _ , keyPrefix := range []string {"ipv4" , "ipv6" } {
4982+ // Check if the address is present.
4983+ value := opts .DeviceConfig [fmt .Sprintf ("%s.address.external" , keyPrefix )]
4984+ if value == "" {
4985+ continue
4986+ }
4987+
4988+ // Validate the address.
4989+ extIP := net .ParseIP (value )
4990+ if extIP == nil {
4991+ return fmt .Errorf ("Invalid external address %q" , value )
4992+ }
4993+
4994+ // Remove the SNAT entry.
4995+ err := n .ovnnb .DeleteLogicalRouterNAT (context .TODO (), n .getRouterName (), "snat" , false , extIP )
4996+ if err != nil && ! errors .Is (err , networkOVN .ErrNotFound ) {
4997+ return err
4998+ }
4999+ }
5000+
49195001 return nil
49205002}
49215003
0 commit comments