1616import collections
1717import copy
1818import datetime
19+ import functools
1920
2021import netaddr
2122from neutron_lib .api .definitions import l3
5152from neutron .common import utils as common_utils
5253from neutron .conf .agent import ovs_conf
5354from neutron .conf .plugins .ml2 .drivers .ovn import ovn_conf
55+ from neutron .conf .plugins .ml2 .drivers .ovn .ovn_conf \
56+ import is_ovn_router_indirect_snat_enabled as is_nested_snat
5457from neutron .db import ovn_revision_numbers_db as db_rev
5558from neutron .db import segments_db
5659from neutron .objects import router
6467LOG = log .getLogger (__name__ )
6568
6669
70+ def _has_separate_snat_per_subnet (router ):
71+ return utils .is_snat_enabled (router ) and not is_nested_snat ()
72+
73+
6774OvnPortInfo = collections .namedtuple (
6875 "OvnPortInfo" ,
6976 [
@@ -1219,23 +1226,23 @@ def _get_gw_info(self, context, port_dict):
12191226 else const .IPv6_ANY ))
12201227 return gateways_info
12211228
1222- def _delete_router_ext_gw (self , router , networks , txn ):
1229+ def _delete_router_ext_gw (self , router_id , txn ):
12231230 context = n_context .get_admin_context ()
1224- if not networks :
1225- networks = []
1226- router_id = router ['id' ]
1231+ cidrs = self ._get_snat_cidrs_for_external_router (context , router_id )
12271232 gw_lrouter_name = utils .ovn_name (router_id )
12281233 deleted_ports = []
12291234 for gw_port in self ._get_router_gw_ports (context , router_id ):
12301235 for gw_info in self ._get_gw_info (context , gw_port ):
1231- if gw_info .ip_version == const .IP_VERSION_4 :
1232- for network in networks :
1233- txn .add (self ._nb_idl .delete_nat_rule_in_lrouter (
1234- gw_lrouter_name , type = 'snat' , logical_ip = network ,
1235- external_ip = gw_info .router_ip ))
12361236 txn .add (self ._nb_idl .delete_static_route (
12371237 gw_lrouter_name , ip_prefix = gw_info .ip_prefix ,
12381238 nexthop = gw_info .gateway_ip ))
1239+ if gw_info .ip_version != const .IP_VERSION_4 :
1240+ continue
1241+ for cidr in cidrs :
1242+ txn .add (self ._nb_idl .delete_nat_rule_in_lrouter (
1243+ gw_lrouter_name , type = 'snat' ,
1244+ external_ip = gw_info .router_ip ,
1245+ logical_ip = cidr ))
12391246 txn .add (self ._nb_idl .delete_lrouter_port (
12401247 utils .ovn_lrouter_port_name (gw_port ['id' ]),
12411248 gw_lrouter_name ))
@@ -1281,7 +1288,7 @@ def _get_nets_and_ipv6_ra_confs_for_router_port(self, context, port):
12811288
12821289 return list (networks ), ipv6_ra_configs
12831290
1284- def _add_router_ext_gw (self , context , router , networks , txn ):
1291+ def _add_router_ext_gw (self , context , router , txn ):
12851292 lrouter_name = utils .ovn_name (router ['id' ])
12861293 router_default_route_ecmp_enabled = router .get (
12871294 'enable_default_route_ecmp' , False )
@@ -1319,9 +1326,9 @@ def _add_router_ext_gw(self, context, router, networks, txn):
13191326 maintain_bfd = router_default_route_bfd_enabled ,
13201327 ** columns ))
13211328
1322- # 3. Add snat rules for tenant networks in lrouter if snat is enabled
1323- if utils .is_snat_enabled (router ) and networks :
1324- self .update_nat_rules (router , networks , enable_snat = True , txn = txn )
1329+ # 3. Add necessary snat rule(s) in lrouter if snat is enabled
1330+ if utils .is_snat_enabled (router ):
1331+ self .update_nat_rules (router [ 'id' ] , enable_snat = True , txn = txn )
13251332 return added_ports
13261333
13271334 def _check_external_ips_changed (self , ovn_snats ,
@@ -1423,17 +1430,20 @@ def _get_v4_network_for_router_port(self, context, port):
14231430 cidr = subnet ['cidr' ]
14241431 return cidr
14251432
1426- def _get_v4_network_of_all_router_ports (self , context , router_id ,
1427- ports = None ):
1433+ def _get_v4_network_of_all_router_ports (self , context , router_id ):
14281434 networks = []
1429- ports = ports or self ._get_router_ports (context , router_id )
1430- for port in ports :
1435+ for port in self ._get_router_ports (context , router_id ):
14311436 network = self ._get_v4_network_for_router_port (context , port )
14321437 if network :
14331438 networks .append (network )
1434-
14351439 return networks
14361440
1441+ def _get_snat_cidrs_for_external_router (self , context , router_id ):
1442+ if is_nested_snat ():
1443+ return [ovn_const .OVN_DEFAULT_SNAT_CIDR ]
1444+ # nat rule per attached subnet per external ip
1445+ return self ._get_v4_network_of_all_router_ports (context , router_id )
1446+
14371447 def _gen_router_ext_ids (self , router ):
14381448 return {
14391449 ovn_const .OVN_ROUTER_NAME_EXT_ID_KEY :
@@ -1462,12 +1472,9 @@ def create_router(self, context, router, add_external_gateway=True):
14621472 # by the ovn_db_sync.py script, remove it after the database
14631473 # synchronization work
14641474 if add_external_gateway :
1465- networks = self ._get_v4_network_of_all_router_ports (
1466- context , router ['id' ])
1467- if (router .get (l3_ext_gw_multihoming .EXTERNAL_GATEWAYS ) and
1468- networks is not None ):
1475+ if router .get (l3_ext_gw_multihoming .EXTERNAL_GATEWAYS ):
14691476 added_gw_ports = self ._add_router_ext_gw (
1470- context , router , networks , txn )
1477+ context , router , txn )
14711478
14721479 self ._qos_driver .create_router (txn , router )
14731480
@@ -1495,7 +1502,6 @@ def update_router(self, context, new_router, router_object=None):
14951502 l3_ext_gw_multihoming .EXTERNAL_GATEWAYS )
14961503
14971504 ovn_snats = utils .get_lrouter_snats (ovn_router )
1498- networks = self ._get_v4_network_of_all_router_ports (context , router_id )
14991505 try :
15001506 check_rev_cmd = self ._nb_idl .check_revision_number (
15011507 router_name , new_router , ovn_const .TYPE_ROUTERS )
@@ -1504,13 +1510,13 @@ def update_router(self, context, new_router, router_object=None):
15041510 if gateway_new and not gateway_old :
15051511 # Route gateway is set
15061512 added_gw_ports = self ._add_router_ext_gw (
1507- context , new_router , networks , txn )
1513+ context , new_router , txn )
15081514 elif gateway_old and not gateway_new :
15091515 # router gateway is removed
15101516 txn .add (self ._nb_idl .delete_lrouter_ext_gw (router_name ))
15111517 if router_object :
15121518 deleted_gw_port_ids = self ._delete_router_ext_gw (
1513- router_object , networks , txn )
1519+ router_object [ 'id' ] , txn )
15141520 elif gateway_new and gateway_old :
15151521 # Check if external gateway has changed, if yes, delete
15161522 # the old gateway and add the new gateway
@@ -1528,16 +1534,16 @@ def update_router(self, context, new_router, router_object=None):
15281534 router_name ))
15291535 if router_object :
15301536 deleted_gw_port_ids = self ._delete_router_ext_gw (
1531- router_object , networks , txn )
1537+ router_object [ 'id' ] , txn )
15321538 added_gw_ports = self ._add_router_ext_gw (
1533- context , new_router , networks , txn )
1539+ context , new_router , txn )
15341540 else :
15351541 # Check if snat has been enabled/disabled and update
15361542 new_snat_state = utils .is_snat_enabled (new_router )
1537- if bool (ovn_snats ) != new_snat_state and networks :
1543+ if bool (ovn_snats ) != new_snat_state :
15381544 self .update_nat_rules (
1539- new_router , networks ,
1540- enable_snat = new_snat_state , txn = txn )
1545+ new_router [ 'id' ], enable_snat = new_snat_state ,
1546+ txn = txn )
15411547
15421548 update = {'external_ids' : self ._gen_router_ext_ids (new_router )}
15431549 update ['enabled' ] = new_router .get ('admin_state_up' ) or False
@@ -1785,26 +1791,26 @@ def create_router_port(self, context, router_id, router_interface):
17851791
17861792 gw_ports = self ._get_router_gw_ports (context , router_id )
17871793 if gw_ports :
1788- cidr = None
1789- for fixed_ip in port ['fixed_ips' ]:
1790- subnet = self ._plugin .get_subnet (context ,
1791- fixed_ip ['subnet_id' ])
1792- if multi_prefix :
1793- if 'subnet_id' in router_interface :
1794- if subnet ['id' ] != router_interface ['subnet_id' ]:
1795- continue
1796- if subnet ['ip_version' ] == const .IP_VERSION_4 :
1797- cidr = subnet ['cidr' ]
1798-
17991794 if ovn_conf .is_ovn_emit_need_to_frag_enabled ():
18001795 for gw_port in gw_ports :
18011796 provider_net = self ._plugin .get_network (
18021797 context , gw_port ['network_id' ])
18031798 self .set_gateway_mtu (context , provider_net )
18041799
1805- if utils .is_snat_enabled (router ) and cidr :
1806- self .update_nat_rules (router , networks = [cidr ],
1807- enable_snat = True , txn = txn )
1800+ if _has_separate_snat_per_subnet (router ):
1801+ for fixed_ip in port ['fixed_ips' ]:
1802+ subnet = self ._plugin .get_subnet (
1803+ context , fixed_ip ['subnet_id' ])
1804+ if (multi_prefix and
1805+ 'subnet_id' in router_interface and
1806+ subnet ['id' ] != router_interface ['subnet_id' ]):
1807+ continue
1808+ if subnet ['ip_version' ] == const .IP_VERSION_4 :
1809+ self .update_nat_rules (
1810+ router ['id' ], cidrs = [subnet ['cidr' ]],
1811+ enable_snat = True , txn = txn )
1812+ break # TODO(ihar): handle multiple ipv4 ips?
1813+
18081814 if ovn_conf .is_ovn_distributed_floating_ip ():
18091815 router_gw_ports = self ._get_router_gw_ports (context ,
18101816 router_id )
@@ -1937,19 +1943,17 @@ def delete_router_port(self, context, port_id, subnet_ids=None):
19371943 context , gw_port ['network_id' ])
19381944 self .set_gateway_mtu (context , provider_net , txn = txn )
19391945
1940- cidr = None
1941- for sid in subnet_ids :
1942- try :
1943- subnet = self ._plugin .get_subnet (context , sid )
1944- except n_exc .SubnetNotFound :
1945- continue
1946- if subnet ['ip_version' ] == const .IP_VERSION_4 :
1947- cidr = subnet ['cidr' ]
1948- break
1949-
1950- if utils .is_snat_enabled (router ) and cidr :
1951- self .update_nat_rules (
1952- router , networks = [cidr ], enable_snat = False , txn = txn )
1946+ if _has_separate_snat_per_subnet (router ):
1947+ for sid in subnet_ids :
1948+ try :
1949+ subnet = self ._plugin .get_subnet (context , sid )
1950+ except n_exc .SubnetNotFound :
1951+ continue
1952+ if subnet ['ip_version' ] == const .IP_VERSION_4 :
1953+ self .update_nat_rules (
1954+ router ['id' ], cidrs = [subnet ['cidr' ]],
1955+ enable_snat = False , txn = txn )
1956+ break # TODO(ihar): handle multiple ipv4 ips?
19531957
19541958 if ovn_conf .is_ovn_distributed_floating_ip ():
19551959 router_gw_ports = self ._get_router_gw_ports (context , router_id )
@@ -1968,20 +1972,35 @@ def delete_router_port(self, context, port_id, subnet_ids=None):
19681972 db_rev .bump_revision (
19691973 context , port , ovn_const .TYPE_ROUTER_PORTS )
19701974
1971- def update_nat_rules (self , router , networks , enable_snat , txn = None ):
1972- """Update the NAT rules in a logical router."""
1975+ def _iter_ipv4_gw_addrs (self , context , router_id ):
1976+ yield from (
1977+ gw_info .router_ip
1978+ for gw_port in self ._get_router_gw_ports (context , router_id )
1979+ for gw_info in self ._get_gw_info (context , gw_port )
1980+ if gw_info .ip_version != const .IP_VERSION_6
1981+ )
1982+
1983+ def update_nat_rules (self , router_id , enable_snat , cidrs = None , txn = None ):
1984+ if enable_snat :
1985+ idl_func = self ._nb_idl .add_nat_rule_in_lrouter
1986+ else :
1987+ idl_func = self ._nb_idl .delete_nat_rule_in_lrouter
1988+ func = functools .partial (
1989+ idl_func , utils .ovn_name (router_id ), type = 'snat' )
1990+
19731991 context = n_context .get_admin_context ()
1974- func = (self ._nb_idl .add_nat_rule_in_lrouter if enable_snat else
1975- self ._nb_idl .delete_nat_rule_in_lrouter )
1976- gw_lrouter_name = utils .ovn_name (router ['id' ])
1977- # Update NAT rules only for IPv4 subnets
1978- commands = [func (gw_lrouter_name , type = 'snat' , logical_ip = network ,
1979- external_ip = gw_info .router_ip )
1980- for gw_port in self ._get_router_gw_ports (context ,
1981- router ['id' ])
1982- for gw_info in self ._get_gw_info (context , gw_port )
1983- if gw_info .ip_version != const .IP_VERSION_6
1984- for network in networks ]
1992+ cidrs = (
1993+ cidrs or
1994+ self ._get_snat_cidrs_for_external_router (context , router_id )
1995+ )
1996+ commands = [
1997+ func (logical_ip = cidr , external_ip = router_ip )
1998+ for router_ip in self ._iter_ipv4_gw_addrs (context , router_id )
1999+ for cidr in cidrs
2000+ ]
2001+ if not commands :
2002+ return
2003+
19852004 self ._transaction (commands , txn = txn )
19862005
19872006 def create_provnet_port (self , network_id , segment , txn = None ):
0 commit comments