Skip to content

Commit 4006ce7

Browse files
authored
[Multi-Asic] Forward SNMP requests received on front panel interface to SNMP agent in host. (sonic-net#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.
1 parent a92986c commit 4006ce7

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

files/build_templates/docker_image_ctl.j2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ function postStartAction()
9191
{
9292
{%- if docker_container_name == "database" %}
9393
if [ "$DEV" ]; then
94+
# Enable the forwarding on eth0 interface in namespace.
95+
SYSCTL_NET_CONFIG="/etc/sysctl.d/sysctl-net.conf"
96+
docker exec -i database$DEV sed -i -e "s/^net.ipv4.conf.eth0.forwarding=0/net.ipv4.conf.eth0.forwarding=1/;
97+
s/^net.ipv6.conf.eth0.forwarding=0/net.ipv6.conf.eth0.forwarding=1/" $SYSCTL_NET_CONFIG
9498
docker exec -i database$DEV sysctl --system -e
9599
link_namespace $DEV
96100
fi

files/image_config/caclmgrd/caclmgrd

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,26 +85,37 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
8585
self.config_db_map[''].connect()
8686
self.iptables_cmd_ns_prefix[''] = ""
8787
self.namespace_mgmt_ip = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[''], '')
88+
self.namespace_mgmt_ipv6 = self.get_namespace_mgmt_ipv6(self.iptables_cmd_ns_prefix[''], '')
8889
self.namespace_docker_mgmt_ip = {}
90+
self.namespace_docker_mgmt_ipv6 = {}
8991
namespaces = device_info.get_all_namespaces()
9092
for front_asic_namespace in namespaces['front_ns']:
9193
self.config_db_map[front_asic_namespace] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespace)
9294
self.config_db_map[front_asic_namespace].connect()
9395
self.iptables_cmd_ns_prefix[front_asic_namespace] = "ip netns exec " + front_asic_namespace + " "
9496
self.namespace_docker_mgmt_ip[front_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[front_asic_namespace],
9597
front_asic_namespace)
98+
self.namespace_docker_mgmt_ipv6[front_asic_namespace] = self.get_namespace_mgmt_ipv6(self.iptables_cmd_ns_prefix[front_asic_namespace],
99+
front_asic_namespace)
96100

97101
for back_asic_namespace in namespaces['back_ns']:
98102
self.iptables_cmd_ns_prefix[back_asic_namespace] = "ip netns exec " + back_asic_namespace + " "
99103
self.namespace_docker_mgmt_ip[back_asic_namespace] = self.get_namespace_mgmt_ip(self.iptables_cmd_ns_prefix[back_asic_namespace],
100104
back_asic_namespace)
105+
self.namespace_docker_mgmt_ipv6[back_asic_namespace] = self.get_namespace_mgmt_ipv6(self.iptables_cmd_ns_prefix[back_asic_namespace],
106+
back_asic_namespace)
101107

102108
def get_namespace_mgmt_ip(self, iptable_ns_cmd_prefix, namespace):
103109
ip_address_get_command = iptable_ns_cmd_prefix + "ip -4 -o addr show " + ("eth0" if namespace else "docker0") +\
104110
" | awk '{print $4}' | cut -d'/' -f1 | head -1"
105111

106112
return self.run_commands([ip_address_get_command])
107113

114+
def get_namespace_mgmt_ipv6(self, iptable_ns_cmd_prefix, namespace):
115+
ipv6_address_get_command = iptable_ns_cmd_prefix + "ip -6 -o addr show scope global " + ("eth0" if namespace else "docker0") +\
116+
" | awk '{print $4}' | cut -d'/' -f1 | head -1"
117+
return self.run_commands([ipv6_address_get_command])
118+
108119
def run_commands(self, commands):
109120
"""
110121
Given a list of shell commands, run them in order
@@ -206,6 +217,39 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
206217
(docker_mgmt_ip, self.namespace_mgmt_ip))
207218
return allow_internal_docker_ip_cmds
208219

220+
def generate_fwd_snmp_traffic_from_namespace_to_host_commands(self, namespace):
221+
"""
222+
The below SNAT and DNAT rules are added in asic namespace in multi-ASIC platforms. It helps to forward the SNMP request coming
223+
in through the front panel interfaces created/present in the asic namespace to the SNMP Agent running in SNMP container in
224+
linux host network namespace. The external IP addresses are NATed to the internal docker IP addresses for the SNMP Agent to respond.
225+
"""
226+
fwd_snmp_traffic_from_namespace_to_host_cmds = []
227+
228+
if namespace:
229+
# IPv4 rules
230+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -X")
231+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "iptables -t nat -F")
232+
233+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
234+
"iptables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
235+
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ip))
236+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
237+
"iptables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
238+
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ip[namespace]))
239+
240+
# IPv6 rules
241+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -X")
242+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] + "ip6tables -t nat -F")
243+
244+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
245+
"ip6tables -t nat -A PREROUTING -p udp --dport {} -j DNAT --to-destination {}".format
246+
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_mgmt_ipv6))
247+
fwd_snmp_traffic_from_namespace_to_host_cmds.append(self.iptables_cmd_ns_prefix[namespace] +
248+
"ip6tables -t nat -A POSTROUTING -p udp --dport {} -j SNAT --to-source {}".format
249+
(self.ACL_SERVICES['SNMP']['dst_ports'][0], self.namespace_docker_mgmt_ipv6[namespace]))
250+
251+
return fwd_snmp_traffic_from_namespace_to_host_cmds
252+
209253
def is_rule_ipv4(self, rule_props):
210254
if (("SRC_IP" in rule_props and rule_props["SRC_IP"]) or
211255
("DST_IP" in rule_props and rule_props["DST_IP"])):
@@ -438,6 +482,19 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
438482

439483
self.run_commands(iptables_cmds)
440484

485+
def update_control_plane_nat_acls(self, namespace):
486+
"""
487+
Convenience wrapper which programs the NAT rules for allowing the
488+
snmp traffic coming on the front panel interface
489+
"""
490+
# Add iptables commands to allow front panel snmp traffic
491+
iptables_cmds = self.generate_fwd_snmp_traffic_from_namespace_to_host_commands(namespace)
492+
self.log_info("Issuing the following iptables commands:")
493+
for cmd in iptables_cmds:
494+
self.log_info(" " + cmd)
495+
496+
self.run_commands(iptables_cmds)
497+
441498
def run(self):
442499
# Select Time-out for 10 Seconds
443500
SELECT_TIMEOUT_MS = 1000 * 10
@@ -462,6 +519,7 @@ class ControlPlaneAclManager(daemon_base.DaemonBase):
462519
for namespace in self.config_db_map.keys():
463520
# Unconditionally update control plane ACLs once at start on given namespace
464521
self.update_control_plane_acls(namespace)
522+
self.update_control_plane_nat_acls(namespace)
465523
# Connect to Config DB of given namespace
466524
acl_db_connector = swsscommon.DBConnector("CONFIG_DB", 0, False, namespace)
467525
# Subscribe to notifications when ACL tables changes

0 commit comments

Comments
 (0)