Skip to content

Commit e21db16

Browse files
authored
[BFD]Fix BFD blackout issue (#150)
Fixes sonic-net#19762 When caclmgrd processes any ACL table change, this results in rules flushed and readded which was introduced in #114 . However in this flow the BFD and vxlan rules were added at the end which may result in traffic loss because of the DROP rule for ip2me installed. To overcome this added BFD and vxlan rules at the very start so that there is no drop seen for BFD. Existing UT should verify the flows. Additionally manually verified
1 parent 39834f2 commit e21db16

3 files changed

Lines changed: 55 additions & 15 deletions

File tree

scripts/caclmgrd

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,14 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
586586
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + ['iptables', '-A', 'INPUT', '-s', '127.0.0.1', '-i', 'lo', '-j', 'ACCEPT'])
587587
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + ['ip6tables', '-A', 'INPUT', '-s', '::1', '-i', 'lo', '-j', 'ACCEPT'])
588588

589+
590+
if self.bfdAllowed:
591+
iptables_cmds += self.get_bfd_iptable_commands(namespace)
592+
593+
if self.VxlanAllowed:
594+
fvs = swsscommon.FieldValuePairs([("src_ip", self.VxlanSrcIP)])
595+
iptables_cmds += self.get_vxlan_port_iptable_commands(namespace, fvs)
596+
589597
# Add iptables commands to allow internal docker traffic
590598
iptables_cmds += self.generate_allow_internal_docker_ip_traffic_commands(namespace)
591599

@@ -813,12 +821,6 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
813821
self.log_info(" " + ' '.join(cmd))
814822

815823
self.run_commands(iptables_cmds)
816-
if self.bfdAllowed:
817-
self.allow_bfd_protocol(namespace)
818-
if self.VxlanAllowed:
819-
fvs = swsscommon.FieldValuePairs([("src_ip", self.VxlanSrcIP)])
820-
self.allow_vxlan_port(namespace, fvs)
821-
822824

823825
self.update_control_plane_nat_acls(namespace, service_to_source_ip_map, config_db_connector)
824826

@@ -886,24 +888,29 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
886888
finally:
887889
new_config_db_connector.close("CONFIG_DB")
888890

889-
def allow_bfd_protocol(self, namespace):
891+
def get_bfd_iptable_commands(self, namespace):
890892
iptables_cmds = []
891893
# Add iptables/ip6tables commands to allow all BFD singlehop and multihop sessions
892894
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + ['iptables', '-I', 'INPUT', '2', '-p', 'udp', '-m', 'multiport', '--dports', '3784,4784', '-j', 'ACCEPT'])
893895
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] + ['ip6tables', '-I', 'INPUT', '2', '-p', 'udp', '-m', 'multiport', '--dports', '3784,4784', '-j', 'ACCEPT'])
894-
self.run_commands(iptables_cmds)
896+
return iptables_cmds
895897

896-
def allow_vxlan_port(self, namespace, data):
898+
def allow_bfd_protocol(self, namespace):
899+
iptables_cmds = self.get_bfd_iptable_commands(namespace)
900+
if iptables_cmds:
901+
self.run_commands(iptables_cmds)
902+
903+
904+
def get_vxlan_port_iptable_commands(self, namespace, data):
905+
iptables_cmds = []
897906
for fv in data:
898907
if (fv[0] == "src_ip"):
899908
self.VxlanSrcIP = fv[1]
900909
break
901910

902911
if not self.VxlanSrcIP:
903912
self.log_info("Received vxlan tunnel configuration without source ip")
904-
return False
905-
906-
iptables_cmds = []
913+
return iptables_cmds
907914

908915
# Add iptables/ip6tables commands to allow VxLAN packets
909916
ip_addr = ipaddress.ip_address(self.VxlanSrcIP)
@@ -914,10 +921,15 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
914921
iptables_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
915922
['iptables', '-I', 'INPUT', '2', '-p', 'udp', '-d', self.VxlanSrcIP, '--dport', '4789', '-j', 'ACCEPT'])
916923

924+
return iptables_cmds
925+
926+
def allow_vxlan_port(self, namespace, data):
927+
iptables_cmds = self.get_vxlan_port_iptable_commands(namespace, data)
928+
if not iptables_cmds:
929+
return False
917930
self.run_commands(iptables_cmds)
918931
self.log_info("Enabled vxlan port for source ip " + self.VxlanSrcIP)
919932
self.VxlanAllowed = True
920-
return True
921933

922934
def block_vxlan_port(self, namespace):
923935
if not self.VxlanSrcIP:

tests/caclmgrd/caclmgrd_bfd_test.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,28 @@ def test_caclmgrd_bfd(self, test_name, test_data, fs):
4747

4848
caclmgrd_daemon = self.caclmgrd.ControlPlaneAclManager("caclmgrd")
4949
caclmgrd_daemon.allow_bfd_protocol('')
50-
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
50+
mocked_subprocess.Popen.assert_has_calls(test_data["expected_bfd_subprocess_calls"], any_order=True)
5151
caclmgrd_daemon.bfdAllowed = True
5252
mocked_subprocess.Popen.reset_mock()
5353
caclmgrd_daemon.num_changes[''] = 1
5454
caclmgrd_daemon.check_and_update_control_plane_acls('', 1)
55-
mocked_subprocess.Popen.assert_has_calls(test_data["expected_subprocess_calls"], any_order=True)
55+
56+
#Ensure BFD rules are installed before ip2me rules to avoid traffic loss during update of control plane acl rules
57+
bfd_ipv4_idx = 0
58+
bfd_ipv6_idx = 0
59+
ip2me_ipv4_idx = 0
60+
ip2me_ipv6_idx = 0
61+
idx = 0
62+
for call in mocked_subprocess.Popen.call_args_list:
63+
if test_data["expected_subprocess_calls"][0] == call:
64+
bfd_ipv4_idx = idx
65+
elif test_data["expected_subprocess_calls"][1] == call:
66+
bfd_ipv6_idx = idx
67+
elif test_data["expected_subprocess_calls"][2] == call:
68+
ip2me_ipv4_idx = idx
69+
elif test_data["expected_subprocess_calls"][3] == call:
70+
ip2me_ipv6_idx = idx
71+
idx = idx+1
72+
assert (bfd_ipv4_idx != 0 and bfd_ipv6_idx != 0 and ip2me_ipv4_idx !=0 and ip2me_ipv6_idx != 0)
73+
assert (bfd_ipv4_idx < ip2me_ipv4_idx and bfd_ipv6_idx < ip2me_ipv6_idx)
5674

tests/caclmgrd/test_bfd_vectors.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,18 @@
2121
"state": "enabled",
2222
}
2323
},
24+
"LOOPBACK_INTERFACE": {
25+
"Loopback0|2.2.2.1/32": {},
26+
"Loopback0|2001:db8:10::/64": {}
27+
},
2428
},
2529
"expected_subprocess_calls": [
30+
call(['iptables', '-I', 'INPUT', '2', '-p', 'udp', '-m', 'multiport', '--dports', '3784,4784', '-j', 'ACCEPT'], universal_newlines=True, stdout=subprocess.PIPE),
31+
call(['ip6tables', '-I', 'INPUT', '2', '-p', 'udp', '-m', 'multiport', '--dports', '3784,4784', '-j', 'ACCEPT'], universal_newlines=True, stdout=subprocess.PIPE),
32+
call(['iptables', '-A', 'INPUT', '-d', '2.2.2.1/32', '-j', 'DROP'],universal_newlines=True, stdout=subprocess.PIPE),
33+
call(['ip6tables', '-A', 'INPUT', '-d', '2001:db8:10::/128', '-j', 'DROP'],universal_newlines=True, stdout=subprocess.PIPE)
34+
],
35+
"expected_bfd_subprocess_calls": [
2636
call(['iptables', '-I', 'INPUT', '2', '-p', 'udp', '-m', 'multiport', '--dports', '3784,4784', '-j', 'ACCEPT'], universal_newlines=True, stdout=subprocess.PIPE),
2737
call(['ip6tables', '-I', 'INPUT', '2', '-p', 'udp', '-m', 'multiport', '--dports', '3784,4784', '-j', 'ACCEPT'], universal_newlines=True, stdout=subprocess.PIPE)
2838
],

0 commit comments

Comments
 (0)