caclmgrd: support packet mark in DHCP chain (#9191)

* caclmgrd:support packet mark in DCHP chain
This commit is contained in:
trzhang-msft 2021-11-08 14:54:57 -08:00 committed by GitHub
parent baa00e6969
commit 7e8ebaabee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 215 additions and 18 deletions

View File

@ -359,16 +359,20 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
return chain_list return chain_list
def dhcp_acl_rule(self, iptable_ns_cmd_prefix, op, intf): def dhcp_acl_rule(self, iptable_ns_cmd_prefix, op, intf, mark):
''' '''
sample: iptables --insert/delete/check DHCP -m physdev --physdev-in Ethernet4 -j DROP sample: iptables --insert/delete/check DHCP -m physdev --physdev-in Ethernet4 -j DROP
sample: iptables --insert/delete/check DHCP -m mark --mark 0x67004 -j DROP
''' '''
return iptable_ns_cmd_prefix + 'iptables --{} DHCP -m physdev --physdev-in {} -j DROP'.format(op, intf) if mark is None:
return iptable_ns_cmd_prefix + 'iptables --{} DHCP -m physdev --physdev-in {} -j DROP'.format(op, intf)
else:
return iptable_ns_cmd_prefix + 'iptables --{} DHCP -m mark --mark {} -j DROP'.format(op, mark)
def update_dhcp_chain(self, op, intf): def update_dhcp_chain(self, op, intf, mark):
for namespace in list(self.config_db_map.keys()): for namespace in list(self.config_db_map.keys()):
check_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], "check", intf) check_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], "check", intf, mark)
update_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], op, intf) update_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], op, intf, mark)
execute = 0 execute = 0
ret = subprocess.call(check_cmd, shell=True) # ret==0 indicates the rule exists ret = subprocess.call(check_cmd, shell=True) # ret==0 indicates the rule exists
@ -382,7 +386,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
subprocess.call(update_cmd, shell=True) subprocess.call(update_cmd, shell=True)
self.log_info("Update DHCP chain: {}".format(update_cmd)) self.log_info("Update DHCP chain: {}".format(update_cmd))
def update_dhcp_acl(self, key, op, data): def update_dhcp_acl(self, key, op, data, mark):
if "state" not in data: if "state" not in data:
self.log_warning("Unexpected update in MUX_CABLE_TABLE") self.log_warning("Unexpected update in MUX_CABLE_TABLE")
return return
@ -391,16 +395,33 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
state = data["state"] state = data["state"]
if state == "active": if state == "active":
self.update_dhcp_chain("delete", intf) self.update_dhcp_chain("delete", intf, mark)
elif state == "standby": elif state == "standby":
self.update_dhcp_chain("insert", intf) self.update_dhcp_chain("insert", intf, mark)
elif state == "unknown": elif state == "unknown":
self.update_dhcp_chain("delete", intf) self.update_dhcp_chain("delete", intf, mark)
elif state == "error": elif state == "error":
self.log_warning("Cable state shows error") self.log_warning("Cable state shows error")
else: else:
self.log_warning("Unexpected cable state") self.log_warning("Unexpected cable state")
def update_dhcp_acl_for_mark_change(self, key, pre_mark, cur_mark):
for namespace in list(self.config_db_map.keys()):
check_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], "check", key, pre_mark)
ret = subprocess.call(check_cmd, shell=True) # ret==0 indicates the rule exists
'''update only when the rule with pre_mark exists'''
if ret == 0:
delete_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], "delete", key, pre_mark)
insert_cmd = self.dhcp_acl_rule(self.iptables_cmd_ns_prefix[namespace], "insert", key, cur_mark)
subprocess.call(delete_cmd, shell=True)
self.log_info("Update DHCP chain: {}".format(delete_cmd))
subprocess.call(insert_cmd, shell=True)
self.log_info("Update DHCP chain: {}".format(insert_cmd))
def get_acl_rules_and_translate_to_iptables_commands(self, namespace): def get_acl_rules_and_translate_to_iptables_commands(self, namespace):
""" """
Retrieves current ACL tables and rules from Config DB, translates Retrieves current ACL tables and rules from Config DB, translates
@ -708,16 +729,22 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
# Set up STATE_DB connector to monitor the change in MUX_CABLE_TABLE # Set up STATE_DB connector to monitor the change in MUX_CABLE_TABLE
state_db_connector = None state_db_connector = None
subscribe_mux_cable = None subscribe_mux_cable = None
subscribe_dhcp_packet_mark = None
state_db_id = swsscommon.SonicDBConfig.getDbId("STATE_DB") state_db_id = swsscommon.SonicDBConfig.getDbId("STATE_DB")
dhcp_packet_mark_tbl = {}
if self.DualToR: if self.DualToR:
self.log_info("Dual ToR mode") self.log_info("Dual ToR mode")
# set up state_db connector # set up state_db connector
state_db_connector = swsscommon.DBConnector("STATE_DB", 0) state_db_connector = swsscommon.DBConnector("STATE_DB", 0)
subscribe_mux_cable = swsscommon.SubscriberStateTable(state_db_connector, self.MUX_CABLE_TABLE) subscribe_mux_cable = swsscommon.SubscriberStateTable(state_db_connector, self.MUX_CABLE_TABLE)
sel.addSelectable(subscribe_mux_cable) sel.addSelectable(subscribe_mux_cable)
subscribe_dhcp_packet_mark = swsscommon.SubscriberStateTable(state_db_connector, "DHCP_PACKET_MARK")
sel.addSelectable(subscribe_dhcp_packet_mark)
# create DHCP chain # create DHCP chain
for namespace in list(self.config_db_map.keys()): for namespace in list(self.config_db_map.keys()):
self.setup_dhcp_chain(namespace) self.setup_dhcp_chain(namespace)
@ -762,12 +789,28 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
if db_id == state_db_id: if db_id == state_db_id:
if self.DualToR: if self.DualToR:
'''dhcp packet mark update'''
while True:
key, op, fvs = subscribe_dhcp_packet_mark.pop()
if not key:
break
self.log_info("dhcp packet mark update : '%s'" % str((key, op, fvs)))
'''initial value is None'''
pre_mark = None if key not in dhcp_packet_mark_tbl else dhcp_packet_mark_tbl[key]
cur_mark = None if op == 'DEL' else dict(fvs)['mark']
dhcp_packet_mark_tbl[key] = cur_mark
self.update_dhcp_acl_for_mark_change(key, pre_mark, cur_mark)
'''mux cable update'''
while True: while True:
key, op, fvs = subscribe_mux_cable.pop() key, op, fvs = subscribe_mux_cable.pop()
if not key: if not key:
break break
self.log_info("mux cable update : '%s'" % str((key, op, fvs))) self.log_info("mux cable update : '%s'" % str((key, op, fvs)))
self.update_dhcp_acl(key, op, dict(fvs))
mark = None if key not in dhcp_packet_mark_tbl else dhcp_packet_mark_tbl[key]
self.update_dhcp_acl(key, op, dict(fvs), mark)
continue continue
ctrl_plane_acl_notification = set() ctrl_plane_acl_notification = set()

View File

@ -41,14 +41,15 @@ class TestCaclmgrdDhcp(TestCase):
popen_mock.configure_mock(**popen_attrs) popen_mock.configure_mock(**popen_attrs)
mocked_subprocess.Popen.return_value = popen_mock mocked_subprocess.Popen.return_value = popen_mock
call_mock = mock.Mock()
call_rc = test_data["call_rc"] call_rc = test_data["call_rc"]
mocked_subprocess.call.return_value = call_rc mocked_subprocess.call.return_value = call_rc
mark = test_data["mark"]
caclmgrd_daemon = caclmgrd.ControlPlaneAclManager("caclmgrd") caclmgrd_daemon = caclmgrd.ControlPlaneAclManager("caclmgrd")
mux_update = test_data["mux_update"] mux_update = test_data["mux_update"]
for key,data in mux_update: for key,data in mux_update:
caclmgrd_daemon.update_dhcp_acl(key, '', data) caclmgrd_daemon.update_dhcp_acl(key, '', data, mark)
mocked_subprocess.call.assert_has_calls(test_data["expected_subprocess_calls"], any_order=False) mocked_subprocess.call.assert_has_calls(test_data["expected_subprocess_calls"], any_order=False)

View File

@ -5,7 +5,7 @@ from unittest.mock import call
""" """
CACLMGRD_DHCP_TEST_VECTOR = [ CACLMGRD_DHCP_TEST_VECTOR = [
[ [
"Active_Present", "Active_Present_Interface",
{ {
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
@ -29,10 +29,36 @@ CACLMGRD_DHCP_TEST_VECTOR = [
'communicate.return_value': ('output', 'error'), 'communicate.return_value': ('output', 'error'),
}, },
"call_rc": 0, "call_rc": 0,
"mark": None,
}, },
], ],
[ [
"Active_Absent", "Active_Present_Mark",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
}
},
},
"mux_update": [
("Ethernet4", {"state": "active"}),
],
"expected_subprocess_calls": [
call("iptables --check DHCP -m mark --mark 0x67004 -j DROP", shell=True),
call("iptables --delete DHCP -m mark --mark 0x67004 -j DROP", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error'),
},
"call_rc": 0,
"mark": "0x67004",
},
],
[
"Active_Absent_Interface",
{ {
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
@ -54,10 +80,35 @@ CACLMGRD_DHCP_TEST_VECTOR = [
'communicate.return_value': ('output', 'error'), 'communicate.return_value': ('output', 'error'),
}, },
"call_rc": 1, "call_rc": 1,
"mark": None,
}, },
], ],
[ [
"Standby_Present", "Active_Absent_Mark",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
}
},
},
"mux_update": [
("Ethernet4", {"state": "active"}),
],
"expected_subprocess_calls": [
call("iptables --check DHCP -m mark --mark 0x67004 -j DROP", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error'),
},
"call_rc": 1,
"mark": "0x67004",
},
],
[
"Standby_Present_Interface",
{ {
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
@ -79,10 +130,35 @@ CACLMGRD_DHCP_TEST_VECTOR = [
'communicate.return_value': ('output', 'error'), 'communicate.return_value': ('output', 'error'),
}, },
"call_rc": 0, "call_rc": 0,
"mark": None,
}, },
], ],
[ [
"Standby_Absent", "Standby_Present_Mark",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
}
},
},
"mux_update": [
("Ethernet4", {"state": "standby"}),
],
"expected_subprocess_calls": [
call("iptables --check DHCP -m mark --mark 0x67004 -j DROP", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error'),
},
"call_rc": 0,
"mark": "0x67004",
},
],
[
"Standby_Absent_Interface",
{ {
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
@ -106,10 +182,36 @@ CACLMGRD_DHCP_TEST_VECTOR = [
'communicate.return_value': ('output', 'error'), 'communicate.return_value': ('output', 'error'),
}, },
"call_rc": 1, "call_rc": 1,
"mark": None,
}, },
], ],
[ [
"Unknown_Present", "Standby_Absent_Mark",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
}
},
},
"mux_update": [
("Ethernet4", {"state": "standby"}),
],
"expected_subprocess_calls": [
call("iptables --check DHCP -m mark --mark 0x67004 -j DROP", shell=True),
call("iptables --insert DHCP -m mark --mark 0x67004 -j DROP", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error'),
},
"call_rc": 1,
"mark": "0x67004",
},
],
[
"Unknown_Present_Interface",
{ {
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
@ -133,10 +235,36 @@ CACLMGRD_DHCP_TEST_VECTOR = [
'communicate.return_value': ('output', 'error'), 'communicate.return_value': ('output', 'error'),
}, },
"call_rc": 0, "call_rc": 0,
"mark": None,
}, },
], ],
[ [
"Uknown_Absent", "Unknown_Present_Mark",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
}
},
},
"mux_update": [
("Ethernet4", {"state": "unknown"}),
],
"expected_subprocess_calls": [
call("iptables --check DHCP -m mark --mark 0x67004 -j DROP", shell=True),
call("iptables --delete DHCP -m mark --mark 0x67004 -j DROP", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error'),
},
"call_rc": 0,
"mark": "0x67004",
},
],
[
"Uknown_Absent_Interface",
{ {
"config_db": { "config_db": {
"DEVICE_METADATA": { "DEVICE_METADATA": {
@ -158,6 +286,31 @@ CACLMGRD_DHCP_TEST_VECTOR = [
'communicate.return_value': ('output', 'error'), 'communicate.return_value': ('output', 'error'),
}, },
"call_rc": 1, "call_rc": 1,
"mark": None,
},
],
[
"Uknown_Absent_Mark",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"subtype": "DualToR",
"type": "ToRRouter",
}
},
},
"mux_update": [
("Ethernet4", {"state": "unknown"}),
],
"expected_subprocess_calls": [
call("iptables --check DHCP -m mark --mark 0x67004 -j DROP", shell=True),
],
"popen_attributes": {
'communicate.return_value': ('output', 'error'),
},
"call_rc": 1,
"mark": "0x67004",
}, },
], ],
] ]