Skip to content

Commit 04e49f8

Browse files
authored
Add iptables rules to drop all bgp packets destined for loopback1 IP addresses in dual ToR (#262)
Why I did it issue: https://msazure.visualstudio.com/One/_workitems/edit/32910131/ Based on a recent Incident 628608070 : [SONiC RCA][SLB_DNC] Gemini Tors dropping vip traffic, we need to block BGP from being established on loopback1. This PR addresses the requirement to block BGP packets on lookback1 for both active-active and active-standby dual tor scenarios. How I did it Added iptable rules to drop all bgp packets destined for loopback1 IP addresses. use -I to ensure its priority over all the other ACCEPT rules. How to verify it The DROP rule is at the top of the chain: Sent 1000 bgp pkts from PTF, verified that all were dropped:
1 parent 727e21b commit 04e49f8

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

scripts/caclmgrd

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,33 @@ class ControlPlaneAclManager(logger.Logger):
368368

369369
return allow_internal_docker_ip_cmds
370370

371+
def generate_block_bgp_loopback1(self, namespace, config_db_connector):
372+
373+
drop_dulator_bgp_loopback1_cmds = []
374+
if self.DualToR:
375+
loopback_table = config_db_connector.get_table(self.LOOPBACK_TABLE)
376+
loopback1_name = 'Loopback1'
377+
378+
if loopback_table:
379+
for key, _ in loopback_table.items():
380+
if not _ip_prefix_in_key(key):
381+
continue
382+
383+
iface_name, iface_cidr = key
384+
if iface_name.startswith(loopback1_name):
385+
loopback1_intf = ipaddress.ip_interface(iface_cidr)
386+
loopback1_addr = loopback1_intf.ip
387+
# Add iptables rules to drop all bgp packets destined for loopback1 IP addresses
388+
if isinstance(loopback1_addr, ipaddress.IPv4Address):
389+
drop_dulator_bgp_loopback1_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
390+
['iptables', '-I', 'INPUT', '1', '-d', str(loopback1_addr), '-p', 'tcp', '--dport', '179', '-j', 'DROP'])
391+
elif isinstance(loopback1_addr, ipaddress.IPv6Address):
392+
drop_dulator_bgp_loopback1_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
393+
['ip6tables', '-I', 'INPUT', '1', '-d', str(loopback1_addr), '-p', 'tcp', '--dport', '179', '-j', 'DROP'])
394+
else:
395+
self.log_warning("Unrecognized Loopback 1 IP {}".format(loopback1_addr))
396+
397+
return drop_dulator_bgp_loopback1_cmds
371398

372399
def generate_fwd_traffic_from_host_to_soc(self, namespace, config_db_connector):
373400

@@ -874,6 +901,7 @@ class ControlPlaneAclManager(logger.Logger):
874901

875902
if self.DualToR:
876903
dualtor_iptables_cmds = self.generate_fwd_traffic_from_host_to_soc(namespace, config_db_connector)
904+
dualtor_iptables_cmds += self.generate_block_bgp_loopback1(namespace, config_db_connector)
877905
for cmd in dualtor_iptables_cmds:
878906
self.log_info(" " + ' '.join(cmd))
879907
self.run_commands(dualtor_iptables_cmds)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import os
2+
import sys
3+
import swsscommon
4+
5+
from parameterized import parameterized
6+
from sonic_py_common.general import load_module_from_source
7+
from unittest import TestCase, mock
8+
from pyfakefs.fake_filesystem_unittest import patchfs
9+
10+
from .test_bgp_loopback1_vectors import BGP_LOOPBACK1_TEST_VECTOR
11+
from tests.common.mock_configdb import MockConfigDb
12+
from unittest.mock import MagicMock, patch
13+
14+
DBCONFIG_PATH = '/var/run/redis/sonic-db/database_config.json'
15+
16+
class TestCaclmgrdLoopback1Drop(TestCase):
17+
"""
18+
Test caclmgrd soc
19+
"""
20+
def setUp(self):
21+
swsscommon.swsscommon.ConfigDBConnector = MockConfigDb
22+
test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
23+
modules_path = os.path.dirname(test_path)
24+
scripts_path = os.path.join(modules_path, "scripts")
25+
sys.path.insert(0, modules_path)
26+
caclmgrd_path = os.path.join(scripts_path, 'caclmgrd')
27+
self.caclmgrd = load_module_from_source('caclmgrd', caclmgrd_path)
28+
29+
@parameterized.expand(BGP_LOOPBACK1_TEST_VECTOR)
30+
@patchfs
31+
def test_caclmgrd_soc(self, test_name, test_data, fs):
32+
if not os.path.exists(DBCONFIG_PATH):
33+
fs.create_file(DBCONFIG_PATH) # fake database_config.json
34+
35+
MockConfigDb.set_config_db(test_data["config_db"])
36+
37+
with mock.patch("caclmgrd.ControlPlaneAclManager.run_commands_pipe", return_value='sonic'):
38+
with mock.patch("caclmgrd.subprocess") as mocked_subprocess:
39+
popen_mock = mock.Mock()
40+
popen_attrs = test_data["popen_attributes"]
41+
popen_mock.configure_mock(**popen_attrs)
42+
mocked_subprocess.Popen.return_value = popen_mock
43+
mocked_subprocess.PIPE = -1
44+
45+
call_rc = test_data["call_rc"]
46+
mocked_subprocess.call.return_value = call_rc
47+
48+
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
49+
caclmgrd_daemon.update_control_plane_nat_acls('', {}, MockConfigDb())
50+
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from unittest.mock import call
2+
import subprocess
3+
4+
"""
5+
caclmgrd soc test vector
6+
"""
7+
BGP_LOOPBACK1_TEST_VECTOR = [
8+
[
9+
"BGP_LOOPBACK1_SESSION_TEST",
10+
{
11+
"config_db": {
12+
"DEVICE_METADATA": {
13+
"localhost": {
14+
"subtype": "DualToR",
15+
"type": "ToRRouter",
16+
}
17+
},
18+
"MUX_CABLE": {
19+
"Ethernet4": {
20+
"cable_type": "active-active",
21+
"soc_ipv4": "10.10.10.7/32",
22+
}
23+
},
24+
"VLAN_INTERFACE": {
25+
"Vlan1000|10.10.10.3/24": {
26+
"NULL": "NULL",
27+
}
28+
},
29+
"LOOPBACK_INTERFACE": {
30+
"Loopback1|10.1.0.10/32": {},
31+
"Loopback1|10.1.0.12/32": {},
32+
"Loopback1|FC00:1:0:10::/128": {},
33+
"Loopback1|FC00:1:0:34::/128": {}
34+
},
35+
"FEATURE": {
36+
},
37+
},
38+
"expected_subprocess_calls": [
39+
call(['iptables', '-I', 'INPUT', '1', '-d', "10.1.0.10", '-p', 'tcp', '--dport', '179', '-j', 'DROP'], universal_newlines=True, stdout=-1),
40+
call(['iptables', '-I', 'INPUT', '1', '-d', "10.1.0.12", '-p', 'tcp', '--dport', '179', '-j', 'DROP'], universal_newlines=True, stdout=-1),
41+
call(['ip6tables', '-I', 'INPUT', '1', '-d', "fc00:1:0:10::", '-p', 'tcp', '--dport', '179', '-j', 'DROP'], universal_newlines=True, stdout=-1),
42+
call(['ip6tables', '-I', 'INPUT', '1', '-d', "fc00:1:0:34::", '-p', 'tcp', '--dport', '179', '-j', 'DROP'], universal_newlines=True, stdout=-1)
43+
],
44+
"popen_attributes": {
45+
'communicate.return_value': ('output', 'error'),
46+
},
47+
"call_rc": 0,
48+
}
49+
]
50+
]

0 commit comments

Comments
 (0)