16
16
import collections
17
17
import copy
18
18
import datetime
19
+ import functools
19
20
20
21
import netaddr
21
22
from neutron_lib .api .definitions import l3
51
52
from neutron .common import utils as common_utils
52
53
from neutron .conf .agent import ovs_conf
53
54
from 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
54
57
from neutron .db import ovn_revision_numbers_db as db_rev
55
58
from neutron .db import segments_db
56
59
from neutron .objects import router
64
67
LOG = log .getLogger (__name__ )
65
68
66
69
70
+ def _has_separate_snat_per_subnet (router ):
71
+ return utils .is_snat_enabled (router ) and not is_nested_snat ()
72
+
73
+
67
74
OvnPortInfo = collections .namedtuple (
68
75
"OvnPortInfo" ,
69
76
[
@@ -1219,23 +1226,23 @@ def _get_gw_info(self, context, port_dict):
1219
1226
else const .IPv6_ANY ))
1220
1227
return gateways_info
1221
1228
1222
- def _delete_router_ext_gw (self , router , networks , txn ):
1229
+ def _delete_router_ext_gw (self , router_id , txn ):
1223
1230
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 )
1227
1232
gw_lrouter_name = utils .ovn_name (router_id )
1228
1233
deleted_ports = []
1229
1234
for gw_port in self ._get_router_gw_ports (context , router_id ):
1230
1235
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 ))
1236
1236
txn .add (self ._nb_idl .delete_static_route (
1237
1237
gw_lrouter_name , ip_prefix = gw_info .ip_prefix ,
1238
1238
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 ))
1239
1246
txn .add (self ._nb_idl .delete_lrouter_port (
1240
1247
utils .ovn_lrouter_port_name (gw_port ['id' ]),
1241
1248
gw_lrouter_name ))
@@ -1281,7 +1288,7 @@ def _get_nets_and_ipv6_ra_confs_for_router_port(self, context, port):
1281
1288
1282
1289
return list (networks ), ipv6_ra_configs
1283
1290
1284
- def _add_router_ext_gw (self , context , router , networks , txn ):
1291
+ def _add_router_ext_gw (self , context , router , txn ):
1285
1292
lrouter_name = utils .ovn_name (router ['id' ])
1286
1293
router_default_route_ecmp_enabled = router .get (
1287
1294
'enable_default_route_ecmp' , False )
@@ -1319,9 +1326,9 @@ def _add_router_ext_gw(self, context, router, networks, txn):
1319
1326
maintain_bfd = router_default_route_bfd_enabled ,
1320
1327
** columns ))
1321
1328
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 )
1325
1332
return added_ports
1326
1333
1327
1334
def _check_external_ips_changed (self , ovn_snats ,
@@ -1423,17 +1430,20 @@ def _get_v4_network_for_router_port(self, context, port):
1423
1430
cidr = subnet ['cidr' ]
1424
1431
return cidr
1425
1432
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 ):
1428
1434
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 ):
1431
1436
network = self ._get_v4_network_for_router_port (context , port )
1432
1437
if network :
1433
1438
networks .append (network )
1434
-
1435
1439
return networks
1436
1440
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
+
1437
1447
def _gen_router_ext_ids (self , router ):
1438
1448
return {
1439
1449
ovn_const .OVN_ROUTER_NAME_EXT_ID_KEY :
@@ -1462,12 +1472,9 @@ def create_router(self, context, router, add_external_gateway=True):
1462
1472
# by the ovn_db_sync.py script, remove it after the database
1463
1473
# synchronization work
1464
1474
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 ):
1469
1476
added_gw_ports = self ._add_router_ext_gw (
1470
- context , router , networks , txn )
1477
+ context , router , txn )
1471
1478
1472
1479
self ._qos_driver .create_router (txn , router )
1473
1480
@@ -1495,7 +1502,6 @@ def update_router(self, context, new_router, router_object=None):
1495
1502
l3_ext_gw_multihoming .EXTERNAL_GATEWAYS )
1496
1503
1497
1504
ovn_snats = utils .get_lrouter_snats (ovn_router )
1498
- networks = self ._get_v4_network_of_all_router_ports (context , router_id )
1499
1505
try :
1500
1506
check_rev_cmd = self ._nb_idl .check_revision_number (
1501
1507
router_name , new_router , ovn_const .TYPE_ROUTERS )
@@ -1504,13 +1510,13 @@ def update_router(self, context, new_router, router_object=None):
1504
1510
if gateway_new and not gateway_old :
1505
1511
# Route gateway is set
1506
1512
added_gw_ports = self ._add_router_ext_gw (
1507
- context , new_router , networks , txn )
1513
+ context , new_router , txn )
1508
1514
elif gateway_old and not gateway_new :
1509
1515
# router gateway is removed
1510
1516
txn .add (self ._nb_idl .delete_lrouter_ext_gw (router_name ))
1511
1517
if router_object :
1512
1518
deleted_gw_port_ids = self ._delete_router_ext_gw (
1513
- router_object , networks , txn )
1519
+ router_object [ 'id' ] , txn )
1514
1520
elif gateway_new and gateway_old :
1515
1521
# Check if external gateway has changed, if yes, delete
1516
1522
# the old gateway and add the new gateway
@@ -1528,16 +1534,16 @@ def update_router(self, context, new_router, router_object=None):
1528
1534
router_name ))
1529
1535
if router_object :
1530
1536
deleted_gw_port_ids = self ._delete_router_ext_gw (
1531
- router_object , networks , txn )
1537
+ router_object [ 'id' ] , txn )
1532
1538
added_gw_ports = self ._add_router_ext_gw (
1533
- context , new_router , networks , txn )
1539
+ context , new_router , txn )
1534
1540
else :
1535
1541
# Check if snat has been enabled/disabled and update
1536
1542
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 :
1538
1544
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 )
1541
1547
1542
1548
update = {'external_ids' : self ._gen_router_ext_ids (new_router )}
1543
1549
update ['enabled' ] = new_router .get ('admin_state_up' ) or False
@@ -1785,26 +1791,26 @@ def create_router_port(self, context, router_id, router_interface):
1785
1791
1786
1792
gw_ports = self ._get_router_gw_ports (context , router_id )
1787
1793
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
-
1799
1794
if ovn_conf .is_ovn_emit_need_to_frag_enabled ():
1800
1795
for gw_port in gw_ports :
1801
1796
provider_net = self ._plugin .get_network (
1802
1797
context , gw_port ['network_id' ])
1803
1798
self .set_gateway_mtu (context , provider_net )
1804
1799
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
+
1808
1814
if ovn_conf .is_ovn_distributed_floating_ip ():
1809
1815
router_gw_ports = self ._get_router_gw_ports (context ,
1810
1816
router_id )
@@ -1937,19 +1943,17 @@ def delete_router_port(self, context, port_id, subnet_ids=None):
1937
1943
context , gw_port ['network_id' ])
1938
1944
self .set_gateway_mtu (context , provider_net , txn = txn )
1939
1945
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?
1953
1957
1954
1958
if ovn_conf .is_ovn_distributed_floating_ip ():
1955
1959
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):
1968
1972
db_rev .bump_revision (
1969
1973
context , port , ovn_const .TYPE_ROUTER_PORTS )
1970
1974
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
+
1973
1991
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
+
1985
2004
self ._transaction (commands , txn = txn )
1986
2005
1987
2006
def create_provnet_port (self , network_id , segment , txn = None ):
0 commit comments