Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 9 commits into from
Sep 26, 2020
4 changes: 4 additions & 0 deletions files/build_templates/docker_image_ctl.j2
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ function postStartAction()
{
{%- if docker_container_name == "database" %}
if [ "$DEV" ]; then
# Enable the forwarding on eth0 interface in namespace.
judyjoseph marked this conversation as resolved.
Show resolved Hide resolved
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
link_namespace $DEV
fi
Expand Down
59 changes: 59 additions & 0 deletions files/image_config/caclmgrd/caclmgrd
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,37 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
self.config_db_map[''].connect()
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_ipv6 = {}
namespaces = device_info.get_all_namespaces()
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].connect()
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],
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']:
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],
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):
ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\
" | awk '{print $4}' | cut -d'/' -f1 | head -1"

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):
"""
Given a list of shell commands, run them in order
Expand Down Expand Up @@ -202,6 +213,40 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
(docker_mgmt_ip, self.namespace_mgmt_ip))
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 present in namespace to the SNMP Agent running in the linux host. The external IP address are
NATed to the internal docker IP address for the SNMP agent running in linux host 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")
judyjoseph marked this conversation as resolved.
Show resolved Hide resolved

# For namespace docker allow all tcp/udp traffic from host docker bridge to its eth0 management ip
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
judyjoseph marked this conversation as resolved.
Show resolved Hide resolved
"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):
if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or
("DST_IP" in rule_props and rule_props["DST_IP"])):
Expand Down Expand Up @@ -429,6 +474,19 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):

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)
log_info("Issuing the following iptables commands:")
for cmd in iptables_cmds:
log_info(" " + cmd)

self.run_commands(iptables_cmds)

def run(self):
# Select Time-out for 10 Seconds
SELECT_TIMEOUT_MS = 1000 * 10
Expand All @@ -453,6 +511,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
for namespace in self.config_db_map.keys():
# Unconditionally update control plane ACLs once at start on given namespace
self.update_control_plane_acls(namespace)
self.update_control_plane_nat_acls(namespace)
# Connect to Config DB of given namespace
acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace)
# Subscribe to notifications when ACL tables changes
Expand Down