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 <ra...@nvidia.com>
Signed-off-by: Emeel Hakim <eha...@nvidia.com>
---
 ipsec/ovs-monitor-ipsec.in | 459 ++++++++++++++++++++++++++-----------
 1 file changed, 325 insertions(+), 134 deletions(-)

diff --git a/ipsec/ovs-monitor-ipsec.in b/ipsec/ovs-monitor-ipsec.in
index a8b0705d9..582c96132 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,38 +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")
-        subprocess.call([self.IPSEC, "update"])
-        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)
@@ -371,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):
@@ -442,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_"
 
@@ -546,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
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to