Skip to content

Commit dfe4521

Browse files
authored
[DualToR][caclmgrd] Fix IPtables rules for multiple vlan interfaces for DualToR config (#17093)
* [DualToR][caclmgrd] Fix IPtables rules for multiple vlan interfaces for DualToR config Signed-off-by: vaibhav-dahiya <[email protected]>
1 parent 213c18e commit dfe4521

File tree

3 files changed

+130
-22
lines changed

3 files changed

+130
-22
lines changed

src/sonic-host-services/scripts/caclmgrd

+31-12
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ def _ip_prefix_in_key(key):
4141
"""
4242
return (isinstance(key, tuple))
4343

44-
def get_ip_from_interface_table(table, intf_name):
45-
44+
def get_ipv4_networks_from_interface_table(table, intf_name):
45+
46+
addresses = []
4647
if table:
4748
for key, _ in table.items():
4849
if not _ip_prefix_in_key(key):
@@ -51,12 +52,11 @@ def get_ip_from_interface_table(table, intf_name):
5152

5253
iface_name, iface_cidr = key
5354
if iface_name.startswith(intf_name):
54-
ip_str = iface_cidr.split("/")[0]
55-
ip_addr = ipaddress.ip_address(ip_str)
56-
if isinstance(ip_addr, ipaddress.IPv4Address):
57-
return ip_addr
55+
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
56+
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
57+
addresses.append(ip_ntwrk)
5858

59-
return None
59+
return addresses
6060

6161
# ============================== Classes ==============================
6262

@@ -342,22 +342,41 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
342342
if self.DualToR:
343343
loopback_table = config_db_connector.get_table(self.LOOPBACK_TABLE)
344344
loopback_name = 'Loopback3'
345-
loopback_address = get_ip_from_interface_table(loopback_table, loopback_name)
345+
loopback_networks = get_ipv4_networks_from_interface_table(loopback_table, loopback_name)
346+
if len(loopback_networks) == 0:
347+
self.log_warning("Loopback 3 IP not available from DualToR active-active config")
348+
return fwd_dualtor_grpc_traffic_from_host_to_soc_cmds
349+
350+
if not isinstance(loopback_networks[0], ipaddress.IPv4Network):
351+
self.log_warning("Loopback 3 IP Network not available from DualToR active-active config")
352+
return fwd_dualtor_grpc_traffic_from_host_to_soc_cmds
353+
354+
loopback_address = loopback_networks[0].network_address
346355
vlan_name = 'Vlan'
347356
vlan_table = config_db_connector.get_table(self.VLAN_INTF_TABLE)
357+
vlan_networks = get_ipv4_networks_from_interface_table(vlan_table, vlan_name)
358+
359+
if len(vlan_networks) == 0:
360+
self.log_warning("Vlan IP not available from DualToR active-active config")
361+
return fwd_dualtor_grpc_traffic_from_host_to_soc_cmds
348362

349-
vlan_address = get_ip_from_interface_table(vlan_table, vlan_name)
350363
fwd_dualtor_grpc_traffic_from_host_to_soc_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
351364
"iptables -t nat --flush POSTROUTING")
352-
365+
353366
if loopback_address is not None:
354367
mux_table = config_db_connector.get_table(self.CONFIG_MUX_CABLE)
355368
mux_table_keys = mux_table.keys()
356369
for key in mux_table_keys:
357370
kvp = mux_table.get(key)
358371
if 'cable_type' in kvp and kvp['cable_type'] == 'active-active':
359-
fwd_dualtor_grpc_traffic_from_host_to_soc_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
360-
"iptables -t nat -A POSTROUTING --destination {} --source {} -j SNAT --to-source {}".format(kvp['soc_ipv4'], vlan_address, loopback_address))
372+
soc_ipv4_str = kvp['soc_ipv4'].split("/")[0]
373+
soc_ipv4_addr = ipaddress.ip_address(soc_ipv4_str)
374+
for ip_network in vlan_networks:
375+
# Only add the vlan source IP specific soc IP address to IPtables
376+
if soc_ipv4_addr in ip_network:
377+
vlan_address = ip_network.network_address
378+
fwd_dualtor_grpc_traffic_from_host_to_soc_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
379+
"iptables -t nat -A POSTROUTING --destination {} --source {} -j SNAT --to-source {}".format(str(soc_ipv4_addr), str(vlan_address), str(loopback_address)))
361380

362381
return fwd_dualtor_grpc_traffic_from_host_to_soc_cmds
363382

src/sonic-host-services/tests/caclmgrd/caclmgrd_soc_rules_test.py

+55-6
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
from parameterized import parameterized
66
from sonic_py_common.general import load_module_from_source
7-
from ipaddress import IPv4Address
7+
from ipaddress import IPv4Address, IPv4Network
88
from unittest import TestCase, mock
99
from pyfakefs.fake_filesystem_unittest import patchfs
1010

11-
from .test_soc_rules_vectors import CACLMGRD_SOC_TEST_VECTOR
11+
from .test_soc_rules_vectors import CACLMGRD_SOC_TEST_VECTOR, CACLMGRD_SOC_TEST_VECTOR_EMPTY
1212
from tests.common.mock_configdb import MockConfigDb
1313
from unittest.mock import MagicMock, patch
1414

@@ -29,7 +29,7 @@ def setUp(self):
2929

3030
@parameterized.expand(CACLMGRD_SOC_TEST_VECTOR)
3131
@patchfs
32-
@patch('caclmgrd.get_ip_from_interface_table', MagicMock(return_value="10.10.10.10"))
32+
@patch('caclmgrd.get_ipv4_networks_from_interface_table', MagicMock(return_value=[IPv4Network('10.10.10.18/24', strict=False), IPv4Network('10.10.11.18/24', strict=False)]))
3333
def test_caclmgrd_soc(self, test_name, test_data, fs):
3434
if not os.path.exists(DBCONFIG_PATH):
3535
fs.create_file(DBCONFIG_PATH) # fake database_config.json
@@ -50,11 +50,60 @@ def test_caclmgrd_soc(self, test_name, test_data, fs):
5050
caclmgrd_daemon.update_control_plane_nat_acls('', {}, MockConfigDb())
5151
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
5252

53-
def test_get_ip_from_interface_table(self):
53+
54+
@parameterized.expand(CACLMGRD_SOC_TEST_VECTOR_EMPTY)
55+
@patchfs
56+
@patch('caclmgrd.get_ipv4_networks_from_interface_table', MagicMock(return_value=[]))
57+
def test_caclmgrd_soc_no_ips(self, test_name, test_data, fs):
58+
if not os.path.exists(DBCONFIG_PATH):
59+
fs.create_file(DBCONFIG_PATH) # fake database_config.json
60+
61+
MockConfigDb.set_config_db(test_data["config_db"])
62+
63+
with mock.patch("caclmgrd.subprocess") as mocked_subprocess:
64+
popen_mock = mock.Mock()
65+
popen_attrs = test_data["popen_attributes"]
66+
popen_mock.configure_mock(**popen_attrs)
67+
mocked_subprocess.Popen.return_value = popen_mock
68+
mocked_subprocess.PIPE = -1
69+
70+
call_rc = test_data["call_rc"]
71+
mocked_subprocess.call.return_value = call_rc
72+
73+
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
74+
caclmgrd_daemon.update_control_plane_nat_acls('', {}, MockConfigDb())
75+
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
76+
77+
78+
@parameterized.expand(CACLMGRD_SOC_TEST_VECTOR_EMPTY)
79+
@patchfs
80+
@patch('caclmgrd.get_ipv4_networks_from_interface_table', MagicMock(return_value=['10.10.10.10']))
81+
def test_caclmgrd_soc_ip_string(self, test_name, test_data, fs):
82+
if not os.path.exists(DBCONFIG_PATH):
83+
fs.create_file(DBCONFIG_PATH) # fake database_config.json
84+
85+
MockConfigDb.set_config_db(test_data["config_db"])
86+
87+
with mock.patch("caclmgrd.subprocess") as mocked_subprocess:
88+
popen_mock = mock.Mock()
89+
popen_attrs = test_data["popen_attributes"]
90+
popen_mock.configure_mock(**popen_attrs)
91+
mocked_subprocess.Popen.return_value = popen_mock
92+
mocked_subprocess.PIPE = -1
93+
94+
call_rc = test_data["call_rc"]
95+
mocked_subprocess.call.return_value = call_rc
96+
97+
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
98+
caclmgrd_daemon.update_control_plane_nat_acls('', {}, MockConfigDb())
99+
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
100+
101+
102+
def test_get_ipv4_networks_from_interface_table(self):
54103
if not os.path.exists(DBCONFIG_PATH):
55104
fs.create_file(DBCONFIG_PATH) # fake database_config.json
56105

57106
table = {("Vlan1000","10.10.10.1/32"): "val"}
58-
ip_addr = self.caclmgrd.get_ip_from_interface_table(table, "Vlan")
107+
ip_addr = self.caclmgrd.get_ipv4_networks_from_interface_table(table, "Vlan")
59108

60-
assert (ip_addr == IPv4Address('10.10.10.1'))
109+
assert (ip_addr == [IPv4Network('10.10.10.1/32')])

src/sonic-host-services/tests/caclmgrd/test_soc_rules_vectors.py

+44-4
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
"MUX_CABLE": {
1919
"Ethernet4": {
2020
"cable_type": "active-active",
21-
"soc_ipv4": "192.168.1.0/32",
21+
"soc_ipv4": "10.10.11.7/32",
2222
}
2323
},
2424
"VLAN_INTERFACE": {
25-
"Vlan1000|10.10.2.2/23": {
25+
"Vlan1000|10.10.10.3/24": {
2626
"NULL": "NULL",
2727
}
2828
},
@@ -35,8 +35,48 @@
3535
},
3636
},
3737
"expected_subprocess_calls": [
38-
call("iptables -t nat -A POSTROUTING --destination 192.168.1.0/32 --source 10.10.10.10 -j SNAT --to-source 10.10.10.10",shell=True, universal_newlines=True, stdout=-1)
39-
],
38+
call('iptables -t nat -A POSTROUTING --destination 10.10.11.7 --source 10.10.11.0 -j SNAT --to-source 10.10.10.0', shell=True, universal_newlines=True, stdout=-1)
39+
],
40+
"popen_attributes": {
41+
'communicate.return_value': ('output', 'error'),
42+
},
43+
"call_rc": 0,
44+
}
45+
]
46+
]
47+
48+
49+
CACLMGRD_SOC_TEST_VECTOR_EMPTY = [
50+
[
51+
"SOC_SESSION_TEST",
52+
{
53+
"config_db": {
54+
"DEVICE_METADATA": {
55+
"localhost": {
56+
"subtype": "DualToR",
57+
"type": "ToRRouter",
58+
}
59+
},
60+
"MUX_CABLE": {
61+
"Ethernet4": {
62+
"cable_type": "active-active",
63+
"soc_ipv4": "10.10.11.7/32",
64+
}
65+
},
66+
"VLAN_INTERFACE": {
67+
"Vlan1000|10.10.10.3/24": {
68+
"NULL": "NULL",
69+
}
70+
},
71+
"LOOPBACK_INTERFACE": {
72+
"Loopback3|10.10.10.10/32": {
73+
"NULL": "NULL",
74+
}
75+
},
76+
"FEATURE": {
77+
},
78+
},
79+
"expected_subprocess_calls": [],
4080
"popen_attributes": {
4181
'communicate.return_value': ('output', 'error'),
4282
},

0 commit comments

Comments
 (0)