[Multi-Asic] Forward SNMP requests received on front panel interface to SNMP agent in host. (#5420)

* [Multi-Asic] Forward SNMP requests destined to loopback IP, and coming in through the front panel interface
             present in the network namespace, to SNMP agent running in the linux host.

* Updates based on comments

* Further updates in docker_image_ctl.j2 and caclmgrd

* Change the variable for net config file.

* Updated the comments in the code.

* No need to clean up the exising NAT rules if present, which could be created by some other process.

* Delete our rule first and add it back, to take care of caclmgrd restart.
Another benefit is that we delete only our rules, rather than earlier approach of "iptables -F" which cleans up all rules.

* Keeping the original logic to clean the NAT entries, to revist when NAT feature added in namespace.

* Missing updates to log_info call.
This commit is contained in:
judyjoseph 2020-09-26 12:14:30 -07:00 committed by Abhishek Dosi
parent b70c6f72b2
commit cff716f7a5
2 changed files with 62 additions and 0 deletions

View File

@ -91,6 +91,10 @@ function postStartAction()
{ {
{%- if docker_container_name == "database" %} {%- if docker_container_name == "database" %}
if [ "$DEV" ]; then if [ "$DEV" ]; then
# Enable the forwarding on eth0 interface in namespace.
SYSCTL_NET_CONFIG="/etc/sysctl.d/sysctl-net.conf"
docker exec -i database$DEV sed -i -e "s/^net.ipv4.conf.eth0.forwarding=0/net.ipv4.conf.eth0.forwarding=1/;
s/^net.ipv6.conf.eth0.forwarding=0/net.ipv6.conf.eth0.forwarding=1/" $SYSCTL_NET_CONFIG
docker exec -i database$DEV sysctl --system -e docker exec -i database$DEV sysctl --system -e
link_namespace $DEV link_namespace $DEV
fi fi

View File

@ -81,7 +81,9 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
self.config_db_map[''].connect() self.config_db_map[''].connect()
self.iptables_cmd_ns_prefix[''] = "" self.iptables_cmd_ns_prefix[''] = ""
self.namespace_mgmt_ip = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[''], '') self.namespace_mgmt_ip = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[''], '')
self.namespace_mgmt_ipv6 = self.get_namespace_mgmt_ipv6(self.iptables_cmd_ns_prefix[''], '')
self.namespace_docker_mgmt_ip = {} self.namespace_docker_mgmt_ip = {}
self.namespace_docker_mgmt_ipv6 = {}
namespaces = device_info.get_all_namespaces() namespaces = device_info.get_all_namespaces()
for front_asic_namespace in namespaces['front_ns']: for front_asic_namespace in namespaces['front_ns']:
self.config_db_map[front_asic_namespace] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespace) self.config_db_map[front_asic_namespace] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespace)
@ -89,11 +91,15 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
self.iptables_cmd_ns_prefix[front_asic_namespace] = "ip netns exec " + front_asic_namespace + " " self.iptables_cmd_ns_prefix[front_asic_namespace] = "ip netns exec " + front_asic_namespace + " "
self.namespace_docker_mgmt_ip[front_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[front_asic_namespace], self.namespace_docker_mgmt_ip[front_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[front_asic_namespace],
front_asic_namespace) front_asic_namespace)
self.namespace_docker_mgmt_ipv6[front_asic_namespace] = self.get_namespace_mgmt_ipv6(self.iptables_cmd_ns_prefix[front_asic_namespace],
front_asic_namespace)
for back_asic_namespace in namespaces['back_ns']: for back_asic_namespace in namespaces['back_ns']:
self.iptables_cmd_ns_prefix[back_asic_namespace] = "ip netns exec " + back_asic_namespace + " " self.iptables_cmd_ns_prefix[back_asic_namespace] = "ip netns exec " + back_asic_namespace + " "
self.namespace_docker_mgmt_ip[back_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[back_asic_namespace], self.namespace_docker_mgmt_ip[back_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[back_asic_namespace],
back_asic_namespace) back_asic_namespace)
self.namespace_docker_mgmt_ipv6[back_asic_namespace] = self.get_namespace_mgmt_ipv6(self.iptables_cmd_ns_prefix[back_asic_namespace],
back_asic_namespace)
def get_namespace_mgmt_ip(self, iptable_ns_cmd_prefix, namespace): def get_namespace_mgmt_ip(self, iptable_ns_cmd_prefix, namespace):
ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\ ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\
@ -101,6 +107,11 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
return self.run_commands([ip_address_get_command]) return self.run_commands([ip_address_get_command])
def get_namespace_mgmt_ipv6(self, iptable_ns_cmd_prefix, namespace):
ipv6_address_get_command = iptable_ns_cmd_prefix + "ip -6 -o addr show scope global " + ("eth0" if namespace else "docker0") +\
" | awk '{print $4}' | cut -d'/' -f1 | head -1"
return self.run_commands([ipv6_address_get_command])
def run_commands(self, commands): def run_commands(self, commands):
""" """
Given a list of shell commands, run them in order Given a list of shell commands, run them in order
@ -202,6 +213,39 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
(docker_mgmt_ip, self.namespace_mgmt_ip)) (docker_mgmt_ip, self.namespace_mgmt_ip))
return allow_internal_docker_ip_cmds return allow_internal_docker_ip_cmds
def generate_fwd_snmp_traffic_from_namespace_to_host_commands(self, namespace):
"""
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward the SNMP request coming
in through the front panel interfaces created/present in the asic namespace to the SNMP Agent running in SNMP container in
linux host network namespace. The external IP addresses are NATed to the internal docker IP addresses for the SNMP Agent to respond.
"""
fwd_snmp_traffic_from_namespace_to_host_cmds = []
if namespace:
# IPv4 rules
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -X")
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -F")
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
"iptables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ip))
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
"iptables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ip[namespace]))
# IPv6 rules
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -X")
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -F")
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
"ip6tables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ipv6))
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
"ip6tables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ipv6[namespace]))
return fwd_snmp_traffic_from_namespace_to_host_cmds
def is_rule_ipv4(self, rule_props): def is_rule_ipv4(self, rule_props):
if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or
("DST_IP" in rule_props and rule_props["DST_IP"])): ("DST_IP" in rule_props and rule_props["DST_IP"])):
@ -429,6 +473,19 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
self.run_commands(iptables_cmds) self.run_commands(iptables_cmds)
def update_control_plane_nat_acls(self, namespace):
"""
Convenience wrapper which programs the NAT rules for allowing the
snmp traffic coming on the front panel interface
"""
# Add iptables commands to allow front panel snmp traffic
iptables_cmds = self.generate_fwd_snmp_traffic_from_namespace_to_host_commands(namespace)
self.log_info("Issuing the following iptables commands:")
for cmd in iptables_cmds:
self.log_info(" " + cmd)
self.run_commands(iptables_cmds)
def run(self): def run(self):
# Select Time-out for 10 Seconds # Select Time-out for 10 Seconds
SELECT_TIMEOUT_MS = 1000 * 10 SELECT_TIMEOUT_MS = 1000 * 10
@ -453,6 +510,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
for namespace in self.config_db_map.keys(): for namespace in self.config_db_map.keys():
# Unconditionally update control plane ACLs once at start on given namespace # Unconditionally update control plane ACLs once at start on given namespace
self.update_control_plane_acls(namespace) self.update_control_plane_acls(namespace)
self.update_control_plane_nat_acls(namespace)
# Connect to Config DB of given namespace # Connect to Config DB of given namespace
acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace) acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace)
# Subscribe to notifications when ACL tables changes # Subscribe to notifications when ACL tables changes