[caclmgrd][dualtor] add iptables rule for dualtor gRPC to allow packets getting forwarded from loopback IP (#11458)
This PR is a required for changing the L3 IP forwarding Behavior to SoC in active-active toplogy. Basically, for getting a packet to be forwarded to the SoC IP in active-active topology, the requirement is to use the the LoopBack 3 IP inside SONiC device as the SRC IP. This is required because in active-active topology by default if the ToR wants to send packet to the SoC, it would pick the Vlan IP since that's the IP in the subnet, but since there are firewalls inside the SoC , the IP packets with Vlan IP as src IP in the IP header will be dropped. Hence to overcome this limitation, there is an iptable nat rule that is installed inside the kernel, with which all the packets which have SoC IP as destination IP, use Loopnack 3 IP as src in IP header How I did it check the config DB if the ToR is a DualToR and has an SoC IP assigned. put an iptable rule iptables -t nat -A POSTROUTING --destination -j SNAT --to-source " Signed-off-by: vaibhav-dahiya vdahiya@microsoft.com
This commit is contained in:
parent
3e9c1d16c1
commit
3829faf2c9
@ -41,6 +41,22 @@ def _ip_prefix_in_key(key):
|
||||
"""
|
||||
return (isinstance(key, tuple))
|
||||
|
||||
def get_ip_from_interface_table(table, intf_name):
|
||||
|
||||
if table:
|
||||
for key, _ in table.items():
|
||||
if not _ip_prefix_in_key(key):
|
||||
continue
|
||||
|
||||
iface_name, iface_cidr = key
|
||||
if iface_name.startswith(intf_name):
|
||||
ip_ntwrk = ipaddress.ip_network(iface_cidr, strict=False)
|
||||
if isinstance(ip_ntwrk, ipaddress.IPv4Network):
|
||||
ip_addr = ip_ntwrk.network_address
|
||||
return ip_addr
|
||||
|
||||
return None
|
||||
|
||||
# ============================== Classes ==============================
|
||||
|
||||
|
||||
@ -57,6 +73,8 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
ACL_RULE = "ACL_RULE"
|
||||
DEVICE_METADATA_TABLE = "DEVICE_METADATA"
|
||||
MUX_CABLE_TABLE = "MUX_CABLE_TABLE"
|
||||
CONFIG_MUX_CABLE = "MUX_CABLE"
|
||||
LOOPBACK_TABLE = "LOOPBACK_INTERFACE"
|
||||
|
||||
ACL_TABLE_TYPE_CTRLPLANE = "CTRLPLANE"
|
||||
|
||||
@ -284,6 +302,28 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
|
||||
return allow_internal_docker_ip_cmds
|
||||
|
||||
|
||||
def generate_fwd_traffic_from_host_to_soc(self, namespace):
|
||||
|
||||
fwd_dualtor_grpc_traffic_from_host_to_soc_cmds = []
|
||||
if self.DualToR:
|
||||
loopback_table = self.config_db_map[DEFAULT_NAMESPACE].get_table(self.LOOPBACK_TABLE)
|
||||
loopback_name = 'Loopback3'
|
||||
loopback_address = get_ip_from_interface_table(loopback_table, loopback_name)
|
||||
fwd_dualtor_grpc_traffic_from_host_to_soc_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
||||
"iptables -t nat --flush POSTROUTING")
|
||||
|
||||
if loopback_address is not None:
|
||||
mux_table = self.config_db_map[DEFAULT_NAMESPACE].get_table(self.CONFIG_MUX_CABLE)
|
||||
mux_table_keys = mux_table.keys()
|
||||
for key in mux_table_keys:
|
||||
kvp = mux_table.get(key)
|
||||
if 'cable_type' in kvp and kvp['cable_type'] == 'active-active':
|
||||
fwd_dualtor_grpc_traffic_from_host_to_soc_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
|
||||
"iptables -t nat -A POSTROUTING --destination {} -j SNAT --to-source {}".format(kvp['soc_ipv4'], loopback_address))
|
||||
|
||||
return fwd_dualtor_grpc_traffic_from_host_to_soc_cmds
|
||||
|
||||
def generate_fwd_traffic_from_namespace_to_host_commands(self, namespace, acl_source_ip_map):
|
||||
"""
|
||||
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward request coming
|
||||
@ -685,6 +725,13 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
|
||||
|
||||
self.run_commands(iptables_cmds)
|
||||
|
||||
if self.DualToR:
|
||||
dualtor_iptables_cmds = self.generate_fwd_traffic_from_host_to_soc(namespace)
|
||||
for cmd in dualtor_iptables_cmds:
|
||||
self.log_info(" " + cmd)
|
||||
self.run_commands(dualtor_iptables_cmds)
|
||||
|
||||
|
||||
def check_and_update_control_plane_acls(self, namespace, num_changes):
|
||||
"""
|
||||
This function is intended to be spawned in a separate thread.
|
||||
|
@ -0,0 +1,51 @@
|
||||
import os
|
||||
import sys
|
||||
import swsscommon
|
||||
|
||||
from parameterized import parameterized
|
||||
from sonic_py_common.general import load_module_from_source
|
||||
from unittest import TestCase, mock
|
||||
from pyfakefs.fake_filesystem_unittest import patchfs
|
||||
|
||||
from .test_soc_rules_vectors import CACLMGRD_SOC_TEST_VECTOR
|
||||
from tests.common.mock_configdb import MockConfigDb
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
DBCONFIG_PATH = '/var/run/redis/sonic-db/database_config.json'
|
||||
|
||||
class TestCaclmgrdSoc(TestCase):
|
||||
"""
|
||||
Test caclmgrd soc
|
||||
"""
|
||||
def setUp(self):
|
||||
swsscommon.swsscommon.ConfigDBConnector = MockConfigDb
|
||||
test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
modules_path = os.path.dirname(test_path)
|
||||
scripts_path = os.path.join(modules_path, "scripts")
|
||||
sys.path.insert(0, modules_path)
|
||||
caclmgrd_path = os.path.join(scripts_path, 'caclmgrd')
|
||||
self.caclmgrd = load_module_from_source('caclmgrd', caclmgrd_path)
|
||||
|
||||
@parameterized.expand(CACLMGRD_SOC_TEST_VECTOR)
|
||||
@patchfs
|
||||
@patch('caclmgrd.get_ip_from_interface_table', MagicMock(return_value="10.10.10.10"))
|
||||
def test_caclmgrd_soc(self, test_name, test_data, fs):
|
||||
if not os.path.exists(DBCONFIG_PATH):
|
||||
fs.create_file(DBCONFIG_PATH) # fake database_config.json
|
||||
|
||||
MockConfigDb.set_config_db(test_data["config_db"])
|
||||
|
||||
with mock.patch("caclmgrd.subprocess") as mocked_subprocess:
|
||||
popen_mock = mock.Mock()
|
||||
popen_attrs = test_data["popen_attributes"]
|
||||
popen_mock.configure_mock(**popen_attrs)
|
||||
mocked_subprocess.Popen.return_value = popen_mock
|
||||
mocked_subprocess.PIPE = -1
|
||||
|
||||
call_rc = test_data["call_rc"]
|
||||
mocked_subprocess.call.return_value = call_rc
|
||||
|
||||
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
|
||||
caclmgrd_daemon.update_control_plane_nat_acls('', {})
|
||||
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
|
||||
|
@ -0,0 +1,41 @@
|
||||
from unittest.mock import call
|
||||
import subprocess
|
||||
|
||||
"""
|
||||
caclmgrd soc test vector
|
||||
"""
|
||||
CACLMGRD_SOC_TEST_VECTOR = [
|
||||
[
|
||||
"SOC_SESSION_TEST",
|
||||
{
|
||||
"config_db": {
|
||||
"DEVICE_METADATA": {
|
||||
"localhost": {
|
||||
"subtype": "DualToR",
|
||||
"type": "ToRRouter",
|
||||
}
|
||||
},
|
||||
"MUX_CABLE": {
|
||||
"Ethernet4": {
|
||||
"cable_type": "active-active",
|
||||
"soc_ipv4": "192.168.1.0/32",
|
||||
}
|
||||
},
|
||||
"LOOPBACK_INTERFACE": {
|
||||
"Loopback3|10.10.10.10/32": {
|
||||
"NULL": "NULL",
|
||||
}
|
||||
},
|
||||
"FEATURE": {
|
||||
},
|
||||
},
|
||||
"expected_subprocess_calls": [
|
||||
call("iptables -t nat -A POSTROUTING --destination 192.168.1.0/32 -j SNAT --to-source 10.10.10.10",shell=True, universal_newlines=True, stdout=-1)
|
||||
],
|
||||
"popen_attributes": {
|
||||
'communicate.return_value': ('output', 'error'),
|
||||
},
|
||||
"call_rc": 0,
|
||||
}
|
||||
]
|
||||
]
|
Reference in New Issue
Block a user