diff --git a/files/image_config/hostcfgd/hostcfgd b/files/image_config/hostcfgd/hostcfgd index b1ec2644e122..4b37c64b9a68 100755 --- a/files/image_config/hostcfgd/hostcfgd +++ b/files/image_config/hostcfgd/hostcfgd @@ -8,6 +8,7 @@ import subprocess import syslog import copy import jinja2 +import ipaddr as ipaddress from swsssdk import ConfigDBConnector # FILE @@ -49,6 +50,82 @@ def obfuscate(data): else: return data +class Iptables(object): + def __init__(self): + ''' + Default MSS to 1460 - (MTU 1500 - 40 (TCP/IP Overhead)) + For IPv6, it would be 1440 - (MTU 1500 - 60 octects) + ''' + self.tcpmss = 1460 + self.tcp6mss = 1440 + + def is_ip_prefix_in_key(self, key): + ''' + Function to check if IP address is present in the key. If it + is present, then the key would be a tuple or else, it shall be + be string + ''' + return (isinstance(key, tuple)) + + def load(self, lpbk_table): + for row in lpbk_table: + self.iptables_handler(row, lpbk_table[row]) + + def command(self, chain, ip, ver, op): + cmd = 'iptables' if ver == '4' else 'ip6tables' + cmd += ' -t mangle --{} {} -p tcp --tcp-flags SYN SYN'.format(op, chain) + cmd += ' -d' if chain == 'PREROUTING' else ' -s' + mss = self.tcpmss if ver == '4' else self.tcp6mss + cmd += ' {} -j TCPMSS --set-mss {}'.format(ip, mss) + + return cmd + + def iptables_handler(self, key, data, add=True): + if not self.is_ip_prefix_in_key(key): + return + + iface, ip = key + ip_str = ip.split("/")[0] + ip_addr = ipaddress.IPAddress(ip_str) + if isinstance(ip_addr, ipaddress.IPv6Address): + ver = '6' + else: + ver = '4' + + self.mangle_handler(ip_str, ver, add) + + def mangle_handler(self, ip, ver, add): + if not add: + op = 'delete' + else: + op = 'check' + + iptables_cmds = [] + chains = ['PREROUTING', 'POSTROUTING'] + for chain in chains: + cmd = self.command(chain, ip, ver, op) + if not add: + iptables_cmds.append(cmd) + else: + ''' + For add case, first check if rule exists. Iptables just appends to the chain + as a new rule even if it is the same as an existing one. Check this and + do nothing if rule exists + ''' + ret = subprocess.call(cmd, shell=True) + if ret == 0: + syslog.syslog(syslog.LOG_INFO, "{} rule exists in {}".format(ip, chain)) + else: + # Modify command from Check to Append + iptables_cmds.append(cmd.replace("check", "append")) + + for cmd in iptables_cmds: + syslog.syslog(syslog.LOG_INFO, "Running cmd - {}".format(cmd)) + try: + subprocess.check_call(cmd, shell=True) + except subprocess.CalledProcessError as err: + syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}" + .format(err.cmd, err.returncode, err.output)) class AaaCfg(object): def __init__(self): @@ -159,6 +236,9 @@ class HostConfigDaemon: self.aaacfg = AaaCfg() self.aaacfg.load(aaa, tacacs_global, tacacs_server) self.hostname_cache="" + lpbk_table = self.config_db.get_table('LOOPBACK_INTERFACE') + self.iptables = Iptables() + self.iptables.load(lpbk_table) def aaa_handler(self, key, data): self.aaacfg.aaa_update(key, data) @@ -215,11 +295,23 @@ class HostConfigDaemon: self.hostname_cache = hostname + def lpbk_handler(self, key, data): + key = ConfigDBConnector.deserialize_key(key) + #Check if delete operation by fetch existing keys + keys = self.config_db.get_keys('LOOPBACK_INTERFACE') + if key in keys: + add = True + else: + add = False + + self.iptables.iptables_handler(key, data, add) + def start(self): self.config_db.subscribe('AAA', lambda table, key, data: self.aaa_handler(key, data)) self.config_db.subscribe('TACPLUS_SERVER', lambda table, key, data: self.tacacs_server_handler(key, data)) self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data)) self.config_db.subscribe('DEVICE_METADATA', lambda table, key, data: self.hostname_handler(key, data)) + self.config_db.subscribe('LOOPBACK_INTERFACE', lambda table, key, data: self.lpbk_handler(key, data)) self.config_db.listen()