diff mbox series

[ovs-dev,v2,1/2] ovs-monitor-ipsec: Migration from ipsec.conf to swanctl.conf

Message ID 20220322140838.28772-2-ehakim@nvidia.com
State Changes Requested
Headers show
Series ovs-monitor-ipsec: move to using swanctl.conf when using strongswan as IKE daemon | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Emeel Hakim March 22, 2022, 2:08 p.m. UTC
As strongswan moved to the modern vici-based interface,this patch
modifies ovs-monitor-ipsec to use strongswan's vici-based
configuration instead of the legacy stroke-based configuration.

Reviewed-by: Raed Salem <raeds@nvidia.com>
Signed-off-by: Emeel Hakim <ehakim@nvidia.com>
---
 ipsec/ovs-monitor-ipsec.in | 466 ++++++++++++++++++++++++++-----------
 1 file changed, 325 insertions(+), 141 deletions(-)

Comments

Mike Pattrick April 21, 2023, 4:07 p.m. UTC | #1
On Tue, Mar 22, 2022 at 10:09 AM Emeel Hakim via dev
<ovs-dev@openvswitch.org> wrote:
>
> As strongswan moved to the modern vici-based interface,this patch
> modifies ovs-monitor-ipsec to use strongswan's vici-based
> configuration instead of the legacy stroke-based configuration.
>
> Reviewed-by: Raed Salem <raeds@nvidia.com>
> Signed-off-by: Emeel Hakim <ehakim@nvidia.com>
> ---
>  ipsec/ovs-monitor-ipsec.in | 466 ++++++++++++++++++++++++++-----------
>  1 file changed, 325 insertions(+), 141 deletions(-)
>
> diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
> index c9f3cc5a1..8c72563e1 100755
> --- a/ipsec/ovs-monitor-ipsec.in
> +++ b/ipsec/ovs-monitor-ipsec.in
> @@ -32,52 +32,6 @@ import ovs.vlog
>
>
>  FILE_HEADER = "# Generated by ovs-monitor-ipsec...do not modify by hand!\n\n"
> -transp_tmpl = {"gre": Template("""\
> -conn $ifname-$version
> -$auth_section
> -    leftprotoport=gre
> -    rightprotoport=gre
> -
> -"""), "gre64": Template("""\
> -conn $ifname-$version
> -$auth_section
> -    leftprotoport=gre
> -    rightprotoport=gre
> -
> -"""), "geneve": Template("""\
> -conn $ifname-in-$version
> -$auth_section
> -    leftprotoport=udp/6081
> -    rightprotoport=udp
> -
> -conn $ifname-out-$version
> -$auth_section
> -    leftprotoport=udp
> -    rightprotoport=udp/6081
> -
> -"""), "stt": Template("""\
> -conn $ifname-in-$version
> -$auth_section
> -    leftprotoport=tcp/7471
> -    rightprotoport=tcp
> -
> -conn $ifname-out-$version
> -$auth_section
> -    leftprotoport=tcp
> -    rightprotoport=tcp/7471
> -
> -"""), "vxlan": Template("""\
> -conn $ifname-in-$version
> -$auth_section
> -    leftprotoport=udp/4789
> -    rightprotoport=udp
> -
> -conn $ifname-out-$version
> -$auth_section
> -    leftprotoport=udp
> -    rightprotoport=udp/4789
> -
> -""")}
>  vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
>  exiting = False
>  monitor = None
> @@ -160,72 +114,249 @@ charon {
>  }
>  """ % (FILE_HEADER)
>
> -    CONF_HEADER = """%s
> -config setup
> -    uniqueids=yes
> +    SWANCTL_CONF_HEADER = """%s
> +conn-defaults {
> +      unique = replace
> +      reauth_time = 0
> +      version = 2
> +      proposals = aes128-sha256-x25519
> +}
>
> -conn %%default
> -    keyingtries=%%forever
> -    type=transport
> -    keyexchange=ikev2
> -    auto=route
> -    ike=aes256gcm16-sha256-modp2048
> -    esp=aes256gcm16-modp2048
> +child-defaults {
> +      esp_proposals = aes256gcm16-modp2048-esn
> +      mode = transport
> +      policies_fwd_out = yes
> +      start_action = start
> +}
>
>  """ % (FILE_HEADER)
>
> -    CA_SECTION = """ca ca_auth
> -    cacert=%s
> +    CA_SECTION = """authorities {
> +    ca_auth {
> +        cacert=%s
> +    }
> +}
>
>  """
>
> -    SHUNT_POLICY = """conn prevent_unencrypted_gre
> -    type=drop
> -    leftprotoport=gre
> -    mark={0}
> +    SHUNT_POLICY = """connections {{
> +   shunts {{
> +      children {{
> +         prevent_unencrypted_gre {{
> +            local_ts = 0.0.0.0/0 [gre]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_gre_ipv6 {{
> +            local_ts = ::/0 [gre]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_geneve {{
> +            local_ts = 0.0.0.0/0 [udp/6081]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_geneve_ipv6 {{
> +            local_ts = ::/0 [udp/6081]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_stt {{
> +            local_ts = 0.0.0.0/0 [tcp/7471]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_stt_ipv6 {{
> +            local_ts = ::/0 [tcp/7471]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_vxlan {{
> +            local_ts = 0.0.0.0/0 [udp/4789]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +         prevent_unencrypted_vxlan_ipv6 {{
> +            local_ts = ::/0 [udp/4789]
> +            mark_in = {0}
> +            mark_out = {0}
> +            mode = drop
> +            start_action = trap
> +         }}
> +      }}
> +   }}
> +}}
> +"""
> +    auth_tmpl = {"psk": Template("""\
> +local {
> +            auth = psk
> +            id = $local_ip
> +         }
> +         remote {
> +            auth = psk
> +            id = $remote_ip
> +         }"""),
> +                 "pki_remote": Template("""\
> +local {
> +            auth = pubkey
> +            id = $local_name
> +            certs = $certificate
> +        }
> +        remote {
> +           auth = pubkey
> +           id = $remote_name
> +           certs = $remote_cert
> +        }"""),
> +                 "pki_ca": Template("""\
> +local {
> +            auth = pubkey
> +            id = $local_name
> +            certs = $certificate
> +        }
> +        remote {
> +          auth = pubkey
> +          id = $remote_name
> +        }""")}
> +
> +    SECRETS_SECTION = """secrets {
> +        ike-$ifname {
> +            id = $local_ip
> +            secret = $psk
> +        }
> +}
>
> -conn prevent_unencrypted_geneve
> -    type=drop
> -    leftprotoport=udp/6081
> -    mark={0}
> +"""
> +    transp_tmpl = {"gre": Template("""\
> +connections {
> +    $ifname-$version : conn-defaults{
> +        local_addrs  = $local_addrs
> +        remote_addrs = $remote_ip
> +
> +        $auth_section
> +
> +        children {
> +            $ifname-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [gre]
> +                remote_ts = $remote_ip/$subnet [gre]
> +            }
> +        }
> +    }
> +}
>
> -conn prevent_unencrypted_stt
> -    type=drop
> -    leftprotoport=tcp/7471
> -    mark={0}
> +"""), "gre64": Template("""\
> +connections {
> +    $ifname-$version : conn-defaults{
> +        local_addrs  = $local_addrs
> +        remote_addrs = $remote_ip
> +
> +        $auth_section
> +
> +        children {
> +            $ifname-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [gre]
> +                remote_ts = $remote_ip/$subnet [gre]
> +            }
> +        }
> +    }
> +}
>
> -conn prevent_unencrypted_vxlan
> -    type=drop
> -    leftprotoport=udp/4789
> -    mark={0}
> +"""), "geneve": Template("""\
> +connections {
> +    $ifname-$version : conn-defaults{
> +        local_addrs  = $local_addrs
> +        remote_addrs = $remote_ip
> +
> +        $auth_section
> +
> +        children {
> +            $ifname-in-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [udp/6081]
> +                remote_ts = $remote_ip/$subnet [udp]
> +            }
> +            $ifname-out-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [udp]
> +                remote_ts = $remote_ip/$subnet [udp/6081]
> +            }
> +        }
>
> -"""
> +    }
> +}
>
> -    auth_tmpl = {"psk": Template("""\
> -    left=%any
> -    right=$remote_ip
> -    authby=psk"""),
> -                 "pki_remote": Template("""\
> -    left=%any
> -    right=$remote_ip
> -    leftid=$local_name
> -    rightid=$remote_name
> -    leftcert=$certificate
> -    rightcert=$remote_cert"""),
> -                 "pki_ca": Template("""\
> -    left=%any
> -    right=$remote_ip
> -    leftid=$local_name
> -    rightid=$remote_name
> -    leftcert=$certificate""")}
> +"""), "stt": Template("""\
> +connections {
> +    $ifname-$version : conn-defaults{
> +        local_addrs  = $local_addrs
> +        remote_addrs = $remote_ip
> +
> +        $auth_section
> +
> +        children {
> +            $ifname-in-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [tcp/7471]
> +                remote_ts = $remote_ip/$subnet [tcp]
> +            }
> +            $ifname-out-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [tcp]
> +                remote_ts = $remote_ip/$subnet [tcp/7471]
> +            }
> +        }
> +    }
> +}
> +
> +"""), "vxlan": Template("""\
> +connections {
> +    $ifname-$version : conn-defaults{
> +        local_addrs  = $local_addrs
> +        remote_addrs = $remote_ip
> +
> +        $auth_section
> +
> +        children {
> +            $ifname-in-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [udp/4789]
> +                remote_ts = $remote_ip/$subnet [udp]
> +            }
> +            $ifname-out-$version : child-defaults {
> +                local_ts = $local_ip/$subnet [udp]
> +                remote_ts = $remote_ip/$subnet [udp/4789]
> +            }
> +        }
> +    }
> +}
> +
> +""")}
>
>      def __init__(self, root_prefix):
> -        self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"
> -        self.IPSEC = root_prefix + "/usr/sbin/ipsec"
> -        self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf"
> -        self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets"
> +        if os.path.exists(root_prefix + "/etc/strongswan.d/"):
> +            self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"
> +        else:
> +            self.CHARON_CONF = (root_prefix +
> +                               "/etc/strongswan/strongswan.d/ovs.conf")
> +        if os.path.exists(root_prefix + "/etc/swanctl/conf.d"):
> +            self.SWANCTL_CONF = (root_prefix +
> +                                "/etc/swanctl/conf.d/ovs-swanctl.conf")
> +        else:
> +            self.SWANCTL_CONF = (root_prefix +
> +                                "/etc/strongswan/swanctl/conf.d/" +
> +                                "ovs-swanctl.conf")
> +        self.SYSTEMCTL = root_prefix + "/usr/bin/systemctl"
> +        self.SWANCTL = root_prefix + "/usr/sbin/swanctl"
>          self.conf_file = None
> -        self.secrets_file = None
>
>      def restart_ike_daemon(self):
>          """This function restarts StrongSwan."""
> @@ -233,26 +364,24 @@ conn prevent_unencrypted_vxlan
>          f.write(self.STRONGSWAN_CONF)
>          f.close()
>
> -        f = open(self.IPSEC_CONF, "w")
> -        f.write(self.CONF_HEADER)
> -        f.close()
> -
> -        f = open(self.IPSEC_SECRETS, "w")
> -        f.write(FILE_HEADER)
> +        f = open(self.SWANCTL_CONF, "w")
> +        f.write(self.SWANCTL_CONF_HEADER)
>          f.close()
>
>          vlog.info("Restarting StrongSwan")
> -        subprocess.call([self.IPSEC, "restart"])
> +        subprocess.call((self.SYSTEMCTL +
> +                       " restart strongswan-starter.service").split())
>
>      def get_active_conns(self):
> -        """This function parses output from 'ipsec status' command.
> +        """This function parses output from 'swanctl --list-conns' command.
>          It returns dictionary where <key> is interface name (as in OVSDB)
>          and <value> is another dictionary.  This another dictionary
>          uses strongSwan connection name as <key> and more detailed
>          sample line from the parsed outpus as <value>. """
>
>          conns = {}
> -        proc = subprocess.Popen([self.IPSEC, 'status'], stdout=subprocess.PIPE)
> +        proc = subprocess.Popen([self.SWANCTL, '--list-conns'],
> +                               stdout=subprocess.PIPE)
>
>          while True:
>              line = proc.stdout.readline().strip().decode()
> @@ -272,10 +401,8 @@ conn prevent_unencrypted_vxlan
>          return conns
>
>      def config_init(self):
> -        self.conf_file = open(self.IPSEC_CONF, "w")
> -        self.secrets_file = open(self.IPSEC_SECRETS, "w")
> -        self.conf_file.write(self.CONF_HEADER)
> -        self.secrets_file.write(FILE_HEADER)
> +        self.conf_file = open(self.SWANCTL_CONF, "w")
> +        self.conf_file.write(self.SWANCTL_CONF_HEADER)
>
>      def config_global(self, monitor):
>          """Configure the global state of IPsec tunnels."""
> @@ -299,13 +426,10 @@ conn prevent_unencrypted_vxlan
>
>      def config_tunnel(self, tunnel):
>          if tunnel.conf["psk"]:
> -            self.secrets_file.write('%%any %s : PSK "%s"\n' %
> -                            (tunnel.conf["remote_ip"], tunnel.conf["psk"]))
>              auth_section = self.auth_tmpl["psk"].substitute(tunnel.conf)
> +            secrets = Template(self.SECRETS_SECTION).substitute(tunnel.conf)
>          else:
> -            self.secrets_file.write("%%any %s : RSA %s\n" %
> -                                        (tunnel.conf["remote_ip"],
> -                                        tunnel.conf["private_key"]))
> +            secrets = None
>              if tunnel.conf["remote_cert"]:
>                  tmpl = self.auth_tmpl["pki_remote"]
>                  auth_section = tmpl.substitute(tunnel.conf)
> @@ -316,45 +440,43 @@ conn prevent_unencrypted_vxlan
>          vals = tunnel.conf.copy()
>          vals["auth_section"] = auth_section
>          vals["version"] = tunnel.version
> -        conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals)
> +        if tunnel.conf["address_family"] == "IPv6":
> +            vals["local_addrs"] = "::/0"
> +            vals["subnet"] = "64"
> +        else:
> +            vals["local_addrs"] = "0.0.0.0/0"
> +            vals["subnet"] = "32"
> +        if vals["local_ip"] == "%defaultroute":
> +            if tunnel.conf["address_family"] == "IPv6":
> +                vals["local_ip"] = "::/0"
> +            else:
> +                vals["local_ip"] = "0.0.0.0/0"
> +        conf_text = self.transp_tmpl[tunnel.conf[
> +                                    "tunnel_type"]].substitute(vals)
>          self.conf_file.write(conf_text)
>
> +        if secrets is not None:
> +            self.conf_file.write(secrets)
> +
>      def config_fini(self):
> -        self.secrets_file.close()
>          self.conf_file.close()
> -        self.secrets_file = None
>          self.conf_file = None
>
>      def refresh(self, monitor):
>          """This functions refreshes strongSwan configuration.  Behind the
>          scenes this function calls:
> -        1. once "ipsec update" command that tells strongSwan to load
> -           all new tunnels from "ipsec.conf"; and
> -        2. once "ipsec rereadsecrets" command that tells strongswan to load
> -           secrets from "ipsec.conf" file
> -        3. for every removed tunnel "ipsec stroke down-nb <tunnel>" command
> +        1. once "swanctl --load-all" command that tells strongSwan to load
> +           all new tunnels from "swanctl.conf"; and
> +        2. for every removed tunnel "swanctl -t --child <tunnel>" command
>             that removes old tunnels.
>          Once strongSwan vici bindings will be distributed with major
>          Linux distributions this function could be simplified."""
>          vlog.info("Refreshing StrongSwan configuration")
> -        proc = subprocess.Popen([self.IPSEC, "update"],
> -                        stdout=subprocess.PIPE,
> -                        stderr=subprocess.PIPE)
> -        outs, errs = proc.communicate()
> -        if proc.returncode != 0:
> -            vlog.err("StrongSwan failed to update configuration:\n"
> -                           "%s \n %s" % (str(outs), str(errs)))
> -
> -        subprocess.call([self.IPSEC, "rereadsecrets"])
> -        # "ipsec update" command does not remove those tunnels that were
> -        # updated or that disappeared from the ipsec.conf file.  So, we have
> -        # to manually remove them by calling "ipsec stroke down-nb <tunnel>"
> +        # "swanctl --load-all" command does not remove those tunnels that were
> +        # updated or that disappeared from the swanctl.conf files.  So, we have
> +        # to manually remove them by calling "swanctl -t --child  <tunnel>"
>          # command.  We use <version> number to tell apart tunnels that
>          # were just updated.
> -        # "ipsec down-nb" command is designed to be non-blocking (opposed
> -        # to "ipsec down" command).  This means that we should not be concerned
> -        # about possibility of ovs-monitor-ipsec to block for each tunnel
> -        # while strongSwan sends IKE messages over Internet.
>          conns_dict = self.get_active_conns()
>          for ifname, conns in conns_dict.items():
>              tunnel = monitor.tunnels.get(ifname)
> @@ -378,7 +500,21 @@ conn prevent_unencrypted_vxlan
>
>                  if not tunnel or tunnel.version != ver:
>                      vlog.info("%s is outdated %u" % (conn, ver))
> -                    subprocess.call([self.IPSEC, "stroke", "down-nb", conn])
> +                    self.terminate_ipsec_connection(conn)
> +
> +        self.update_ipsec_connections()
> +
> +    def update_ipsec_connections(self):
> +        process = subprocess.Popen((self.SWANCTL + " --load-all").split(),
> +                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
> +        err = str(process.stderr.read())
> +        if re.match(r".*Error.*", err, re.IGNORECASE) is not None:
> +            vlog.err(err)
> +
> +    def terminate_ipsec_connection(self, conn_name):
> +        subprocess.Popen((self.SWANCTL + " -t --child " +
> +                        conn_name).split(), stdout=subprocess.PIPE)
> +        vlog.info("IPsec connection terminated for " + conn_name)
>
>
>  class LibreSwanHelper(object):
> @@ -449,6 +585,53 @@ conn prevent_unencrypted_vxlan
>      leftrsasigkey=%cert
>      rightca=%same""")}
>

The following templates actually introduce a syntax error for me.

I had to make the following sort of change to get ipsec working again:
-    conn $ifname-$version
+conn $ifname-$version
     $auth_section
-        leftprotoport=gre
-        rightprotoport=gre
+    leftprotoport=gre
+    rightprotoport=gre

WIthout that, pluto doesn't seem to parse the config file properly.

Cheers,
M


> +    transp_tmpl = {"gre": Template("""\
> +    conn $ifname-$version
> +    $auth_section
> +        leftprotoport=gre
> +        rightprotoport=gre
> +
> +    """), "gre64": Template("""\
> +    conn $ifname-$version
> +    $auth_section
> +        leftprotoport=gre
> +        rightprotoport=gre
> +
> +    """), "geneve": Template("""\
> +    conn $ifname-in-$version
> +    $auth_section
> +        leftprotoport=udp/6081
> +        rightprotoport=udp
> +
> +    conn $ifname-out-$version
> +    $auth_section
> +        leftprotoport=udp
> +        rightprotoport=udp/6081
> +
> +    """), "stt": Template("""\
> +    conn $ifname-in-$version
> +    $auth_section
> +        leftprotoport=tcp/7471
> +        rightprotoport=tcp
> +
> +    conn $ifname-out-$version
> +    $auth_section
> +        leftprotoport=tcp
> +        rightprotoport=tcp/7471
> +
> +    """), "vxlan": Template("""\
> +    conn $ifname-in-$version
> +    $auth_section
> +        leftprotoport=udp/4789
> +        rightprotoport=udp
> +
> +    conn $ifname-out-$version
> +    $auth_section
> +        leftprotoport=udp
> +        rightprotoport=udp/4789
> +
> +    """)}
> +
>      CERT_PREFIX = "ovs_cert_"
>      CERTKEY_PREFIX = "ovs_certkey_"
>
> @@ -553,7 +736,8 @@ conn prevent_unencrypted_vxlan
>          vals = tunnel.conf.copy()
>          vals["auth_section"] = auth_section
>          vals["version"] = tunnel.version
> -        conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals)
> +        conf_text = self.transp_tmpl[tunnel.conf[
> +                                    "tunnel_type"]].substitute(vals)
>          self.conf_file.write(conf_text)
>
>      def config_fini(self):
> --
> 2.21.3
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
index c9f3cc5a1..8c72563e1 100755
--- a/ipsec/ovs-monitor-ipsec.in
+++ b/ipsec/ovs-monitor-ipsec.in
@@ -32,52 +32,6 @@  import ovs.vlog
 
 
 FILE_HEADER = "# Generated by ovs-monitor-ipsec...do not modify by hand!\n\n"
-transp_tmpl = {"gre": Template("""\
-conn $ifname-$version
-$auth_section
-    leftprotoport=gre
-    rightprotoport=gre
-
-"""), "gre64": Template("""\
-conn $ifname-$version
-$auth_section
-    leftprotoport=gre
-    rightprotoport=gre
-
-"""), "geneve": Template("""\
-conn $ifname-in-$version
-$auth_section
-    leftprotoport=udp/6081
-    rightprotoport=udp
-
-conn $ifname-out-$version
-$auth_section
-    leftprotoport=udp
-    rightprotoport=udp/6081
-
-"""), "stt": Template("""\
-conn $ifname-in-$version
-$auth_section
-    leftprotoport=tcp/7471
-    rightprotoport=tcp
-
-conn $ifname-out-$version
-$auth_section
-    leftprotoport=tcp
-    rightprotoport=tcp/7471
-
-"""), "vxlan": Template("""\
-conn $ifname-in-$version
-$auth_section
-    leftprotoport=udp/4789
-    rightprotoport=udp
-
-conn $ifname-out-$version
-$auth_section
-    leftprotoport=udp
-    rightprotoport=udp/4789
-
-""")}
 vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
 exiting = False
 monitor = None
@@ -160,72 +114,249 @@  charon {
 }
 """ % (FILE_HEADER)
 
-    CONF_HEADER = """%s
-config setup
-    uniqueids=yes
+    SWANCTL_CONF_HEADER = """%s
+conn-defaults {
+      unique = replace
+      reauth_time = 0
+      version = 2
+      proposals = aes128-sha256-x25519
+}
 
-conn %%default
-    keyingtries=%%forever
-    type=transport
-    keyexchange=ikev2
-    auto=route
-    ike=aes256gcm16-sha256-modp2048
-    esp=aes256gcm16-modp2048
+child-defaults {
+      esp_proposals = aes256gcm16-modp2048-esn
+      mode = transport
+      policies_fwd_out = yes
+      start_action = start
+}
 
 """ % (FILE_HEADER)
 
-    CA_SECTION = """ca ca_auth
-    cacert=%s
+    CA_SECTION = """authorities {
+    ca_auth {
+        cacert=%s
+    }
+}
 
 """
 
-    SHUNT_POLICY = """conn prevent_unencrypted_gre
-    type=drop
-    leftprotoport=gre
-    mark={0}
+    SHUNT_POLICY = """connections {{
+   shunts {{
+      children {{
+         prevent_unencrypted_gre {{
+            local_ts = 0.0.0.0/0 [gre]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_gre_ipv6 {{
+            local_ts = ::/0 [gre]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_geneve {{
+            local_ts = 0.0.0.0/0 [udp/6081]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_geneve_ipv6 {{
+            local_ts = ::/0 [udp/6081]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_stt {{
+            local_ts = 0.0.0.0/0 [tcp/7471]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_stt_ipv6 {{
+            local_ts = ::/0 [tcp/7471]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_vxlan {{
+            local_ts = 0.0.0.0/0 [udp/4789]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+         prevent_unencrypted_vxlan_ipv6 {{
+            local_ts = ::/0 [udp/4789]
+            mark_in = {0}
+            mark_out = {0}
+            mode = drop
+            start_action = trap
+         }}
+      }}
+   }}
+}}
+"""
+    auth_tmpl = {"psk": Template("""\
+local {
+            auth = psk
+            id = $local_ip
+         }
+         remote {
+            auth = psk
+            id = $remote_ip
+         }"""),
+                 "pki_remote": Template("""\
+local {
+            auth = pubkey
+            id = $local_name
+            certs = $certificate
+        }
+        remote {
+           auth = pubkey
+           id = $remote_name
+           certs = $remote_cert
+        }"""),
+                 "pki_ca": Template("""\
+local {
+            auth = pubkey
+            id = $local_name
+            certs = $certificate
+        }
+        remote {
+          auth = pubkey
+          id = $remote_name
+        }""")}
+
+    SECRETS_SECTION = """secrets {
+        ike-$ifname {
+            id = $local_ip
+            secret = $psk
+        }
+}
 
-conn prevent_unencrypted_geneve
-    type=drop
-    leftprotoport=udp/6081
-    mark={0}
+"""
+    transp_tmpl = {"gre": Template("""\
+connections {
+    $ifname-$version : conn-defaults{
+        local_addrs  = $local_addrs
+        remote_addrs = $remote_ip
+
+        $auth_section
+
+        children {
+            $ifname-$version : child-defaults {
+                local_ts = $local_ip/$subnet [gre]
+                remote_ts = $remote_ip/$subnet [gre]
+            }
+        }
+    }
+}
 
-conn prevent_unencrypted_stt
-    type=drop
-    leftprotoport=tcp/7471
-    mark={0}
+"""), "gre64": Template("""\
+connections {
+    $ifname-$version : conn-defaults{
+        local_addrs  = $local_addrs
+        remote_addrs = $remote_ip
+
+        $auth_section
+
+        children {
+            $ifname-$version : child-defaults {
+                local_ts = $local_ip/$subnet [gre]
+                remote_ts = $remote_ip/$subnet [gre]
+            }
+        }
+    }
+}
 
-conn prevent_unencrypted_vxlan
-    type=drop
-    leftprotoport=udp/4789
-    mark={0}
+"""), "geneve": Template("""\
+connections {
+    $ifname-$version : conn-defaults{
+        local_addrs  = $local_addrs
+        remote_addrs = $remote_ip
+
+        $auth_section
+
+        children {
+            $ifname-in-$version : child-defaults {
+                local_ts = $local_ip/$subnet [udp/6081]
+                remote_ts = $remote_ip/$subnet [udp]
+            }
+            $ifname-out-$version : child-defaults {
+                local_ts = $local_ip/$subnet [udp]
+                remote_ts = $remote_ip/$subnet [udp/6081]
+            }
+        }
 
-"""
+    }
+}
 
-    auth_tmpl = {"psk": Template("""\
-    left=%any
-    right=$remote_ip
-    authby=psk"""),
-                 "pki_remote": Template("""\
-    left=%any
-    right=$remote_ip
-    leftid=$local_name
-    rightid=$remote_name
-    leftcert=$certificate
-    rightcert=$remote_cert"""),
-                 "pki_ca": Template("""\
-    left=%any
-    right=$remote_ip
-    leftid=$local_name
-    rightid=$remote_name
-    leftcert=$certificate""")}
+"""), "stt": Template("""\
+connections {
+    $ifname-$version : conn-defaults{
+        local_addrs  = $local_addrs
+        remote_addrs = $remote_ip
+
+        $auth_section
+
+        children {
+            $ifname-in-$version : child-defaults {
+                local_ts = $local_ip/$subnet [tcp/7471]
+                remote_ts = $remote_ip/$subnet [tcp]
+            }
+            $ifname-out-$version : child-defaults {
+                local_ts = $local_ip/$subnet [tcp]
+                remote_ts = $remote_ip/$subnet [tcp/7471]
+            }
+        }
+    }
+}
+
+"""), "vxlan": Template("""\
+connections {
+    $ifname-$version : conn-defaults{
+        local_addrs  = $local_addrs
+        remote_addrs = $remote_ip
+
+        $auth_section
+
+        children {
+            $ifname-in-$version : child-defaults {
+                local_ts = $local_ip/$subnet [udp/4789]
+                remote_ts = $remote_ip/$subnet [udp]
+            }
+            $ifname-out-$version : child-defaults {
+                local_ts = $local_ip/$subnet [udp]
+                remote_ts = $remote_ip/$subnet [udp/4789]
+            }
+        }
+    }
+}
+
+""")}
 
     def __init__(self, root_prefix):
-        self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"
-        self.IPSEC = root_prefix + "/usr/sbin/ipsec"
-        self.IPSEC_CONF = root_prefix + "/etc/ipsec.conf"
-        self.IPSEC_SECRETS = root_prefix + "/etc/ipsec.secrets"
+        if os.path.exists(root_prefix + "/etc/strongswan.d/"):
+            self.CHARON_CONF = root_prefix + "/etc/strongswan.d/ovs.conf"
+        else:
+            self.CHARON_CONF = (root_prefix +
+                               "/etc/strongswan/strongswan.d/ovs.conf")
+        if os.path.exists(root_prefix + "/etc/swanctl/conf.d"):
+            self.SWANCTL_CONF = (root_prefix +
+                                "/etc/swanctl/conf.d/ovs-swanctl.conf")
+        else:
+            self.SWANCTL_CONF = (root_prefix +
+                                "/etc/strongswan/swanctl/conf.d/" +
+                                "ovs-swanctl.conf")
+        self.SYSTEMCTL = root_prefix + "/usr/bin/systemctl"
+        self.SWANCTL = root_prefix + "/usr/sbin/swanctl"
         self.conf_file = None
-        self.secrets_file = None
 
     def restart_ike_daemon(self):
         """This function restarts StrongSwan."""
@@ -233,26 +364,24 @@  conn prevent_unencrypted_vxlan
         f.write(self.STRONGSWAN_CONF)
         f.close()
 
-        f = open(self.IPSEC_CONF, "w")
-        f.write(self.CONF_HEADER)
-        f.close()
-
-        f = open(self.IPSEC_SECRETS, "w")
-        f.write(FILE_HEADER)
+        f = open(self.SWANCTL_CONF, "w")
+        f.write(self.SWANCTL_CONF_HEADER)
         f.close()
 
         vlog.info("Restarting StrongSwan")
-        subprocess.call([self.IPSEC, "restart"])
+        subprocess.call((self.SYSTEMCTL +
+                       " restart strongswan-starter.service").split())
 
     def get_active_conns(self):
-        """This function parses output from 'ipsec status' command.
+        """This function parses output from 'swanctl --list-conns' command.
         It returns dictionary where <key> is interface name (as in OVSDB)
         and <value> is another dictionary.  This another dictionary
         uses strongSwan connection name as <key> and more detailed
         sample line from the parsed outpus as <value>. """
 
         conns = {}
-        proc = subprocess.Popen([self.IPSEC, 'status'], stdout=subprocess.PIPE)
+        proc = subprocess.Popen([self.SWANCTL, '--list-conns'],
+                               stdout=subprocess.PIPE)
 
         while True:
             line = proc.stdout.readline().strip().decode()
@@ -272,10 +401,8 @@  conn prevent_unencrypted_vxlan
         return conns
 
     def config_init(self):
-        self.conf_file = open(self.IPSEC_CONF, "w")
-        self.secrets_file = open(self.IPSEC_SECRETS, "w")
-        self.conf_file.write(self.CONF_HEADER)
-        self.secrets_file.write(FILE_HEADER)
+        self.conf_file = open(self.SWANCTL_CONF, "w")
+        self.conf_file.write(self.SWANCTL_CONF_HEADER)
 
     def config_global(self, monitor):
         """Configure the global state of IPsec tunnels."""
@@ -299,13 +426,10 @@  conn prevent_unencrypted_vxlan
 
     def config_tunnel(self, tunnel):
         if tunnel.conf["psk"]:
-            self.secrets_file.write('%%any %s : PSK "%s"\n' %
-                            (tunnel.conf["remote_ip"], tunnel.conf["psk"]))
             auth_section = self.auth_tmpl["psk"].substitute(tunnel.conf)
+            secrets = Template(self.SECRETS_SECTION).substitute(tunnel.conf)
         else:
-            self.secrets_file.write("%%any %s : RSA %s\n" %
-                                        (tunnel.conf["remote_ip"],
-                                        tunnel.conf["private_key"]))
+            secrets = None
             if tunnel.conf["remote_cert"]:
                 tmpl = self.auth_tmpl["pki_remote"]
                 auth_section = tmpl.substitute(tunnel.conf)
@@ -316,45 +440,43 @@  conn prevent_unencrypted_vxlan
         vals = tunnel.conf.copy()
         vals["auth_section"] = auth_section
         vals["version"] = tunnel.version
-        conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals)
+        if tunnel.conf["address_family"] == "IPv6":
+            vals["local_addrs"] = "::/0"
+            vals["subnet"] = "64"
+        else:
+            vals["local_addrs"] = "0.0.0.0/0"
+            vals["subnet"] = "32"
+        if vals["local_ip"] == "%defaultroute":
+            if tunnel.conf["address_family"] == "IPv6":
+                vals["local_ip"] = "::/0"
+            else:
+                vals["local_ip"] = "0.0.0.0/0"
+        conf_text = self.transp_tmpl[tunnel.conf[
+                                    "tunnel_type"]].substitute(vals)
         self.conf_file.write(conf_text)
 
+        if secrets is not None:
+            self.conf_file.write(secrets)
+
     def config_fini(self):
-        self.secrets_file.close()
         self.conf_file.close()
-        self.secrets_file = None
         self.conf_file = None
 
     def refresh(self, monitor):
         """This functions refreshes strongSwan configuration.  Behind the
         scenes this function calls:
-        1. once "ipsec update" command that tells strongSwan to load
-           all new tunnels from "ipsec.conf"; and
-        2. once "ipsec rereadsecrets" command that tells strongswan to load
-           secrets from "ipsec.conf" file
-        3. for every removed tunnel "ipsec stroke down-nb <tunnel>" command
+        1. once "swanctl --load-all" command that tells strongSwan to load
+           all new tunnels from "swanctl.conf"; and
+        2. for every removed tunnel "swanctl -t --child <tunnel>" command
            that removes old tunnels.
         Once strongSwan vici bindings will be distributed with major
         Linux distributions this function could be simplified."""
         vlog.info("Refreshing StrongSwan configuration")
-        proc = subprocess.Popen([self.IPSEC, "update"],
-                        stdout=subprocess.PIPE,
-                        stderr=subprocess.PIPE)
-        outs, errs = proc.communicate()
-        if proc.returncode != 0:
-            vlog.err("StrongSwan failed to update configuration:\n"
-                           "%s \n %s" % (str(outs), str(errs)))
-
-        subprocess.call([self.IPSEC, "rereadsecrets"])
-        # "ipsec update" command does not remove those tunnels that were
-        # updated or that disappeared from the ipsec.conf file.  So, we have
-        # to manually remove them by calling "ipsec stroke down-nb <tunnel>"
+        # "swanctl --load-all" command does not remove those tunnels that were
+        # updated or that disappeared from the swanctl.conf files.  So, we have
+        # to manually remove them by calling "swanctl -t --child  <tunnel>"
         # command.  We use <version> number to tell apart tunnels that
         # were just updated.
-        # "ipsec down-nb" command is designed to be non-blocking (opposed
-        # to "ipsec down" command).  This means that we should not be concerned
-        # about possibility of ovs-monitor-ipsec to block for each tunnel
-        # while strongSwan sends IKE messages over Internet.
         conns_dict = self.get_active_conns()
         for ifname, conns in conns_dict.items():
             tunnel = monitor.tunnels.get(ifname)
@@ -378,7 +500,21 @@  conn prevent_unencrypted_vxlan
 
                 if not tunnel or tunnel.version != ver:
                     vlog.info("%s is outdated %u" % (conn, ver))
-                    subprocess.call([self.IPSEC, "stroke", "down-nb", conn])
+                    self.terminate_ipsec_connection(conn)
+
+        self.update_ipsec_connections()
+
+    def update_ipsec_connections(self):
+        process = subprocess.Popen((self.SWANCTL + " --load-all").split(),
+                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        err = str(process.stderr.read())
+        if re.match(r".*Error.*", err, re.IGNORECASE) is not None:
+            vlog.err(err)
+
+    def terminate_ipsec_connection(self, conn_name):
+        subprocess.Popen((self.SWANCTL + " -t --child " +
+                        conn_name).split(), stdout=subprocess.PIPE)
+        vlog.info("IPsec connection terminated for " + conn_name)
 
 
 class LibreSwanHelper(object):
@@ -449,6 +585,53 @@  conn prevent_unencrypted_vxlan
     leftrsasigkey=%cert
     rightca=%same""")}
 
+    transp_tmpl = {"gre": Template("""\
+    conn $ifname-$version
+    $auth_section
+        leftprotoport=gre
+        rightprotoport=gre
+
+    """), "gre64": Template("""\
+    conn $ifname-$version
+    $auth_section
+        leftprotoport=gre
+        rightprotoport=gre
+
+    """), "geneve": Template("""\
+    conn $ifname-in-$version
+    $auth_section
+        leftprotoport=udp/6081
+        rightprotoport=udp
+
+    conn $ifname-out-$version
+    $auth_section
+        leftprotoport=udp
+        rightprotoport=udp/6081
+
+    """), "stt": Template("""\
+    conn $ifname-in-$version
+    $auth_section
+        leftprotoport=tcp/7471
+        rightprotoport=tcp
+
+    conn $ifname-out-$version
+    $auth_section
+        leftprotoport=tcp
+        rightprotoport=tcp/7471
+
+    """), "vxlan": Template("""\
+    conn $ifname-in-$version
+    $auth_section
+        leftprotoport=udp/4789
+        rightprotoport=udp
+
+    conn $ifname-out-$version
+    $auth_section
+        leftprotoport=udp
+        rightprotoport=udp/4789
+
+    """)}
+
     CERT_PREFIX = "ovs_cert_"
     CERTKEY_PREFIX = "ovs_certkey_"
 
@@ -553,7 +736,8 @@  conn prevent_unencrypted_vxlan
         vals = tunnel.conf.copy()
         vals["auth_section"] = auth_section
         vals["version"] = tunnel.version
-        conf_text = transp_tmpl[tunnel.conf["tunnel_type"]].substitute(vals)
+        conf_text = self.transp_tmpl[tunnel.conf[
+                                    "tunnel_type"]].substitute(vals)
         self.conf_file.write(conf_text)
 
     def config_fini(self):