2020-11-06 12:15:49 -06:00
|
|
|
#!/usr/bin/env python3
|
2020-01-29 19:40:43 -06:00
|
|
|
|
|
|
|
""""
|
|
|
|
Description: restore_nat_entries.py -- restoring nat entries table into kernel during system warm reboot.
|
|
|
|
The script is started by supervisord in nat docker when the docker is started.
|
|
|
|
It does not do anything in case neither system nor nat warm restart is enabled.
|
|
|
|
In case nat warm restart enabled only, it sets the stateDB flag so natsyncd can continue
|
|
|
|
the reconciation process.
|
|
|
|
In case system warm reboot is enabled, it will try to restore the nat entries table into kernel
|
|
|
|
, then it sets the stateDB flag for natsyncd to continue the
|
|
|
|
reconciliation process.
|
|
|
|
"""
|
|
|
|
|
2020-11-06 12:15:49 -06:00
|
|
|
import os
|
|
|
|
import re
|
2020-01-29 19:40:43 -06:00
|
|
|
import subprocess
|
2020-11-06 12:15:49 -06:00
|
|
|
import sys
|
|
|
|
|
|
|
|
from sonic_py_common.logger import Logger
|
2020-01-29 19:40:43 -06:00
|
|
|
from swsscommon import swsscommon
|
2020-11-06 12:15:49 -06:00
|
|
|
|
|
|
|
SYSLOG_IDENTIFIER = os.path.basename(__file__)
|
2020-01-29 19:40:43 -06:00
|
|
|
|
|
|
|
WARM_BOOT_FILE_DIR = '/var/warmboot/nat/'
|
|
|
|
NAT_WARM_BOOT_FILE = 'nat_entries.dump'
|
|
|
|
IP_PROTO_TCP = '6'
|
|
|
|
|
|
|
|
MATCH_CONNTRACK_ENTRY = '^(\w+)\s+(\d+).*src=([\d.]+)\s+dst=([\d.]+)\s+sport=(\d+)\s+dport=(\d+).*src=([\d.]+)\s+dst=([\d.]+)\s+sport=(\d+)\s+dport=(\d+)'
|
|
|
|
|
2020-11-06 12:15:49 -06:00
|
|
|
# Global logger instance
|
|
|
|
logger = Logger(SYSLOG_IDENTIFIER)
|
|
|
|
logger.set_min_log_priority_info()
|
|
|
|
|
2020-01-29 19:40:43 -06:00
|
|
|
|
|
|
|
def add_nat_conntrack_entry_in_kernel(ipproto, srcip, dstip, srcport, dstport, natsrcip, natdstip, natsrcport, natdstport):
|
|
|
|
# pyroute2 doesn't have support for adding conntrack entries via netlink yet. So, invoking the conntrack utility to add the entries.
|
|
|
|
state = ''
|
|
|
|
if (ipproto == IP_PROTO_TCP):
|
|
|
|
state = ' --state ESTABLISHED '
|
|
|
|
ctcmd = 'conntrack -I -n ' + natdstip + ':' + natdstport + ' -g ' + natsrcip + ':' + natsrcport + \
|
2020-07-08 16:28:43 -05:00
|
|
|
' --protonum ' + ipproto + state + ' --timeout 432000 --src ' + srcip + ' --sport ' + srcport + \
|
2020-01-29 19:40:43 -06:00
|
|
|
' --dst ' + dstip + ' --dport ' + dstport + ' -u ASSURED'
|
|
|
|
subprocess.call(ctcmd, shell=True)
|
2020-11-06 12:15:49 -06:00
|
|
|
logger.log_info("Restored NAT entry: {}".format(ctcmd))
|
|
|
|
|
2020-01-29 19:40:43 -06:00
|
|
|
|
|
|
|
# Set the statedb "NAT_RESTORE_TABLE|Flags", so natsyncd can start reconciliation
|
|
|
|
def set_statedb_nat_restore_done():
|
2020-05-06 17:41:28 -05:00
|
|
|
statedb = swsscommon.DBConnector("STATE_DB", 0)
|
2020-01-29 19:40:43 -06:00
|
|
|
tbl = swsscommon.Table(statedb, "NAT_RESTORE_TABLE")
|
|
|
|
fvs = swsscommon.FieldValuePairs([("restored", "true")])
|
|
|
|
tbl.set("Flags", fvs)
|
|
|
|
return
|
|
|
|
|
2020-11-06 12:15:49 -06:00
|
|
|
|
2020-01-29 19:40:43 -06:00
|
|
|
# This function is to restore the kernel nat entries based on the saved nat entries.
|
|
|
|
def restore_update_kernel_nat_entries(filename):
|
|
|
|
# Read the entries from nat_entries.dump file and add them to kernel
|
|
|
|
conntrack_match_pattern = re.compile(r'{}'.format(MATCH_CONNTRACK_ENTRY))
|
|
|
|
with open(filename, 'r') as fp:
|
|
|
|
for line in fp:
|
|
|
|
ctline = conntrack_match_pattern.findall(line)
|
|
|
|
if not ctline:
|
|
|
|
continue
|
|
|
|
cmdargs = list(ctline.pop(0))
|
|
|
|
proto = cmdargs.pop(0)
|
|
|
|
if proto not in ('tcp', 'udp'):
|
|
|
|
continue
|
|
|
|
add_nat_conntrack_entry_in_kernel(*cmdargs)
|
|
|
|
|
2020-11-06 12:15:49 -06:00
|
|
|
|
2020-01-29 19:40:43 -06:00
|
|
|
def main():
|
2020-11-06 12:15:49 -06:00
|
|
|
logger.log_info("restore_nat_entries service is started")
|
2020-01-29 19:40:43 -06:00
|
|
|
|
|
|
|
# Use warmstart python binding to check warmstart information
|
|
|
|
warmstart = swsscommon.WarmStart()
|
|
|
|
warmstart.initialize("natsyncd", "nat")
|
|
|
|
warmstart.checkWarmStart("natsyncd", "nat", False)
|
|
|
|
|
|
|
|
# if swss or system warm reboot not enabled, don't run
|
|
|
|
if not warmstart.isWarmStart():
|
2020-11-06 12:15:49 -06:00
|
|
|
logger.log_info("restore_nat_entries service is skipped as warm restart not enabled")
|
2020-01-29 19:40:43 -06:00
|
|
|
return
|
|
|
|
|
|
|
|
# NAT restart not system warm reboot, set statedb directly
|
|
|
|
if not warmstart.isSystemWarmRebootEnabled():
|
|
|
|
set_statedb_nat_restore_done()
|
2020-11-06 12:15:49 -06:00
|
|
|
logger.log_info("restore_nat_entries service is done as system warm reboot not enabled")
|
2020-01-29 19:40:43 -06:00
|
|
|
return
|
|
|
|
|
|
|
|
# Program the nat conntrack entries in the kernel by reading the
|
|
|
|
# entries from nat_entries.dump
|
|
|
|
try:
|
|
|
|
restore_update_kernel_nat_entries(WARM_BOOT_FILE_DIR + NAT_WARM_BOOT_FILE)
|
|
|
|
except Exception as e:
|
2020-11-06 12:15:49 -06:00
|
|
|
logger.log_error(str(e))
|
2020-01-29 19:40:43 -06:00
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# Remove the dump file after restoration
|
|
|
|
os.remove(WARM_BOOT_FILE_DIR + NAT_WARM_BOOT_FILE)
|
|
|
|
|
|
|
|
# set statedb to signal other processes like natsyncd
|
|
|
|
set_statedb_nat_restore_done()
|
2020-11-06 12:15:49 -06:00
|
|
|
logger.log_info("restore_nat_entries service is done for system warmreboot")
|
2020-01-29 19:40:43 -06:00
|
|
|
return
|
|
|
|
|
2020-11-06 12:15:49 -06:00
|
|
|
|
2020-01-29 19:40:43 -06:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|