Skip to content

Commit 86c5c5c

Browse files
kirankellatiantianlv
authored andcommitted
Changes in sonic-buildimage to support the NAT feature (sonic-net#3494)
* Changes in sonic-buildimage for the NAT feature - Docker for NAT - installing the required tools iptables and conntrack for nat Signed-off-by: [email protected] * Add redis-tools dependencies in the docker nat compilation * Addressed review comments * add natsyncd to warm-boot finalizer list * addressed review comments * using swsscommon.DBConnector instead of swsssdk.SonicV2Connector * Enable NAT application in docker-sonic-vs
1 parent 2d5a01e commit 86c5c5c

18 files changed

Lines changed: 636 additions & 3 deletions

File tree

build_debian.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y in
272272
cgroup-tools \
273273
ipmitool \
274274
ndisc6 \
275-
makedumpfile
275+
makedumpfile \
276+
conntrack
276277

277278

278279
if [[ $CONFIGURED_ARCH == amd64 ]]; then

dockers/docker-nat/Dockerfile.j2

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{% from "dockers/dockerfile-macros.j2" import install_debian_packages, copy_files %}
2+
FROM docker-config-engine-stretch
3+
4+
ARG docker_container_name
5+
RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf
6+
7+
RUN echo
8+
9+
## Make apt-get non-interactive
10+
ENV DEBIAN_FRONTEND=noninteractive
11+
12+
## Install redis-tools dependencies
13+
## TODO: implicitly install dependencies
14+
RUN apt-get update \
15+
&& apt-get install -f -y \
16+
libdbus-1-3 \
17+
libdaemon0 \
18+
libjansson4 \
19+
libpython2.7 \
20+
libatomic1 \
21+
libjemalloc1 \
22+
liblua5.1-0 \
23+
lua-bitop \
24+
lua-cjson \
25+
libelf1 \
26+
libmnl0 \
27+
bridge-utils \
28+
conntrack
29+
30+
{% if docker_nat_debs.strip() -%}
31+
# Copy locally-built Debian package dependencies
32+
{{copy_files ("debs/", docker_nat_debs.split(' '), "/debs/") }}
33+
34+
# Install locally-built Debian packages and implicitly install their dependencies
35+
{{ install_debian_packages(docker_nat_debs.split(' ')) }}
36+
{%- endif %}
37+
38+
COPY ["start.sh", "/usr/bin/"]
39+
COPY ["supervisord.conf", "/etc/supervisor/conf.d/"]
40+
COPY ["restore_nat_entries.py", "/usr/bin/"]
41+
42+
RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y
43+
RUN rm -rf /debs
44+
45+
ENTRYPOINT ["/usr/bin/supervisord"]
46+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
# -t option needed only for shell, not for commands
4+
5+
docker exec -i nat natctl "$@"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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()

dockers/docker-nat/start.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
3+
rm -f /var/run/rsyslogd.pid
4+
rm -f /var/run/nat/*
5+
6+
mkdir -p /var/warmboot/nat
7+
8+
supervisorctl start rsyslogd
9+
10+
supervisorctl start natmgrd
11+
12+
supervisorctl start natsyncd
13+
14+
supervisorctl start restore_nat_entries
15+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[supervisord]
2+
logfile_maxbytes=1MB
3+
logfile_backups=2
4+
nodaemon=true
5+
6+
[program:start.sh]
7+
command=/usr/bin/start.sh
8+
priority=1
9+
autostart=true
10+
autorestart=false
11+
stdout_logfile=syslog
12+
stderr_logfile=syslog
13+
14+
[program:rsyslogd]
15+
command=/usr/sbin/rsyslogd -n
16+
priority=2
17+
autostart=false
18+
autorestart=false
19+
stdout_logfile=syslog
20+
stderr_logfile=syslog
21+
22+
[program:natmgrd]
23+
command=/usr/bin/natmgrd
24+
priority=3
25+
autostart=false
26+
autorestart=false
27+
stdout_logfile=syslog
28+
stderr_logfile=syslog
29+
30+
[program:natsyncd]
31+
command=/usr/bin/natsyncd
32+
priority=4
33+
autostart=false
34+
autorestart=false
35+
stdout_logfile=syslog
36+
stderr_logfile=syslog
37+
38+
[program:restore_nat_entries]
39+
command=/usr/bin/restore_nat_entries.py
40+
priority=5
41+
autostart=false
42+
autorestart=false
43+
startsecs=0
44+
startretries=0
45+
stdout_logfile=syslog
46+
stderr_logfile=syslog
47+

dockers/docker-orchagent/Dockerfile.j2

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ RUN apt-get update && \
2020
tcpdump \
2121
libelf1 \
2222
libmnl0 \
23-
bridge-utils
23+
bridge-utils \
24+
conntrack
2425

2526
{% if ( CONFIGURED_ARCH == "armhf" or CONFIGURED_ARCH == "arm64" ) %}
2627
## Fix for gcc/python not found in arm docker
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[Unit]
2+
Description=NAT container
3+
Requires=updategraph.service swss.service
4+
After=updategraph.service swss.service syncd.service
5+
Before=ntp-config.service
6+
7+
[Service]
8+
User={{ sonicadmin_user }}
9+
ExecStartPre=/usr/bin/{{docker_container_name}}.sh start
10+
ExecStart=/usr/bin/{{docker_container_name}}.sh wait
11+
ExecStop=/usr/bin/{{docker_container_name}}.sh stop
12+
13+
[Install]
14+
WantedBy=multi-user.target swss.service
15+

files/build_templates/sonic_debian_extension.j2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ sudo mkdir -p $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
8181
sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/ifupdown2_*.deb || \
8282
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
8383

84+
# Install ipables (and its dependencies via 'apt-get -y install -f')
85+
sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/iptables_*.deb || \
86+
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
87+
8488
# Install dependencies for SONiC config engine
8589
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install \
8690
python-dev \

files/image_config/warmboot-finalizer/finalize-warmboot.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
VERBOSE=no
44

55
# Check components
6-
COMP_LIST="orchagent neighsyncd bgp"
6+
COMP_LIST="orchagent neighsyncd bgp natsyncd"
77
EXP_STATE="reconciled"
88

99
ASSISTANT_SCRIPT="/usr/bin/neighbor_advertiser"

0 commit comments

Comments
 (0)