|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +"""" |
| 4 | +Description: restore_nat_entries.py -- restoring nat entries table into kernel during system warm reboot. |
| 5 | + The script is started by supervisord in nat docker when the docker is started. |
| 6 | + It does not do anything in case neither system nor nat warm restart is enabled. |
| 7 | + In case nat warm restart enabled only, it sets the stateDB flag so natsyncd can continue |
| 8 | + the reconciation process. |
| 9 | + In case system warm reboot is enabled, it will try to restore the nat entries table into kernel |
| 10 | + , then it sets the stateDB flag for natsyncd to continue the |
| 11 | + reconciliation process. |
| 12 | +""" |
| 13 | + |
| 14 | +import sys |
| 15 | +import subprocess |
| 16 | +from swsscommon import swsscommon |
| 17 | +import logging |
| 18 | +import logging.handlers |
| 19 | +import re |
| 20 | +import os |
| 21 | + |
| 22 | +WARM_BOOT_FILE_DIR = '/var/warmboot/nat/' |
| 23 | +NAT_WARM_BOOT_FILE = 'nat_entries.dump' |
| 24 | +IP_PROTO_TCP = '6' |
| 25 | + |
| 26 | +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+)' |
| 27 | +REDIS_SOCK = "/var/run/redis/redis.sock" |
| 28 | + |
| 29 | +logger = logging.getLogger(__name__) |
| 30 | +logger.setLevel(logging.INFO) |
| 31 | +handler = logging.handlers.SysLogHandler(address = '/dev/log') |
| 32 | +logger.addHandler(handler) |
| 33 | + |
| 34 | +def add_nat_conntrack_entry_in_kernel(ipproto, srcip, dstip, srcport, dstport, natsrcip, natdstip, natsrcport, natdstport): |
| 35 | + # pyroute2 doesn't have support for adding conntrack entries via netlink yet. So, invoking the conntrack utility to add the entries. |
| 36 | + state = '' |
| 37 | + if (ipproto == IP_PROTO_TCP): |
| 38 | + state = ' --state ESTABLISHED ' |
| 39 | + ctcmd = 'conntrack -I -n ' + natdstip + ':' + natdstport + ' -g ' + natsrcip + ':' + natsrcport + \ |
| 40 | + ' --protonum ' + ipproto + state + ' --timeout 600 --src ' + srcip + ' --sport ' + srcport + \ |
| 41 | + ' --dst ' + dstip + ' --dport ' + dstport + ' -u ASSURED' |
| 42 | + subprocess.call(ctcmd, shell=True) |
| 43 | + logger.info("Restored NAT entry: {}".format(ctcmd)) |
| 44 | + |
| 45 | +# Set the statedb "NAT_RESTORE_TABLE|Flags", so natsyncd can start reconciliation |
| 46 | +def set_statedb_nat_restore_done(): |
| 47 | + statedb = swsscommon.DBConnector(swsscommon.STATE_DB, REDIS_SOCK, 0) |
| 48 | + tbl = swsscommon.Table(statedb, "NAT_RESTORE_TABLE") |
| 49 | + fvs = swsscommon.FieldValuePairs([("restored", "true")]) |
| 50 | + tbl.set("Flags", fvs) |
| 51 | + return |
| 52 | + |
| 53 | +# This function is to restore the kernel nat entries based on the saved nat entries. |
| 54 | +def restore_update_kernel_nat_entries(filename): |
| 55 | + # Read the entries from nat_entries.dump file and add them to kernel |
| 56 | + conntrack_match_pattern = re.compile(r'{}'.format(MATCH_CONNTRACK_ENTRY)) |
| 57 | + with open(filename, 'r') as fp: |
| 58 | + for line in fp: |
| 59 | + ctline = conntrack_match_pattern.findall(line) |
| 60 | + if not ctline: |
| 61 | + continue |
| 62 | + cmdargs = list(ctline.pop(0)) |
| 63 | + proto = cmdargs.pop(0) |
| 64 | + if proto not in ('tcp', 'udp'): |
| 65 | + continue |
| 66 | + add_nat_conntrack_entry_in_kernel(*cmdargs) |
| 67 | + |
| 68 | +def main(): |
| 69 | + logger.info("restore_nat_entries service is started") |
| 70 | + |
| 71 | + # Use warmstart python binding to check warmstart information |
| 72 | + warmstart = swsscommon.WarmStart() |
| 73 | + warmstart.initialize("natsyncd", "nat") |
| 74 | + warmstart.checkWarmStart("natsyncd", "nat", False) |
| 75 | + |
| 76 | + # if swss or system warm reboot not enabled, don't run |
| 77 | + if not warmstart.isWarmStart(): |
| 78 | + logger.info("restore_nat_entries service is skipped as warm restart not enabled") |
| 79 | + return |
| 80 | + |
| 81 | + # NAT restart not system warm reboot, set statedb directly |
| 82 | + if not warmstart.isSystemWarmRebootEnabled(): |
| 83 | + set_statedb_nat_restore_done() |
| 84 | + logger.info("restore_nat_entries service is done as system warm reboot not enabled") |
| 85 | + return |
| 86 | + |
| 87 | + # Program the nat conntrack entries in the kernel by reading the |
| 88 | + # entries from nat_entries.dump |
| 89 | + try: |
| 90 | + restore_update_kernel_nat_entries(WARM_BOOT_FILE_DIR + NAT_WARM_BOOT_FILE) |
| 91 | + except Exception as e: |
| 92 | + logger.exception(str(e)) |
| 93 | + sys.exit(1) |
| 94 | + |
| 95 | + # Remove the dump file after restoration |
| 96 | + os.remove(WARM_BOOT_FILE_DIR + NAT_WARM_BOOT_FILE) |
| 97 | + |
| 98 | + # set statedb to signal other processes like natsyncd |
| 99 | + set_statedb_nat_restore_done() |
| 100 | + logger.info("restore_nat_entries service is done for system warmreboot") |
| 101 | + return |
| 102 | + |
| 103 | +if __name__ == '__main__': |
| 104 | + main() |
0 commit comments