[ovs-dev] [PATCH 1/3] ipsec: reintroduce IPsec support for tunneling

Aaron Conole aconole at redhat.com
Wed Jun 27 20:00:00 UTC 2018


Thanks for the patch.  It's really cool to see IPSec being
re-integrated.  Just a few quick comments inline.

Qiuyu Xiao <qiuyu.xiao.qyx at gmail.com> writes:

> From: Ansis Atteka <aatteka at ovn.org>
>
> This patch reintroduces ovs-monitor-ipsec daemon that
> was previously removed by commit 2b02d770 ("openvswitch:
> Allow external IPsec tunnel management.")
>
> The reason for removal at the time was that there were IPsec
> flavoured tunnel types, like ipsec_gre, that consumed for
> themselves the least significant bit of SKB mark irrelevant
> whether Open vSwitch users were using IPsec or not.
>
> After this patch, there are no IPsec flavored tunnels anymore.
> IPsec is enabled by setting up the right values in:
> 1. OVSDB:Interface:options column;
> 2. OVSDB:Open_vSwitch:other_config column;
> 3. OpenFlow pipeline.
>
> Signed-off-by: Ansis Atteka <aatteka at ovn.org>
> ---

Have you looked into what it would take to support other systems (for
instance, BSDs, Fedora/RHEL, CentOS)?

>  Documentation/automake.mk        |    1 +
>  Documentation/howto/index.rst    |    1 +
>  Documentation/howto/ipsec.rst    |  199 ++++++
>  Makefile.am                      |    1 +
>  debian/automake.mk               |    3 +
>  debian/control                   |   21 +
>  debian/openvswitch-ipsec.dirs    |    1 +
>  debian/openvswitch-ipsec.init    |  189 ++++++
>  debian/openvswitch-ipsec.install |    1 +
>  ipsec/automake.mk                |    9 +
>  ipsec/ovs-monitor-ipsec          |  727 ++++++++++++++++++++
>  tests/automake.mk                |    1 +
>  tests/ovs-monitor-ipsec.at       | 1076 ++++++++++++++++++++++++++++++
>  tests/testsuite.at               |    1 +
>  vswitchd/vswitch.xml             |   83 ++-
>  15 files changed, 2310 insertions(+), 4 deletions(-)
>  create mode 100644 Documentation/howto/ipsec.rst
>  create mode 100644 debian/openvswitch-ipsec.dirs
>  create mode 100644 debian/openvswitch-ipsec.init
>  create mode 100644 debian/openvswitch-ipsec.install
>  create mode 100644 ipsec/automake.mk
>  create mode 100755 ipsec/ovs-monitor-ipsec
>  create mode 100644 tests/ovs-monitor-ipsec.at
>
> diff --git a/Documentation/automake.mk b/Documentation/automake.mk
> index 2202df45b..3a505924a 100644
> --- a/Documentation/automake.mk
> +++ b/Documentation/automake.mk
> @@ -59,6 +59,7 @@ DOC_SOURCE = \
>  	Documentation/howto/docker.rst \
>  	Documentation/howto/dpdk.rst \
>  	Documentation/howto/firewalld.rst \
> +	Documentation/howto/ipsec.rst \
>  	Documentation/howto/kvm.rst \
>  	Documentation/howto/libvirt.rst \
>  	Documentation/howto/selinux.rst \
> diff --git a/Documentation/howto/index.rst b/Documentation/howto/index.rst
> index 201d6936b..9a3487be3 100644
> --- a/Documentation/howto/index.rst
> +++ b/Documentation/howto/index.rst
> @@ -37,6 +37,7 @@ OVS
>     :maxdepth: 2
>  
>     kvm
> +   ipsec
>     selinux
>     libvirt
>     ssl
> diff --git a/Documentation/howto/ipsec.rst b/Documentation/howto/ipsec.rst
> new file mode 100644
> index 000000000..4e4f4d211
> --- /dev/null
> +++ b/Documentation/howto/ipsec.rst
> @@ -0,0 +1,199 @@
> +..
> +      Licensed under the Apache License, Version 2.0 (the "License"); you may
> +      not use this file except in compliance with the License. You may obtain
> +      a copy of the License at
> +
> +          http://www.apache.org/licenses/LICENSE-2.0
> +
> +      Unless required by applicable law or agreed to in writing, software
> +      distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
> +      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
> +      License for the specific language governing permissions and limitations
> +      under the License.
> +
> +      Convention for heading levels in Open vSwitch documentation:
> +
> +      =======  Heading 0 (reserved for the title in a document)
> +      -------  Heading 1
> +      ~~~~~~~  Heading 2
> +      +++++++  Heading 3
> +      '''''''  Heading 4
> +
> +      Avoid deeper levels because they do not render well.
> +
> +==============================================
> +How to Encrypt Open vSwitch Tunnels with IPsec
> +==============================================
> +
> +This document describes how to use Open vSwitch integration with strongSwan
> +5.1 or later to provide IPsec security for STT, GENEVE, GRE and VXLAN tunnels.
> +This document assumes that you have already installed Open vSwitch.
> +
> +
> +Limitations
> +-----------
> +
> +There are several limitations:
> +
> +1) Currently only Debian-based platforms are supported.
> +
> +2) There is no backwards compatibility with the old IPsec implementation
> +   that uses Racoon instead of strongSwan for IKE keying.
> +
> +3) Some older Open vSwitch datapath kernel modules (in Linux Kernel tree)
> +   do not support route lookups with transport L4 ports properly.  In
> +   this case Ethernet over L4 tunneling protocols (e.g. STT, GENEVE)
> +   would not work.  However, GRE would still work because it does not
> +   have concept of L4 ports.
> +
> +4) Some strongSwan versions might not support certain features.  For
> +   example:
> +
> +   a) AES GCM ciphers that improve performance.
> +   b) xfrm_acq_expires setting in strongSwan configuration file.
> +      This setting tells strongSwan how aggressively to retry
> +      establishing tunnel, if peer did not respond to previous keying
> +      request.
> +   c) set_proto_port_transport_sa in charon configuration file that tells
> +      Linux Kernel to filter out unexpected packets that came over IPsec
> +      tunnel.
> +   d) IPsec transport mode for NAT deployment.
> +
> +5) Since IPsec decryption is done from the software when packet has already
> +   gotten past NIC hardware, then benefits of the most tunneling offloads,
> +   like GENEVE or TSO for STT can't be leveraged unless NIC has IPsec offloads
> +   as well and the NIC knows how to leverage them.
> +
> +
> +Setup
> +-----
> +
> +Install strongSwan and openvswitch-ipsec debian packages::
> +
> +      $ apt-get install strongswan
> +      $ dpkg -i openvswitch-ipsec_<version>_amd64.deb
> +
> +
> +Configuration
> +-------------
> +
> +IPsec with ovs-monitor-ipsec daemon can be configured in three
> +different forwarding policy modes:
> +
> +1) ovs-monitor-ipsec assumes that packets from all OVS tunnels by default
> +   should be allowed to exit unencrypted unless particular tunnel is
> +   explicitly configured with IPsec parameters::
> +
> +      % ovs-vsctl add-port br0 ipsec_gre0 -- \
> +                  set interface ipsec_gre0 type=gre \
> +                                     options:remote_ip=1.2.3.4 \
> +                                     options:psk=swordfish])
> +
> +
> +   The problem with this mode is that there is inherent race condition
> +   between ovs-monitor-ipsec and ovs-vswitchd daemons where ovs-vswitchd
> +   could enable forwarding before ovs-monitor-ipsec actually had a chance
> +   to configure IPsec policies.
> +   This mode should be used only and only if tunnel configuration is static
> +   and/or if there is firewall that can drop the plain packets that
> +   occasionally leak the tunnel unencrypted on OVSDB (re)configuration
> +   events.
> +
> +
> +2) ovs-monitor-ipsec assumes that packets marked with a given SKB mark
> +   must be encrypted and hence should not be allowed to leave the system
> +   unencrypted::
> +
> +     % ovs-vsctl set Open_vSwitch . other_config:default_ipsec_skb_mark=1/1
> +     % ovs-vsctl add-port br0 ipsec_gre0 -- \
> +                 set interface ipsec_gre0 type=gre \
> +                                   options:remote_ip=1.2.3.4 \
> +                                   options:psk=swordfish])
> +
> +   With this mode ovs-monitor-ipsec makes strongSwan to install IPsec shunt
> +   policies that serve as safety net and prevent unencrypted tunnel packets
> +   to leave the host in case ovs-vswitchd configured datapath before
> +   ovs-monitor-ipsec installed IPsec policies.
> +   However, assumption here is that OpenFlow controller was careful
> +   and installed OpenFlow rule with set SKB mark action specified in
> +   OVSDB Open_vSwitch table before the first packets were able to leave
> +   the OVS tunnel.
> +
> +3) ovs-monitor-ipsec assumes that packets coming from all OVS tunnels
> +   by default need to be protected with IPsec unless the tunnel is explicitly
> +   allowed to leave unencrypted.  This is inverse behavior of the second
> +   mode::
> +
> +     % ovs-vsctl set Open_vSwitch . other_config:default_ipsec_skb_mark=0/1
> +     % ovs-vsctl add-port br0 ipsec_gre0 -- \
> +                 set interface gre0 type=gre \
> +                                   options:remote_ip=1.2.3.4 \
> +                                   options:psk=swordfish])
> +
> +   With this solution ovs-monitor-ipsec tells strongSwan
> +   to install IPsec shunt policies to match on skb mark 0
> +   which should be the default skb mark value for all tunnel packets
> +   going through NetFilter/XFRM hooks.  As a result IPsec assumes
> +   that all packets coming from tunnels should be encrypted unless
> +   OpenFlow controller explicitly set skb mark to a non-zero value.
> +   This is the most secure mode, but at the same time the most intrusive
> +   one, because the OpenFlow pipeline needs to be overhauled just because
> +   now IPsec is enabled.
> +
> +   Note that instead of using least significant bit of SKB mark, you could
> +   just as well have used any other bit.
> +
> +
> +Troubleshooting
> +---------------
> +
> +Use following ovs-apptcl command to get ovs-monitor-ipsec internal
> +representation of tunnel configuration::
> +
> +    % ovs-appctl -t ovs-monitor-ipsec tunnels/show
> +
> +If there is misconfiguration then ovs-appctl should indicate why.
> +For example::
> +
> +   Interface name: gre0 v5 (CONFIGURED) <--- Should be set to CONFIGURED.
> +                                             Otherwise, error message will
> +                                             be provided
> +   Remote IP:      192.168.2.129
> +   Tunnel Type:    gre
> +   Local IP:       0.0.0.0
> +   Use SSL cert:   False
> +   My cert:        None
> +   My key:         None
> +   His cert:       None
> +   PSK:            swordfish
> +   Ofport:         1          <--- Whether ovs-vswitchd has assigned Ofport
> +                                   number to this Tunnel Port
> +   CFM state:      Up         <--- Whether CFM declared this tunnel healthy
> +   Kernel policies installed:
> +   ...                          <--- IPsec policies for this OVS tunnel in
> +                                     Linux Kernel installed by strongSwan
> +   Kernel security associations installed:
> +   ...                          <--- IPsec security associations for this OVS
> +                                     tunnel in Linux Kernel installed by
> +                                     strongswan
> +   Strongswan connections that are active:
> +   ...                          <--- strongSwan "connections" for this OVS
> +                                     tunnel
> +
> +
> +Bug Reporting
> +-------------
> +
> +If you think you may have found a bug with security implications, like
> +
> +1) IPsec protected tunnel accepted packets that came unencrypted; OR
> +2) IPsec protected tunnel allowed packets to leave unencrypted;
> +
> +Then report such bugs according to `security guidelines
> +<Documentation/internals/security.rst>`__.
> +
> +If bug does not have security implications, then report it according to
> +instructions in `<REPORTING-bugs.rst>`__.
> +
> +There is also a possibility that there is a bug in strongSwan.  In that
> +case report it to strongSwan mailing list.
> \ No newline at end of file
> diff --git a/Makefile.am b/Makefile.am
> index e02799a90..c0fef11fd 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -480,6 +480,7 @@ include tests/automake.mk
>  include include/automake.mk
>  include third-party/automake.mk
>  include debian/automake.mk
> +include ipsec/automake.mk
>  include vswitchd/automake.mk
>  include ovsdb/automake.mk
>  include rhel/automake.mk
> diff --git a/debian/automake.mk b/debian/automake.mk
> index 4d8e204bb..8a8d43c9f 100644
> --- a/debian/automake.mk
> +++ b/debian/automake.mk
> @@ -20,6 +20,9 @@ EXTRA_DIST += \
>  	debian/openvswitch-datapath-source.copyright \
>  	debian/openvswitch-datapath-source.dirs \
>  	debian/openvswitch-datapath-source.install \
> +	debian/openvswitch-ipsec.dirs \
> +	debian/openvswitch-ipsec.init \
> +	debian/openvswitch-ipsec.install \
>  	debian/openvswitch-pki.dirs \
>  	debian/openvswitch-pki.postinst \
>  	debian/openvswitch-pki.postrm \
> diff --git a/debian/control b/debian/control
> index a4c031d85..9443e91c9 100644
> --- a/debian/control
> +++ b/debian/control
> @@ -320,3 +320,24 @@ Description: Open vSwitch development package
>   1000V.
>   .
>   This package provides openvswitch headers and libopenvswitch for developers.
> +
> +Package: openvswitch-ipsec
> +Architecture: linux-any
> +Depends: iproute2,
> +         openvswitch-common (= ${binary:Version}),
> +         openvswitch-switch (= ${binary:Version}),
> +         python,
> +         python-openvswitch (= ${source:Version}),
> +         strongswan,
> +         ${misc:Depends},
> +         ${shlibs:Depends}
> +Description: Open vSwitch IPsec tunneling support
> + Open vSwitch is a production quality, multilayer, software-based,
> + Ethernet virtual switch. It is designed to enable massive network
> + automation through programmatic extension, while still supporting
> + standard management interfaces and protocols (e.g. NetFlow, IPFIX,
> + sFlow, SPAN, RSPAN, CLI, LACP, 802.1ag). In addition, it is designed
> + to support distribution across multiple physical servers similar to
> + VMware's vNetwork distributed vswitch or Cisco's Nexus 1000V.
> + .
> + This package provides IPsec tunneling support for OVS tunnels.
> diff --git a/debian/openvswitch-ipsec.dirs b/debian/openvswitch-ipsec.dirs
> new file mode 100644
> index 000000000..fca44aa7b
> --- /dev/null
> +++ b/debian/openvswitch-ipsec.dirs
> @@ -0,0 +1 @@
> +usr/share/openvswitch/scripts
> \ No newline at end of file
> diff --git a/debian/openvswitch-ipsec.init b/debian/openvswitch-ipsec.init
> new file mode 100644
> index 000000000..1a9683785
> --- /dev/null
> +++ b/debian/openvswitch-ipsec.init
> @@ -0,0 +1,189 @@
> +#!/bin/sh
> +#
> +# Copyright (c) 2007, 2009 Javier Fernandez-Sanguino <jfs at debian.org>
> +#
> +# This is free software; you may redistribute it and/or modify
> +# it under the terms of the GNU General Public License as
> +# published by the Free Software Foundation; either version 2,
> +# or (at your option) any later version.
> +#
> +# This is distributed in the hope that it will be useful, but
> +# WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License with
> +# the Debian operating system, in /usr/share/common-licenses/GPL;  if
> +# not, write to the Free Software Foundation, Inc., 59 Temple Place,
> +# Suite 330, Boston, MA 02111-1307 USA
> +#
> +### BEGIN INIT INFO
> +# Provides:          openvswitch-ipsec
> +# Required-Start:    $network $local_fs $remote_fs openvswitch-switch
> +# Required-Stop:     $remote_fs
> +# Default-Start:     2 3 4 5
> +# Default-Stop:      0 1 6
> +# Short-Description: Open vSwitch GRE-over-IPsec daemon
> +# Description:       The ovs-monitor-ipsec script provides support for encrypting GRE
> +#                    tunnels with IPsec.
> +### END INIT INFO
> +
> +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
> +
> +DAEMON=/usr/share/openvswitch/scripts/ovs-monitor-ipsec # Daemon's location
> +NAME=ovs-monitor-ipsec          # Introduce the short server's name here
> +LOGDIR=/var/log/openvswitch     # Log directory to use
> +
> +PIDFILE=/var/run/openvswitch/$NAME.pid
> +
> +test -x $DAEMON || exit 0
> +
> +. /lib/lsb/init-functions
> +
> +DODTIME=10              # Time to wait for the server to die, in seconds
> +                        # If this value is set too low you might not
> +                        # let some servers to die gracefully and
> +                        # 'restart' will not work
> +
> +set -e
> +
> +running_pid() {
> +# Check if a given process pid's cmdline matches a given name
> +    pid=$1
> +    name=$2
> +    [ -z "$pid" ] && return 1
> +    [ ! -d /proc/$pid ] &&  return 1
> +    cmd=`cat /proc/$pid/cmdline | tr "\000" " "|cut -d " " -f 2`
> +    # Is this the expected server
> +    [ "$cmd" != "$name" ] &&  return 1
> +    return 0
> +}
> +
> +running() {
> +# Check if the process is running looking at /proc
> +# (works for all users)
> +
> +    # No pidfile, probably no daemon present
> +    [ ! -f "$PIDFILE" ] && return 1
> +    pid=`cat $PIDFILE`
> +    running_pid $pid $DAEMON || return 1
> +    return 0
> +}
> +
> +start_server() {
> +    if [ ! -d /var/run/openvswitch ]; then
> +        install -d -m 755 -o root -g root /var/run/openvswitch
> +    fi
> +
> +    /usr/share/openvswitch/scripts/ovs-monitor-ipsec \
> +           --pidfile=$PIDFILE --log-file --detach --monitor \
> +           unix:/var/run/openvswitch/db.sock
> +
> +    return 0
> +}
> +
> +stop_server() {
> +    if [ -e $PIDFILE ]; then
> +        kill `cat $PIDFILE`
> +    fi
> +
> +    return 0
> +}
> +
> +force_stop() {
> +# Force the process to die killing it manually
> +    [ ! -e "$PIDFILE" ] && return
> +    if running ; then
> +        kill -15 $pid
> +        # Is it really dead?
> +        sleep "$DODTIME"
> +        if running ; then
> +            kill -9 $pid
> +            sleep "$DODTIME"
> +            if running ; then
> +                echo "Cannot kill $NAME (pid=$pid)!"
> +                exit 1
> +            fi
> +        fi
> +    fi
> +    rm -f $PIDFILE
> +}
> +
> +
> +case "$1" in
> +  start)
> +        log_daemon_msg "Starting $NAME"
> +        # Check if it's running first
> +        if running ;  then
> +            log_progress_msg "apparently already running"
> +            log_end_msg 0
> +            exit 0
> +        fi
> +        if start_server && running ;  then
> +            # It's ok, the server started and is running
> +            log_end_msg 0
> +        else
> +            # Either we could not start it or it is not running
> +            # after we did
> +            # NOTE: Some servers might die some time after they start,
> +            # this code does not try to detect this and might give
> +            # a false positive (use 'status' for that)
> +            log_end_msg 1
> +        fi
> +        ;;
> +  stop)
> +        log_daemon_msg "Stopping $NAME"
> +        if running ; then
> +            # Only stop the server if we see it running
> +            stop_server
> +            log_end_msg $?
> +        else
> +            # If it's not running don't do anything
> +            log_progress_msg "apparently not running"
> +            log_end_msg 0
> +            exit 0
> +        fi
> +        ;;
> +  force-stop)
> +        # First try to stop gracefully the program
> +        $0 stop
> +        if running; then
> +            # If it's still running try to kill it more forcefully
> +            log_daemon_msg "Stopping (force) $NAME"
> +            force_stop
> +            log_end_msg $?
> +        fi
> +        ;;
> +  restart|force-reload)
> +        log_daemon_msg "Restarting $NAME"
> +        stop_server
> +        # Wait some sensible amount, some server need this
> +        [ -n "$DODTIME" ] && sleep $DODTIME
> +        start_server
> +        running
> +        log_end_msg $?
> +        ;;
> +  status)
> +        log_daemon_msg "Checking status of $NAME"
> +        if running ;  then
> +            log_progress_msg "running"
> +            log_end_msg 0
> +        else
> +            log_progress_msg "apparently not running"
> +            log_end_msg 1
> +            exit 1
> +        fi
> +        ;;
> +  # Use this if the daemon cannot reload
> +  reload)
> +        log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon"
> +        log_warning_msg "cannot re-read the config file (use restart)."
> +        ;;
> +  *)
> +        N=/etc/init.d/openvswitch-ipsec
> +        echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2
> +        exit 1
> +        ;;
> +esac
> +
> +exit 0
> \ No newline at end of file
> diff --git a/debian/openvswitch-ipsec.install b/debian/openvswitch-ipsec.install
> new file mode 100644
> index 000000000..8fe665cb3
> --- /dev/null
> +++ b/debian/openvswitch-ipsec.install
> @@ -0,0 +1 @@
> +ipsec/ovs-monitor-ipsec usr/share/openvswitch/scripts
> diff --git a/ipsec/automake.mk b/ipsec/automake.mk
> new file mode 100644
> index 000000000..12baaed55
> --- /dev/null
> +++ b/ipsec/automake.mk
> @@ -0,0 +1,9 @@
> +# Copyright (C) 2017 Nicira, Inc.
> +#
> +# Copying and distribution of this file, with or without modification,
> +# are permitted in any medium without royalty provided the copyright
> +# notice and this notice are preserved.  This file is offered as-is,
> +# without warranty of any kind.
> +
> +EXTRA_DIST += \
> +        ipsec/ovs-monitor-ipsec

I think it would also make sense to add this utility to the flake8
checks.

> diff --git a/ipsec/ovs-monitor-ipsec b/ipsec/ovs-monitor-ipsec
> new file mode 100755
> index 000000000..322a7045a
> --- /dev/null
> +++ b/ipsec/ovs-monitor-ipsec
> @@ -0,0 +1,727 @@
> +#!/usr/bin/python

Use "/usr/bin/env python" for shebangs for python scripts.  This is
because some systems (such as NetBSD) will install in other locations
(for example, /usr/pkg/bin/python).

This also matches with the rest of the python scripts.

> +# Copyright (c) 2017 Nicira, Inc.
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at:
> +#
> +#     http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +import argparse
> +import glob
> +import os
> +import re
> +import subprocess
> +import sys
> +from string import Template
> +
> +from ovs.db import error
> +from ovs.db import types
> +import ovs.daemon
> +import ovs.db.idl
> +import ovs.dirs
> +import ovs.unixctl
> +import ovs.unixctl.server
> +import ovs.util
> +import ovs.vlog
> +
> +
> +FILE_HEADER = "# Generated by ovs-monitor-ipsec...do not modify by hand!\n\n"
> +
> +vlog = ovs.vlog.Vlog("ovs-monitor-ipsec")
> +exiting = False
> +keyer = None
> +xfrm = None
> +
> +
> +def unixctl_xfrm_policies(conn, unused_argv, unused_aux):
> +    global xfrm
> +    policies = xfrm.get_policies()
> +    conn.reply(str(policies))
> +
> +def unixctl_xfrm_state(conn, unused_argv, unused_aux):
> +    global xfrm
> +    securities = xfrm.get_securities()
> +    conn.reply(str(securities))
> +
> +def unixctl_ipsec_status(conn, unused_argv, unused_aux):
> +    global keyer
> +    conns = keyer._get_strongswan_conns()
> +    conn.reply(str(conns))
> +
> +def unixctl_show(conn, unused_argv, unused_aux):
> +    global keyer
> +    global xfrm
> +    policies = xfrm.get_policies()
> +    securities = xfrm.get_securities()
> +    keyer.show(conn, policies, securities)
> +
> +def unixctl_exit(conn, unused_argv, unused_aux):
> +    global exiting
> +    exiting = True
> +    conn.reply(None)
> +
> +
> +class XFRM(object):
> +    """This class is a simple wrapper around ip-xfrm (8) command line
> +    utility.  We are using this class only for informational purposes
> +    so that ovs-monitor-ipsec could verify that IKE keying daemon has
> +    installed IPsec policies and security associations into kernel as
> +    expected."""
> +
> +    def __init__(self, ip_root_prefix):
> +        self.IP = ip_root_prefix + "/sbin/ip"
> +
> +    def get_policies(self):
> +        """This function returns IPsec policies (from kernel) in a dictionary
> +        where <key> is destination IPv4 address and <value> is SELECTOR of
> +        the IPsec policy."""
> +        policies = {}
> +        proc = subprocess.Popen([self.IP, 'xfrm', 'policy'],
> +                                stdout=subprocess.PIPE)
> +        while True:
> +            line = proc.stdout.readline().strip()
> +            if line == '':
> +                break
> +            a = line.split(" ")
> +            if len(a) >= 4 and a[0] == "src" and a[2] == "dst":
> +                dst = (a[3].split("/"))[0]
> +                if not dst in policies:
> +                    policies[dst] = []
> +                policies[dst].append(line)
> +                src = (a[3].split("/"))[0]
> +                if not src in policies:
> +                    policies[src] = []
> +                policies[src].append(line)
> +        return policies
> +
> +    def get_securities(self):
> +        """This function returns IPsec security associations (from kernel)
> +        in a dictionary where <key> is destination IPv4 address and <value>
> +        is SELECTOR."""
> +        securities = {}
> +        proc = subprocess.Popen([self.IP, 'xfrm', 'state'],
> +                                stdout=subprocess.PIPE)
> +        while True:
> +            line = proc.stdout.readline().strip()
> +            if line == '':
> +                break
> +            a = line.split(" ")
> +            if len(a) >= 4 and a[0] == "sel" and a[1] == "src" and a[3] == "dst":
> +                remote_ip = a[4].rstrip().split("/")[0]
> +                local_ip = a[2].rstrip().split("/")[0]
> +                if not remote_ip in securities:
> +                    securities[remote_ip] = []
> +                securities[remote_ip].append(line)
> +                if not local_ip in securities:
> +                    securities[local_ip] = []
> +                securities[local_ip].append(line)
> +        return securities
> +
> +
> +class StrongSwanTunnel(object):
> +    """This class represents IPsec tunnel in strongSwan."""
> +
> +    transp_tmpl = {"gre" : Template("""\
> +conn $ifname-$version
> +$auth_section
> +    leftsubnet=%dynamic[gre]
> +    rightsubnet=%dynamic[gre]
> +
> +"""), "gre64" : Template("""\
> +conn $ifname-$version
> +$auth_section
> +    leftsubnet=%dynamic[gre]
> +    rightsubnet=%dynamic[gre]
> +
> +"""), "geneve" : Template("""\
> +conn $ifname-in-$version
> +$auth_section
> +    rightsubnet=%dynamic[udp/%any]
> +    leftsubnet=%dynamic[udp/6081]
> +
> +conn $ifname-out-$version
> +$auth_section
> +    rightsubnet=%dynamic[udp/6081]
> +    leftsubnet=%dynamic[udp/%any]
> +
> +"""), "stt" : Template("""\
> +conn $ifname-in-$version
> +$auth_section
> +    rightsubnet=%dynamic[tcp/%any]
> +    leftsubnet=%dynamic[tcp/7471]
> +
> +conn $ifname-out-$version
> +$auth_section
> +    rightsubnet=%dynamic[tcp/7471]
> +    leftsubnet=%dynamic[tcp/%any]
> +
> +"""), "vxlan" : Template("""\
> +conn $ifname-in-$version
> +$auth_section
> +    rightsubnet=%dynamic[udp/%any]
> +    leftsubnet=%dynamic[udp/4789]
> +
> +conn $ifname-out-$version
> +$auth_section
> +    rightsubnet=%dynamic[udp/4789]
> +    leftsubnet=%dynamic[udp/%any]
> +
> +""")}
> +
> +    auth_tmpl = {"psk" : Template("""\
> +    left=$local_ip
> +    right=$remote_ip
> +    authby=psk"""),
> +                 "rsa" : Template("""\
> +    left=$local_ip
> +    right=$remote_ip
> +    rightcert=ovs-$remote_ip.pem
> +    leftcert=$certificate""")}
> +
> +    unixctl_config_tmpl = Template("""\
> +  Remote IP:      $remote_ip
> +  Tunnel Type:    $tunnel_type
> +  Local IP:       $local_ip
> +  SKB mark:       $skb_mark
> +  Use SSL cert:   $use_ssl_cert
> +  My cert:        $certificate
> +  My key:         $private_key
> +  His cert:       $peer_cert
> +  PSK:            $psk
> +""")
> +
> +    unixctl_status_tmpl = Template("""\
> +  Ofport:         $ofport
> +  CFM state:      $cfm_state
> +""")
> +
> +    def __init__(self, name, row):
> +        self.name = name  # 'name' will not change because it is key in OVSDB
> +        self.version = 0  # 'version' is increased on configuration changes
> +        self.last_refreshed_version = -1
> +        self.state = "INIT"
> +        self.conf = {}
> +        self.status = {}
> +        self.cert_file = None
> +        self.update_conf(row)
> +
> +    def update_conf(self, row):
> +        """This function updates IPsec tunnel configuration by using 'row'
> +        from OVSDB interface table.  If configuration was actually changed
> +        in OVSDB then this function returns True.  Otherwise, it returns
> +        False."""
> +        ret = False
> +        options = row.options
> +        use_ssl_cert = options.get("use_ssl_cert") == "true"
> +        cert = options.get("certificate")
> +        key = options.get("private_key")
> +        if use_ssl_cert: # Override with SSL certs if told so
> +            (cert, key) = (keyer.public_cert, keyer.private_key)
> +
> +        new_conf = {
> +            "ifname" : self.name,
> +            "tunnel_type" : row.type,
> +            "remote_ip" : options.get("remote_ip"),
> +            "local_ip" : options.get("local_ip", "0.0.0.0"),
> +            "skb_mark" : keyer.wanted_skb_mark,
> +            "certificate" : cert,
> +            "private_key" : key,
> +            "use_ssl_cert" : use_ssl_cert,
> +            "peer_cert" : options.get("peer_cert"),
> +            "psk" : options.get("psk")}
> +        if self.conf != new_conf:
> +            # Configuration was updated in OVSDB.  Validate it and figure
> +            # out what to do next with this IPsec tunnel.  Also, increment
> +            # version number of this IPsec tunnel so that we could tell
> +            # apart old and new tunnels in "ipsec status" output.
> +            self.version += 1
> +            ret = True
> +            self.conf = new_conf
> +            if self._validate_conf():
> +                self.state = "CONFIGURED"
> +            else:
> +                vlog.warn("%s contains invalid configuration%s" %
> +                          (self.name, self.invalid_reason))
> +                self.state = "INVALID"
> +
> +        new_status = {
> +            "cfm_state" : "Up" if row.cfm_fault == [False] else
> +                          "Down" if row.cfm_fault == [True] else
> +                          "Disabled",
> +            "ofport" : "Not assigned" if (row.ofport in [[], [-1]]) else
> +                       row.ofport[0]}
> +        if self.status != new_status:
> +            # Tunnel has become unhealthy or ofport changed.  Simply log this.
> +            vlog.dbg("%s changed status from %s to %s" %
> +                     (self.name, str(self.status), str(new_status)))
> +            self.status = new_status
> +        return ret
> +
> +    def mark_for_removal(self):
> +        """This function marks tunnel for removal.  We can't delete tunnel
> +        right away because we have to clean any files created for this tunnel
> +        from it's run() function."""
> +        self.version += 1
> +        self.state = "REMOVED"
> +
> +    def run(self):
> +        """This function ensures that Tunnel's Finite State Machine moves
> +        forward."""
> +        if self.last_refreshed_version == self.version:
> +            # Configuration hasn't changed.  Exit early.
> +            return False
> +
> +        if self.state == "CONFIGURED":
> +            # If configuration changed then cleanup the old state
> +            # and push the new state
> +            self._cleanup_old_state()
> +            self._push_new_state()
> +        elif self.state == "INVALID":
> +            # If configuration is invalid then we prevent this
> +            # tunnel from pushing any configuration to the system
> +            self._cleanup_old_state()
> +        elif self.state == "REMOVED":
> +            self._cleanup_old_state()
> +        else:
> +            vlog.fatal("%s is in unexpected state" % self.name)
> +
> +        self.last_refreshed_version = self.version
> +        return True
> +
> +    def write_config(self, secrets, conf):
> +        if self.conf["psk"]:
> +            secrets.write("%s : PSK %s\n" % (self.conf["remote_ip"],
> +                                             self.conf["psk"]))
> +            auth_section = self.auth_tmpl["psk"].substitute(self.conf)
> +        else:
> +            secrets.write("%s : RSA %s\n" % (self.conf["remote_ip"],
> +                                             self.conf["private_key"]))
> +            auth_section = self.auth_tmpl["rsa"].substitute(self.conf)
> +        vals = self.conf.copy()
> +        vals["auth_section"] = auth_section
> +        vals["version"] = self.version
> +        conf.write(self.transp_tmpl[self.conf["tunnel_type"]].substitute(vals))
> +
> +    def show(self, policies, securities, conns):
> +        state = self.state
> +        if self.state == "INVALID":
> +            state += self.invalid_reason
> +        header = "Interface name: %s v%u (%s)\n" % (self.name, self.version,
> +                                                    state)
> +        conf = self.unixctl_config_tmpl.substitute(self.conf)
> +        status = self.unixctl_status_tmpl.substitute(self.status)
> +        spds = "Kernel policies installed:\n"
> +        remote_ip = self.conf["remote_ip"]
> +        if remote_ip in policies:
> +            for line in policies[remote_ip]:
> +                spds += "  " + line + "\n"
> +        sas = "Kernel security associations installed:\n"
> +        if remote_ip in securities:
> +            for line in securities[remote_ip]:
> +                sas += "  " + line + "\n"
> +        cons = "Strongswan connections that are active:\n"
> +        if self.name in conns:
> +            for tname in conns[self.name]:
> +                cons += "  " + conns[self.name][tname] + "\n"
> +
> +        return header + conf + status + spds + sas + cons + "\n"
> +
> +    def _validate_conf(self):
> +        """This function verifies if IPsec tunnel has valid configuration
> +        set in 'conf'.  If it is valid, then it returns True.  Otherwise,
> +        it returns False and sets the reason why configuration was considered
> +        as invalid.
> +
> +        This function could be improved in future to also verify validness
> +        of certificates themselves so that ovs-monitor-ipsec would not
> +        pass malformed configuration to strongSwan."""
> +
> +        self.invalid_reason = None
> +        if not self.conf["remote_ip"]:
> +            self.invalid_reason = ": 'remote_ip' is not set"
> +        elif self.conf["peer_cert"]:
> +            if self.conf["psk"]:
> +                self.invalid_reason = ": 'psk' must be unset with PKI"
> +            elif not self.conf["certificate"]:
> +                self.invalid_reason = ": must set 'certificate' with PKI"
> +            elif not self.conf["private_key"]:
> +                self.invalid_reason = ": must set 'private_key' with PKI"
> +        elif self.conf["psk"]:
> +            if self.conf["certificate"] or self.conf["private_key"]:
> +                self.invalid_reason = ": 'certificate', 'private_key' and "\
> +                                      "'use_ssl_cert' must be unset with PSK"
> +        if self.invalid_reason:
> +            return False
> +        return True
> +
> +    def _cleanup_old_state(self):
> +        if self.cert_file:
> +            try:
> +                os.remove(self.cert_file)
> +            except OSError:
> +                vlog.warn("could not remove '%s'" % (self.cert_file))
> +            self.cert_file = None
> +
> +    def _push_new_state(self):
> +        if self.conf["peer_cert"]:
> +            fn = keyer.CERT_DIR + "/ovs-%s.pem" % (self.name)
> +            try:
> +                cert = open(fn, "w")
> +                try:
> +                    cert.write(self.conf["peer_cert"])
> +                finally:
> +                    cert.close()
> +                    vlog.info("created peer certificate %s" % (fn))
> +                self.cert_file = fn
> +            except (OSError, IOError) as e:
> +                vlog.err("could not create certificate '%s'" % (fn))
> +
> +
> +class StrongSwanKeyer(object):
> +    """This class is a wrapper around strongSwan."""
> +
> +    STRONGSWAN_CONF = """%s
> +charon.plugins.kernel-netlink.set_proto_port_transport_sa = yes
> +charon.plugins.kernel-netlink.xfrm_ack_expires = 10
> +""" % (FILE_HEADER)
> +    CONF_HEADER = """%s
> +config setup
> +    uniqueids=no
> +
> +conn %%default
> +    keyingtries=%%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +""" % (FILE_HEADER)
> +    SHUNT_POLICY = """conn prevent_unencrypted_gre
> +    type=drop
> +    leftprotoport=gre
> +    mark=%s
> +
> +conn prevent_unencrypted_stt
> +    type=drop
> +    leftprotoport=tcp/7471
> +    mark=%s
> +
> +"""
> +
> +    def __init__(self, strongswan_root_prefix):
> +        self.private_key = None
> +        self.public_cert = None
> +        self.wanted_skb_mark = None
> +        self.in_use_skb_mark = None
> +        self.tunnels = {}
> +        self.CERT_DIR = strongswan_root_prefix + "/etc/ipsec.d/certs"
> +        self.CHARON_CONF = strongswan_root_prefix + "/etc/strongswan.d/ovs.conf"
> +        self.IPSEC = strongswan_root_prefix + "/usr/sbin/ipsec"
> +        self.IPSEC_CONF = strongswan_root_prefix + "/etc/ipsec.conf"
> +        self.IPSEC_SECRETS = strongswan_root_prefix + "/etc/ipsec.secrets"
> +
> +    def is_tunneling_type_supported(self, tunnel_type):
> +        """Returns True if we know how to configure IPsec for these
> +        types of tunnels.  Otherwise, returns False. """
> +        return tunnel_type in StrongSwanTunnel.transp_tmpl
> +
> +    def is_ipsec_required(self, options_column):
> +        """Return True if tunnel needs to be encrypted.  Otherwise,
> +        returns False."""
> +        return "psk" in options_column or "peer_cert" in options_column
> +
> +    def restart(self):
> +        """This function restarts strongSwan."""
> +        self._initial_configuration()
> +        vlog.info("restarting strongSwan")
> +        subprocess.call([self.IPSEC, "restart"])
> +
> +    def add_tunnel(self, name, row):
> +        """Adds a new tunnel that keyer will provision with 'name'."""
> +        vlog.info("Tunnel %s appeared in OVSDB" % (name))
> +        self.tunnels[name] = StrongSwanTunnel(name, row)
> +
> +    def update_tunnel(self, name, row):
> +        """Updates configuration of already existing tunnel with 'name'."""
> +        tunnel = self.tunnels[name]
> +        if tunnel.update_conf(row):
> +            vlog.info("Tunnel's '%s' configuration changed in OVSDB to %u" %
> +                      (tunnel.name, tunnel.version))
> +
> +    def del_tunnel(self, name):
> +        """Deletes tunnel by 'name'."""
> +        vlog.info("Tunnel %s disappeared from OVSDB" % (name))
> +        self.tunnels[name].mark_for_removal()
> +
> +    def update_ssl_credentials(self, credentials):
> +        """Updates already existing """
> +        self.public_cert = credentials[0]
> +        self.private_key = credentials[1]
> +
> +    def update_skb_mark(self, skb_mark):
> +        """Update skb_mark used in IPsec policies."""
> +        self.wanted_skb_mark = skb_mark
> +
> +    def show(self, unix_conn, policies, securities):
> +        """This function prints all tunnel state in 'unix_conn'.
> +        It uses 'policies' and securities' received from Linux Kernel
> +        to show if tunnels were actually configured by strongSwan."""
> +        if not self.tunnels:
> +            unix_conn.reply("No tunnels configured with IPsec")
> +            return
> +        s = ""
> +        conns = self._get_strongswan_conns()
> +        for name, tunnel in self.tunnels.iteritems():
> +            s += tunnel.show(policies, securities, conns)
> +        unix_conn.reply(s)
> +
> +    def run(self):
> +        """This function runs state machine that represents whole
> +        strongSwan configuration (i.e. merged together from individual
> +        tunnel state machines).  It creates configuration files and
> +        tells strongSwan to update configuration."""
> +        needs_refresh = False
> +        removed_tunnels = []
> +        ipsec_secrets = open(self.IPSEC_SECRETS, "w")
> +        ipsec_conf = open(self.IPSEC_CONF, "w")
> +        ipsec_secrets.write(FILE_HEADER)
> +        ipsec_conf.write(self.CONF_HEADER)
> +        if self.in_use_skb_mark != self.wanted_skb_mark:
> +            self.in_use_skb_mark = self.wanted_skb_mark
> +            needs_refresh = True
> +
> +        if self.in_use_skb_mark:
> +            ipsec_conf.write("    mark_out=%s\n" % self.in_use_skb_mark)
> +            ipsec_conf.write(self.SHUNT_POLICY % (self.in_use_skb_mark, self.in_use_skb_mark))
> +
> +        for name, tunnel in self.tunnels.iteritems():
> +            if tunnel.run():
> +                needs_refresh = True
> +
> +            if tunnel.state == "REMOVED":
> +                removed_tunnels.append(name)
> +            elif tunnel.state == "CONFIGURED":
> +                tunnel.write_config(ipsec_secrets, ipsec_conf)
> +        ipsec_secrets.close()
> +        ipsec_conf.close()
> +
> +        for name in removed_tunnels:
> +            del self.tunnels[name]
> +
> +        if needs_refresh:
> +            self._refresh()
> +
> +    def _refresh(self):
> +        """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
> +           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>"
> +        # 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_strongswan_conns()
> +        for ifname, conns in conns_dict.iteritems():
> +            tunnel = self.tunnels.get(ifname)
> +            for conn in conns:
> +                # IPsec "connection" names that we choose in strongswan
> +                # must start with Interface name
> +                if not conn.startswith(ifname):
> +                    vlog.err("%s does not start with %s" % (conn, ifname))
> +                    continue
> +
> +                # version number should be the first integer after
> +                # interface name in IPsec "connection"
> +                try:
> +                    ver = int(re.findall(r'\d+', conn[len(ifname):])[0])
> +                except ValueError, IndexError:
> +                    vlog.err("%s does not contain version number")
> +                    continue
> +
> +                if not tunnel or tunnel.version != ver:
> +                    vlog.info("%s is outdated %u" % (conn, ver))
> +                    subprocess.call([self.IPSEC, "stroke", "down-nb", conn])
> +
> +
> +    def _get_strongswan_conns(self):
> +        """This function parses output from 'ipsec status' 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)
> +        while True:
> +            line = proc.stdout.readline().strip()
> +            if line == '':
> +                break
> +            tunnel_name = line.split(":")
> +            if len(tunnel_name) < 2:
> +                continue
> +            ifname = tunnel_name[0].split("-")
> +            if len(ifname) < 2:
> +                continue
> +
> +            if not ifname[0] in conns:
> +                conns[ifname[0]] = {}
> +            (conns[ifname[0]])[tunnel_name[0]] = line
> +
> +        return conns
> +
> +    def _initial_configuration(self):
> +        """This function creates initial configuration that strongSwan should
> +        receive on startup."""
> +        f = open(self.CHARON_CONF, "w")
> +        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.close()
> +
> +
> +def read_ovsdb_open_vswitch_table(data):
> +    """This functions reads IPsec relevant configuration from Open_vSwitch
> +    table."""
> +    ssl = (None, None)
> +    global_ipsec_skb_mark = None
> +    for row in data["Open_vSwitch"].rows.itervalues():
> +        if row.ssl:
> +            ssl = (row.ssl[0].certificate, row.ssl[0].private_key)
> +        global_ipsec_skb_mark = row.other_config.get("default_ipsec_skb_mark")
> +    keyer.update_ssl_credentials(ssl)
> +    keyer.update_skb_mark(global_ipsec_skb_mark)
> +
> +def read_ovsdb_interface_table(data):
> +    """This function reads the IPsec relevant configuration from Interface
> +    table."""
> +    ifaces = set()
> +    for row in data["Interface"].rows.itervalues():
> +        if not keyer.is_tunneling_type_supported(row.type):
> +            continue
> +        if not keyer.is_ipsec_required(row.options):
> +            continue
> +        if row.name in keyer.tunnels:
> +            keyer.update_tunnel(row.name, row)
> +        else:
> +            keyer.add_tunnel(row.name, row)
> +        ifaces.add(row.name)
> +    # Mark for removal those tunnels that just disappeared from OVSDB
> +    for tunnel in keyer.tunnels.keys():
> +        if not tunnel in ifaces:
> +            keyer.del_tunnel(tunnel)
> +
> +def read_ovsdb(data):
> +    """This function reads all configuration from OVSDB that ovs-monitor-ipsec
> +    is interested in."""
> +    read_ovsdb_open_vswitch_table(data)
> +    read_ovsdb_interface_table(data)
> +
> +
> +def main():
> +    parser = argparse.ArgumentParser()
> +    parser.add_argument("database", metavar="DATABASE",
> +                        help="A socket on which ovsdb-server is listening.")
> +    parser.add_argument("--root-prefix", metavar="DIR",
> +                        help="Use DIR as alternate root directory"
> +                        " (for testing).")
> +
> +    ovs.vlog.add_args(parser)
> +    ovs.daemon.add_args(parser)
> +    args = parser.parse_args()
> +    ovs.vlog.handle_args(args)
> +    ovs.daemon.handle_args(args)
> +
> +    global keyer
> +    global xfrm
> +
> +    root_prefix = args.root_prefix if args.root_prefix else ""
> +    xfrm = XFRM(root_prefix)
> +    keyer = StrongSwanKeyer(root_prefix)
> +
> +    remote = args.database
> +    schema_helper = ovs.db.idl.SchemaHelper()
> +    schema_helper.register_columns("Interface",
> +                                   ["name", "type", "options", "cfm_fault",
> +                                    "ofport"])
> +    schema_helper.register_columns("Open_vSwitch", ["ssl", "other_config"])
> +    schema_helper.register_columns("SSL", ["certificate", "private_key"])
> +    idl = ovs.db.idl.Idl(remote, schema_helper)
> +
> +    ovs.daemon.daemonize()
> +
> +    ovs.unixctl.command_register("xfrm/policies", "", 0, 0,
> +                                 unixctl_xfrm_policies, None)
> +    ovs.unixctl.command_register("xfrm/state", "", 0, 0,
> +                                 unixctl_xfrm_state, None)
> +    ovs.unixctl.command_register("ipsec/status", "", 0, 0,
> +                                 unixctl_ipsec_status, None)
> +    ovs.unixctl.command_register("tunnels/show", "", 0, 0,
> +                                 unixctl_show, None)
> +    ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
> +
> +    error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
> +    if error:
> +        ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
> +
> +    seqno = idl.change_seqno  # Sequence number when OVSDB was processed last time
> +    keyer.restart()
> +    while True:
> +        unixctl_server.run()
> +        if exiting:
> +            break
> +
> +        idl.run()
> +        if seqno != idl.change_seqno:
> +            read_ovsdb(idl.tables)
> +            seqno = idl.change_seqno
> +
> +        keyer.run()
> +
> +        poller = ovs.poller.Poller()
> +        unixctl_server.wait(poller)
> +        idl.wait(poller)
> +        poller.block()
> +
> +    unixctl_server.close()
> +    idl.close()
> +
> +
> +if __name__ == '__main__':
> +    try:
> +        main()
> +    except SystemExit:
> +        # Let system.exit() calls complete normally
> +        raise
> +    except:
> +        vlog.exception("traceback")
> +        sys.exit(ovs.daemon.RESTART_EXIT_CODE)
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 8224e5a4a..703b4fa50 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -60,6 +60,7 @@ TESTSUITE_AT = \
>  	tests/lockfile.at \
>  	tests/reconnect.at \
>  	tests/ovs-vswitchd.at \
> +	tests/ovs-monitor-ipsec.at \
>  	tests/dpif-netdev.at \
>  	tests/dpctl.at \
>  	tests/ofproto-dpif.at \
> diff --git a/tests/ovs-monitor-ipsec.at b/tests/ovs-monitor-ipsec.at
> new file mode 100644
> index 000000000..ec537ad32
> --- /dev/null
> +++ b/tests/ovs-monitor-ipsec.at
> @@ -0,0 +1,1076 @@
> +# WAIT_FOR_TUNNEL_TO_UPDATE([tunnel], [version], [state])
> +#
> +# This macro should be called after ovs-vsctl command that updated
> +# IPsec configuration in OVSDB.  It basically serves as synchronization
> +# barrier to ensures that ovs-monitor-ipsec daemon's internal configuration
> +# for 'tunnel' has updated to 'version' and is in 'state'.
> +m4_define([WAIT_FOR_TUNNEL_TO_UPDATE], [
> +OVS_WAIT_UNTIL([ovs-appctl -t ovs-monitor-ipsec tunnels/show | grep "Interface name: $1 v$2 ($3"])
> +])
> +
> +
> +# WAIT_FOR_PROPER_IPSEC_CLEANUP([])
> +#
> +# This macro should be called *only* after all IPsec tunnels were removed
> +# from OVSDB Interface table.  This macro verifies that:
> +# 1) ovs-monitor-ipsec daemon thinks that there are no IPsec tunnels
> +# 2) ipsec.conf file does not contain any tunnels
> +# 3) ipsec.secrets does not contain any 'secrets' for any tunnels
> +# 4) ipsec.d directory does not contain any certificates that were
> +#    previously created by ovs-monitor-ipsec daemon
> +m4_define([WAIT_FOR_PROPER_IPSEC_CLEANUP], [
> +OVS_WAIT_UNTIL([ovs-appctl -t ovs-monitor-ipsec tunnels/show | grep "No tunnels configured with IPsec"])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +])
> +AT_CHECK([ls etc/ipsec.d/certs], [0], [dnl])
> +])
> +
> +
> +# VERIFY_IPSEC_COMMANDS_EXECUTED([ipsec_commands])
> +#
> +# This macro alows to verify that ovs-monitor-ipsec daemon called correct
> +# 'ipsec' commands.  All ipsec commands are logged by a script that mocks
> +# 'ipsec' utility.
> +# This macro filters out "ipsec rereadsecrets" and "ipsec status"
> +# commands.
> +m4_define([VERIFY_IPSEC_COMMANDS_EXECUTED], [
> +AT_CHECK([egrep -v "ipsec status|ipsec rereadsecrets" actions], [0], [$1])
> +])
> +
> +
> +# INIT_SSL_CERTS([])
> +#
> +# This macro creates sample SSL certificates so that it would be
> +# possible to test tunnels that use "use_ssl_certs" setting.  Don't
> +# use these certificates in production.
> +m4_define([INIT_SSL_CERTS], [
> +AT_DATA([ssl_key.pem], [
> +-----BEGIN RSA PRIVATE KEY-----
> +MIIEpAIBAAKCAQEAxd9ZkUK+sBToEb8x1LSoMfVPw+l4ww915BNn/0TSA7yY+lq/
> +k14+FYHFdqB9vSh5C0bUdhrE7QJ451WP8NyTDIUuZlieUBzonXy0hYVBQ8bUSGfF
> +go/4IWb/tHKIlTiZ86GqaBc8uddiZ7cF2beZwv9un9ZHOGtQrvMdzB7FzmzZraJ1
> +NbAsr1GX4w0euBjPbUyu96nzBeKWqX3vdERK1ddL5y8hA2kSUslci60zCf9/jvcV
> +aSDvcpLwxKq2QYeS8caPWLQmyszyBrdIvsnrTcUPIG+OOs/w3M+KNYKrySF12hKU
> +bUujIrBPt+RJ9ptkv28TYRYqnigRZF3nzNnjewIDAQABAoIBAQCqWh2cZ6APrBAX
> +p0lZXKcpS47+lbQ4CsluMB7qr+829FmnwBUK5KoCjhTYild2UK/VO4eSnn1Hp0c7
> +sngX325h6w8FYen6AslpPIGWKiEEHtuH8n8iZpwy9Z/TVH+uKGqyS06QCuFnBb1c
> +mT9aLy0bqhktVqc+NXXjCL9wilW13pAMh56zYQ8oMJUvNLLknttv9dYmuIyZ9hg/
> +5al4pN7OqzC9N4AAgDjyHIn3N6t6yfBc/kcJ9OdJ1709DYIr6Nl500Nhf+GZkrEX
> +1KTeWPzV6GQD7FUZTJ5eZWNnLbD03823Q0HCElGQkboCUU65EdVdNEHvGdPZw74d
> +t2iaXPxxAoGBAPGvd+PByAH98KKeu9z5ysc0AgVOIDTlqhReqMc2jnYTCglWlMcx
> +al7K0T6QcSTl3Y2xgGo5O7P+MzZkIiKFPx5fX2d9x6CybEcOQ+5bd6fI0hYF8W18
> +i+i/mmJgGo33UOW6EQfYKAHLn4WdlsKJ9aTaNNCanqkqmS1l+GsrRgYdAoGBANGX
> +kl/kfGVYOSRh6ztnRc0i3ONo4RZrDgV0bjEmmujc7V5CIDPxhMUkpsOcG8iCt+55
> +4JqdtLbmfWfeTWWafn8j/Y9DsSv4U0MHc6+Hfit1dmYmZ+ixHjzul9m6H7XsYZ2k
> +IpNa7RN+MqvB66pHGpTcC8IQ2fNIkK3GtWLaA3x3AoGBAOX90xtcZxbuLzax05jf
> +5MZYiau+wwtTmtyzj+2zzzIxwBVO3VoJfm4il6jwD5vLW2Dhj5CGUnhg6R9TfuBW
> +6M/gdounuHcGE+AyhRao2F9EzhfDJBLKuOGOpD4Fsn9y4Psca+SJINlEitO+OZ97
> +ZdWxCR2SZnYZYZdAOHzTu1lJAoGAJcxK9oYzNOerLneGP6lJOkx+P3jLlwppdexg
> +bvbCWxp0qFoOiq+UvST1+jLuA8QnPZe3PMsSKyX4GcJKfPdWtsEb2jlf+0kGYwE2
> +CMLLqzS8zIFCngFLLbvtoLNjQqDFnfNa1O5B8RECPF11jbjS/2OLr0zwsWI1zVEX
> +pyMgG9MCgYBb+PUIVpo5vRMTsyQ5whRQPK7+5jbU+wFKMnryqfsZ/K6DfeoZzAmn
> +KhjeRveOiYEfQZfI5GSfm97TKX5hYKQSwOHa9yzTD25iw4aleU8hc4IbqANzbr/c
> +59KynTtKfZXSb/DAd/FTtFvzfwiin5ss4INKbpuBACDrb+mj1nY6bw==
> +-----END RSA PRIVATE KEY-----
> +])
> +AT_DATA([ssl_cert.pem], [
> +-----BEGIN CERTIFICATE-----
> +MIIDgjCCAmoCAQQwDQYJKoZIhvcNAQEFBQAwgYExCzAJBgNVBAYTAlVTMQswCQYD
> +VQQIEwJDQTEVMBMGA1UEChMMT3BlbiB2U3dpdGNoMREwDwYDVQQLEwhzd2l0Y2hj
> +YTE7MDkGA1UEAxMyT1ZTIHN3aXRjaGNhIENBIENlcnRpZmljYXRlICgyMDE1IEZl
> +YiAwOSAwNjoyNjowMykwHhcNMTUwMzEwMTcxMTEzWhcNMjUwMzA3MTcxMTEzWjCB
> +izELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRUwEwYDVQQKEwxPcGVuIHZTd2l0
> +Y2gxHzAdBgNVBAsTFk9wZW4gdlN3aXRjaCBjZXJ0aWZpZXIxNzA1BgNVBAMTLmNs
> +aWVudCBpZDplMWQxNmQ2MS1jMDcwLTQxOWQtOTYxYi00M2Q4YTBkM2IwMzAwggEi
> +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF31mRQr6wFOgRvzHUtKgx9U/D
> +6XjDD3XkE2f/RNIDvJj6Wr+TXj4VgcV2oH29KHkLRtR2GsTtAnjnVY/w3JMMhS5m
> +WJ5QHOidfLSFhUFDxtRIZ8WCj/ghZv+0coiVOJnzoapoFzy512JntwXZt5nC/26f
> +1kc4a1Cu8x3MHsXObNmtonU1sCyvUZfjDR64GM9tTK73qfMF4papfe90RErV10vn
> +LyEDaRJSyVyLrTMJ/3+O9xVpIO9ykvDEqrZBh5Lxxo9YtCbKzPIGt0i+yetNxQ8g
> +b446z/Dcz4o1gqvJIXXaEpRtS6MisE+35En2m2S/bxNhFiqeKBFkXefM2eN7AgMB
> +AAEwDQYJKoZIhvcNAQEFBQADggEBADmkUbvx9zVtcw1/kSMzr184J3xNBZhmJZ39
> +RBXKR1elZ1aVjc4406vsKZiKKWKQA7rC51xzaLk/huKvUez8RU346GugHtUus05x
> +7MZ2DDiwJNb7hJBBiJ9a0ZyhwCBcQJj95g3686TWYGpc8eEPldACf38SP2etPOmE
> +QPvIMm3pztNf6JZDFDoV3cwzVEFhQnDHXQE9sR6eLhxpbQqwVJ8tmAr9F5hRr4i7
> +SpxHclvZyS+c3nsNb0xluN1Dh8fcy2ITSXfpar+H5B/i1nZA0dBJsW7ChRHAhPQU
> +QYLXEK2+YnilIr00RMv7qa0vF6vC0lXHjGZhm5GzHW2hXDDUIig=
> +-----END CERTIFICATE-----
> +])
> +AT_CHECK([ovs-vsctl set Open_vSwitch . ssl=@N1 -- --id=@N1 create SSL private_key=ssl_key.pem certificate=ssl_cert.pem bootstrap_ca_cert=true], [0], [ignore])
> +])
> +
> +
> +
> +AT_BANNER([ovs-monitor-ipsec (XFRM)])
> +
> +# This test ensures that ovs-monitor-ipsec would properly parse
> +# IPsec policies from kernel.
> +AT_SETUP([Parse "ip xfrm policy" output])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_DATA([sbin/ip], [#!/bin/sh
> +echo 'src 192.168.2.128/32 dst 192.168.2.129/32 proto gre
> +    dir in priority 1794
> +    mark 1/0x1
> +    tmpl src 0.0.0.0 dst 0.0.0.0
> +        proto esp reqid 1 mode transport
> +src 192.168.2.129/32 dst 192.168.2.128/32 proto gre
> +    dir out priority 1794
> +    mark 1/0x1
> +    tmpl src 0.0.0.0 dst 0.0.0.0
> +        proto esp reqid 1 mode transport'
> +])
> +chmod +x sbin/ip
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec xfrm/policies], [0], [dnl
> +{'192.168.2.129': [['src 192.168.2.128/32 dst 192.168.2.129/32 proto gre', 'src 192.168.2.128/32 dst 192.168.2.129/32 proto gre']], '192.168.2.128': [['src 192.168.2.129/32 dst 192.168.2.128/32 proto gre', 'src 192.168.2.129/32 dst 192.168.2.128/32 proto gre']]}
> +])
> +
> +OVS_MONITOR_IPSEC_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would properly parse
> +# IPsec security associations from kernel.
> +AT_SETUP([Parse "ip xfrm state" output])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_DATA([sbin/ip], [#!/bin/sh
> +echo 'src 192.168.2.129 dst 192.168.2.128
> +    proto esp spi 0xcdfe5007 reqid 1 mode transport
> +    replay-window 32
> +    mark 1/0x1
> +    aead rfc4106(gcm(aes)) 0x3d7cb9ddde30c4e6a870b9c1474b8795a85e564d 96
> +    sel src 192.168.2.129/32 dst 192.168.2.128/32
> +src 192.168.2.128 dst 192.168.2.129
> +    proto esp spi 0xca623718 reqid 1 mode transport
> +    replay-window 32
> +    mark 1/0x1
> +    aead rfc4106(gcm(aes)) 0x7c5b209afe3c93adae68f544f5a1326320b15fe5 96
> +    sel src 192.168.2.128/32 dst 192.168.2.129/32'
> +])
> +chmod +x sbin/ip
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec xfrm/state], [0], [dnl
> +{'192.168.2.129': [['sel src 192.168.2.129/32 dst 192.168.2.128/32', 'sel src 192.168.2.128/32 dst 192.168.2.129/32']], '192.168.2.128': [['sel src 192.168.2.129/32 dst 192.168.2.128/32', 'sel src 192.168.2.128/32 dst 192.168.2.129/32']]}
> +])
> +OVS_MONITOR_IPSEC_STOP
> +AT_CLEANUP
> +
> +
> +AT_BANNER([ovs-monitor-ipsec (strongSwan)])
> +
> +# This test ensures that ovs-monitor-ipsec would ignore non-IPsec tunnels.
> +AT_SETUP([Ignore non-IPsec tunnels])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_CHECK([ovs-vsctl add-port br0 gre0 -- set interface gre0 type=gre options:remote_ip=1.2.3.4])
> +# Since ovs-monitor-ipsec does not keep track of non-IPsec tunnels then there
> +# is no proper way to synchronize this.  If ovs-monitor-ipsec did not
> +# ignore non-IPsec tunnels then this test would occasionally fail.
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +No tunnels configured with IPsec
> +])
> +AT_CHECK([ovs-vsctl del-port br0 gre0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would properly parse
> +# output from "ipsec status" command
> +AT_SETUP([Parse "ipsec status" output])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_DATA([active_tunns], [Routed Connections:
> +  stt0-out-2{3}:  ROUTED, TRANSPORT
> +  stt0-out-2{3}:   192.168.2.128/32[[tcp]] === 192.168.2.129/32[[tcp/7471]]
> +   stt0-in-2{2}:  ROUTED, TRANSPORT
> +   stt0-in-2{2}:   192.168.2.128/32[[tcp/7471]] === 192.168.2.129/32[[tcp]]
> +Security Associations (1 up, 0 connecting):
> +   stt0-in-2[[3]]: ESTABLISHED 3 minutes ago, 192.168.2.128[[192.168.2.128]]...192.168.2.129[[192.168.2.129]]
> +   stt0-in-2{2}:  INSTALLED, TRANSPORT, ESP SPIs: cbc3e903_i c29b1e5c_o
> +   stt0-in-2{2}:   192.168.2.128/32[[tcp/7471]] === 192.168.2.129/32[[tcp]]
> +  stt0-out-2{3}:  INSTALLED, TRANSPORT, ESP SPIs: c958d9fb_i c45ce6b0_o
> +  stt0-out-2{3}:   192.168.2.128/32[[tcp]] === 192.168.2.129/32[[tcp/7471]]
> +])
> +
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec ipsec/status], [0], [dnl
> +{'stt0': {'stt0-in-2[[3]]': 'stt0-in-2[[3]]: ESTABLISHED 3 minutes ago, 192.168.2.128[[192.168.2.128]]...192.168.2.129[[192.168.2.129]]:', 'stt0-in-2{2}': 'stt0-in-2{2}:   192.168.2.128/32[[tcp/7471]] === 192.168.2.129/32[[tcp]]:', 'stt0-out-2{3}': 'stt0-out-2{3}:   192.168.2.128/32[[tcp]] === 192.168.2.129/32[[tcp/7471]]:'}}
> +])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +OVS_MONITOR_IPSEC_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would properly configure
> +# ipsec_gre tunnel with Pre-Shared Key
> +AT_SETUP([ipsec_gre with PSK authentication])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_CHECK([ovs-vsctl add-port br0 gre0 -- \
> +                    set interface gre0 type=ipsec_gre \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:psk=swordfish])
> +WAIT_FOR_TUNNEL_TO_UPDATE([gre0], [1], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: gre0 v1 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            swordfish
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  gre0-1:
> +
> +])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : PSK swordfish
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn gre0-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    leftsubnet=%dynamic[[gre]]
> +    rightsubnet=%dynamic[[gre]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port gre0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb gre0-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would properly configure
> +# ipsec_stt tunnel with Pre-Shared Key
> +AT_SETUP([ipsec_stt with PSK authentication])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_CHECK([ovs-vsctl add-port br0 stt0 -- \
> +                    set interface stt0 type=ipsec_stt \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:psk=swordfish])
> +WAIT_FOR_TUNNEL_TO_UPDATE([stt0], [1], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: stt0 v1 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_stt
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            swordfish
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  stt0-in-1:
> +  stt0-out-1:
> +
> +])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : PSK swordfish
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn stt0-in-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    rightsubnet=%dynamic[[tcp/%any]]
> +    leftsubnet=%dynamic[[tcp/7471]]
> +
> +conn stt0-out-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    rightsubnet=%dynamic[[tcp/7471]]
> +    leftsubnet=%dynamic[[tcp/%any]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port stt0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb stt0-in-1
> +ipsec stroke down-nb stt0-out-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would properly configure
> +# ipsec_geneve tunnel with Pre-Shared Key
> +AT_SETUP([ipsec_geneve with PSK authentication])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_CHECK([ovs-vsctl add-port br0 geneve0 -- \
> +                    set interface geneve0 type=ipsec_geneve \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:psk=swordfish])
> +WAIT_FOR_TUNNEL_TO_UPDATE([geneve0], [1], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: geneve0 v1 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_geneve
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            swordfish
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  geneve0-in-1:
> +  geneve0-out-1:
> +
> +])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : PSK swordfish
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn geneve0-in-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    rightsubnet=%dynamic[[udp/%any]]
> +    leftsubnet=%dynamic[[udp/6081]]
> +
> +conn geneve0-out-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    rightsubnet=%dynamic[[udp/6081]]
> +    leftsubnet=%dynamic[[udp/%any]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port geneve0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb geneve0-in-1
> +ipsec stroke down-nb geneve0-out-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would properly configure
> +# ipsec_vxlan tunnel with Pre-Shared Key
> +AT_SETUP([ipsec_vxlan with PSK authentication])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_CHECK([ovs-vsctl add-port br0 vxlan0 -- \
> +                    set interface vxlan0 type=ipsec_vxlan \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:psk=swordfish])
> +WAIT_FOR_TUNNEL_TO_UPDATE([vxlan0], [1], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: vxlan0 v1 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_vxlan
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            swordfish
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  vxlan0-in-1:
> +  vxlan0-out-1:
> +
> +])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : PSK swordfish
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn vxlan0-in-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    rightsubnet=%dynamic[[udp/%any]]
> +    leftsubnet=%dynamic[[udp/4789]]
> +
> +conn vxlan0-out-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    authby=psk
> +    rightsubnet=%dynamic[[udp/4789]]
> +    leftsubnet=%dynamic[[udp/%any]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port vxlan0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb vxlan0-in-1
> +ipsec stroke down-nb vxlan0-out-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec is able to configure IPsec for
> +# a tunnel that wants to reuse Open vSwitch SSL certificates for
> +# authentication.  Note that previous PSK tests already verified geneve,
> +# vxlan and stt IPsec tunneling.  So there is not much benefit of redoing
> +# these kind of tests for those tunneling protocols.
> +AT_SETUP([ipsec_gre with PKI authentication (use_ssl_cert)])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +INIT_SSL_CERTS
> +
> +AT_CHECK([ovs-vsctl add-port br0 gre0 -- \
> +                    set interface gre0 type=ipsec_gre \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:use_ssl_cert=true \
> +                                       options:peer_cert="asd"])
> +WAIT_FOR_TUNNEL_TO_UPDATE([gre0], [1], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: gre0 v1 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   True
> +  My cert:        ssl_cert.pem
> +  My key:         ssl_key.pem
> +  His cert:       asd
> +  PSK:            None
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  gre0-1:
> +
> +])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : RSA ssl_key.pem
> +])
> +AT_CHECK([cat etc/ipsec.d/certs/ovs-gre0.pem], [0], [dnl
> +asd])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn gre0-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    rightcert=ovs-1.2.3.4.pem
> +    leftcert=ssl_cert.pem
> +    leftsubnet=%dynamic[[gre]]
> +    rightsubnet=%dynamic[[gre]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port gre0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb gre0-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec would be able to configure
> +# IPsec tunnel that has it's own Private and Public key (opposed to
> +# reusing Open vSwitch's SSL credentials)
> +AT_SETUP([ipsec_gre with PKI authentication])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +AT_CHECK([ovs-vsctl add-port br0 gre0 -- \
> +                    set interface gre0 type=ipsec_gre \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:private_key="private_key.pem" \
> +                                       options:certificate="public_key.pem" \
> +                                       options:peer_cert="asd"])
> +WAIT_FOR_TUNNEL_TO_UPDATE([gre0], [1], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: gre0 v1 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        public_key.pem
> +  My key:         private_key.pem
> +  His cert:       asd
> +  PSK:            None
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  gre0-1:
> +
> +])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : RSA private_key.pem
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn gre0-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    rightcert=ovs-1.2.3.4.pem
> +    leftcert=public_key.pem
> +    leftsubnet=%dynamic[[gre]]
> +    rightsubnet=%dynamic[[gre]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port gre0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb gre0-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec is able to configure
> +# two IPsec tunnels without interfering with each other.
> +AT_SETUP([ipsec_gre and ipsec_stt to two different hosts])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +
> +AT_CHECK([ovs-vsctl add-port br0 gre0 -- \
> +                    set interface gre0 type=ipsec_gre \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:private_key="private_key1.pem" \
> +                                       options:certificate="public_key1.pem" \
> +                                       options:peer_cert="asd"])
> +AT_CHECK([ovs-vsctl add-port br0 stt0 -- \
> +                    set interface stt0 type=ipsec_stt \
> +                                       options:remote_ip=1.2.3.5 \
> +                                       options:private_key="private_key2.pem" \
> +                                       options:certificate="public_key2.pem" \
> +                                       options:peer_cert="fgh"])
> +WAIT_FOR_TUNNEL_TO_UPDATE([gre0], [1], [CONFIGURED])
> +WAIT_FOR_TUNNEL_TO_UPDATE([stt0], [1], [CONFIGURED])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : RSA private_key1.pem
> +1.2.3.5 : RSA private_key2.pem
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn gre0-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    rightcert=ovs-1.2.3.4.pem
> +    leftcert=public_key1.pem
> +    leftsubnet=%dynamic[[gre]]
> +    rightsubnet=%dynamic[[gre]]
> +
> +conn stt0-in-1
> +    left=0.0.0.0
> +    right=1.2.3.5
> +    rightcert=ovs-1.2.3.5.pem
> +    leftcert=public_key2.pem
> +    rightsubnet=%dynamic[[tcp/%any]]
> +    leftsubnet=%dynamic[[tcp/7471]]
> +
> +conn stt0-out-1
> +    left=0.0.0.0
> +    right=1.2.3.5
> +    rightcert=ovs-1.2.3.5.pem
> +    leftcert=public_key2.pem
> +    rightsubnet=%dynamic[[tcp/7471]]
> +    leftsubnet=%dynamic[[tcp/%any]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port gre0])
> +AT_CHECK([ovs-vsctl del-port stt0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb gre0-1
> +ipsec update
> +ipsec stroke down-nb stt0-in-1
> +ipsec stroke down-nb stt0-out-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec is able to configure two
> +# IPsec tunnels simulatenously to the same remote_ip.
> +AT_SETUP([ipsec_gre and ipsec_stt to the same host])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +
> +AT_CHECK([ovs-vsctl add-port br0 gre0 -- \
> +                    set interface gre0 type=ipsec_gre \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:private_key="private_key1.pem" \
> +                                       options:certificate="public_key1.pem" \
> +                                       options:peer_cert="asd"])
> +AT_CHECK([ovs-vsctl add-port br0 stt0 -- \
> +                    set interface stt0 type=ipsec_stt \
> +                                       options:remote_ip=1.2.3.4 \
> +                                       options:private_key="private_key2.pem" \
> +                                       options:certificate="public_key2.pem" \
> +                                       options:peer_cert="fgh"])
> +WAIT_FOR_TUNNEL_TO_UPDATE([stt0], [1], [CONFIGURED])
> +WAIT_FOR_TUNNEL_TO_UPDATE([stt0], [1], [CONFIGURED])
> +AT_CHECK([cat etc/ipsec.secrets], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +1.2.3.4 : RSA private_key1.pem
> +1.2.3.4 : RSA private_key2.pem
> +])
> +AT_CHECK([cat etc/ipsec.conf], [0], [dnl
> +# Generated by ovs-monitor-ipsec...do not modify by hand!
> +
> +
> +config setup
> +    uniqueids=no
> +
> +conn %default
> +    keyingtries=%forever
> +    type=transport
> +    keyexchange=ikev2
> +    auto=route
> +    mark_in=1/1
> +    ike=aes128gcm12-aesxcbc-modp1024
> +    esp=aes128gcm12-modp1024
> +
> +conn gre0-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    rightcert=ovs-1.2.3.4.pem
> +    leftcert=public_key1.pem
> +    leftsubnet=%dynamic[[gre]]
> +    rightsubnet=%dynamic[[gre]]
> +
> +conn stt0-in-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    rightcert=ovs-1.2.3.4.pem
> +    leftcert=public_key2.pem
> +    rightsubnet=%dynamic[[tcp/%any]]
> +    leftsubnet=%dynamic[[tcp/7471]]
> +
> +conn stt0-out-1
> +    left=0.0.0.0
> +    right=1.2.3.4
> +    rightcert=ovs-1.2.3.4.pem
> +    leftcert=public_key2.pem
> +    rightsubnet=%dynamic[[tcp/7471]]
> +    leftsubnet=%dynamic[[tcp/%any]]
> +
> +])
> +AT_CHECK([ovs-vsctl del-port gre0])
> +AT_CHECK([ovs-vsctl del-port stt0])
> +
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb gre0-1
> +ipsec update
> +ipsec stroke down-nb stt0-in-1
> +ipsec stroke down-nb stt0-out-1
> +])
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +
> +# This test ensures that ovs-monitor-ipsec can properly update IPsec tunnel's
> +# configuration by calling "ipsec update" and "ipsec stroke down-nb"
> +# commands whenver OVSDB configuration changes.
> +AT_SETUP([Sequence of IPsec tunnel reconfiguration events])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +AT_SKIP_IF([$non_ascii_cwd])
> +OVS_VSWITCHD_START([])
> +OVS_MONITOR_IPSEC_START
> +
> +# Start with tunnel that has 'type' set to 'ipsec_stt' and nothing else
> +ovs-vsctl add-port br0 tun0 -- set interface tun0 type=ipsec_stt
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [1], [INVALID])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v1 (INVALID: 'remote_ip' is not set)
> +  Remote IP:      None
> +  Tunnel Type:    ipsec_stt
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            None
> +  Ofport:         Not assigned
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +
> +])
> +
> +ovs-vsctl set interface tun0 options:remote_ip="1.2.3.4"
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [2], [INVALID])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v2 (INVALID: must set either 'psk' or 'peer_cert')
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_stt
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            None
> +  Ofport:         Not assigned
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +
> +])
> +
> +ovs-vsctl set interface tun0 options:psk=swordfish
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [3], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v3 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_stt
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            swordfish
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  tun0-out-3:
> +  tun0-in-3:
> +
> +])
> +
> +
> +ovs-vsctl set interface tun0 type=ipsec_gre
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [4], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v4 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       None
> +  PSK:            swordfish
> +  Ofport:         1
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  tun0-4:
> +
> +])
> +
> +
> +ovs-vsctl remove interface tun0 options psk --\
> +          set Interface tun0 options:peer_cert="asd"
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [5], [INVALID])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v5 (INVALID: must set 'certificate' with PKI)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        None
> +  My key:         None
> +  His cert:       asd
> +  PSK:            None
> +  Ofport:         Not assigned
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +
> +])
> +
> +
> +ovs-vsctl set Interface tun0 options:certificate="my_cert.pem"
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [6], [INVALID])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v6 (INVALID: must set 'private_key' with PKI)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        my_cert.pem
> +  My key:         None
> +  His cert:       asd
> +  PSK:            None
> +  Ofport:         2
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +
> +])
> +
> +
> +ovs-vsctl set Interface tun0 options:private_key="my_key.pem"
> +WAIT_FOR_TUNNEL_TO_UPDATE([tun0], [7], [CONFIGURED])
> +AT_CHECK([ovs-appctl -t ovs-monitor-ipsec tunnels/show], [0], [dnl
> +Interface name: tun0 v7 (CONFIGURED)
> +  Remote IP:      1.2.3.4
> +  Tunnel Type:    ipsec_gre
> +  Local IP:       0.0.0.0
> +  Use SSL cert:   False
> +  My cert:        my_cert.pem
> +  My key:         my_key.pem
> +  His cert:       asd
> +  PSK:            None
> +  Ofport:         2
> +  CFM state:      Disabled
> +Kernel policies installed:
> +Kernel security associations installed:
> +Strongswan connections that are active:
> +  tun0-7:
> +
> +])
> +
> +AT_CHECK([ovs-vsctl del-port tun0])
> +WAIT_FOR_PROPER_IPSEC_CLEANUP
> +VERIFY_IPSEC_COMMANDS_EXECUTED([dnl
> +ipsec restart
> +ipsec update
> +ipsec update
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb tun0-in-3
> +ipsec stroke down-nb tun0-out-3
> +ipsec update
> +ipsec stroke down-nb tun0-4
> +ipsec update
> +ipsec update
> +ipsec update
> +ipsec stroke down-nb tun0-7
> +])
> +# For this test all errors in OVS logs that contain 'netdev' must be ignored
> +# because ovs-vswitchd also does basic validation
> +OVS_MONITOR_IPSEC_STOP
> +OVS_VSWITCHD_STOP(["/netdev/d"])
> +AT_CLEANUP
> diff --git a/tests/testsuite.at b/tests/testsuite.at
> index 15c385e2c..23b7defbd 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -55,6 +55,7 @@ m4_include([tests/ovs-router.at])
>  m4_include([tests/lockfile.at])
>  m4_include([tests/reconnect.at])
>  m4_include([tests/ovs-vswitchd.at])
> +m4_include([tests/ovs-monitor-ipsec.at])
>  m4_include([tests/ofproto.at])
>  m4_include([tests/dpif-netdev.at])
>  m4_include([tests/pmd.at])
> diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
> index b93da69bd..205578c87 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -473,8 +473,15 @@
>            OpenFlow specification mandates the timeout to be at least one
>            second. The default is 10 seconds.
>          </p>
> -    </column>
> -
> +      </column>
> +      <column name="other_config" key="ipsec_default_mark"
> +              type='{"type": "string"}'>
> +        <p>
> +          Defines the value and mask of SKB mark to use in IPsec shunt
> +          policies.  There are three packet forwarding modes in which
> +          IPsec could operate.

Would be good to list the format of this option.  "Value and mask"
usually implies parts.

"There are three packet forwarding modes" - does it make sense to
describe the selection of those modes here?

> +        </p>
> +      </column>
>      </group>
>  
>      <group title="Status">
> @@ -1379,7 +1386,7 @@
>        <column name="external_ids"/>
>      </group>
>    </table>
> -  
> +
>    <table name="Port" table="Port or bond configuration.">
>      <p>A port within a <ref table="Bridge"/>.</p>
>      <p>Most commonly, a port has exactly one ``interface,'' pointed to by its
> @@ -2625,7 +2632,7 @@
>  
>          <column name="options" key="csum" type='{"type": "boolean"}'>
>            <p>
> -            Optional.  Compute encapsulation header (either GRE or UDP) 
> +            Optional.  Compute encapsulation header (either GRE or UDP)
>              checksums on outgoing packets.  Default is disabled, set to
>              <code>true</code> to enable.  Checksums present on incoming
>              packets will be validated regardless of this setting.
> @@ -2643,6 +2650,74 @@
>  
>          </column>
>        </group>
> +
> +      <group title="Tunnel Options: IPsec">
> +        <p>
> +          <code>gre</code>, <code>geneve</code>, and
> +          <code>vxlan</code> interfaces support these options.
> +        </p>
> +
> +        <column name="options" key="psk" type='{"type": "boolean"}'>
> +          <p>
> +            The preshared secret to negotiate tunnel in PSK mode.
> +            Must be unset when peer_cert is set.
> +          </p>
> +
> +          <p>
> +            This secret gets populated by ovs-monitor-ipsec daemon
> +            in /etc/ipsec.secrets file.  This value must match on both
> +            tunnel ends, because otherwise authentication failure will be
> +            triggered in /var/log/auth.log file by strongSwan.
> +          </p>
> +
> +        </column>
> +
> +        <column name="options" key="peer_cert" type='{"type": "boolean"}'>
> +          <p>
> +            The public key of remote peer to negotiate tunnel in PKI mode.
> +            Must be unset when psk is set.
> +          </p>
> +
> +          <p>
> +            The ovs-monitor-ipsec daemon creates a file with content of this
> +            value under /etc/ipsec.d/certs directory and then references it
> +            from /etc/ipsec.secrets file.  This value must be set either to
> +            peer's public certificate contents itself or to the Certificate
> +            Authority of that peer.
> +          </p>
> +
> +        </column>
> +
> +        <column name="options" key="private_key" type='{"type": "boolean"}'>
> +          <p>
> +            The private key of this peer to negotiate tunnel in PKI mode.
> +            This file must never ever be leaked, because anyone who posses
> +            it can negotiate tunnel.

This is awkwardly phrased.

> +            Must be unset when psk is set.
> +          </p>
> +
> +          <p>
> +            ...
> +          </p>
> +
> +        </column>
> +
> +        <column name="options" key="certificate" type='{"type": "boolean"}'>
> +          <p>
> +            The public key of this peer to negotiate tunnel in PKI mode.
> +            This file is exhanged over wire during IKE negotiation phase.

s/exhanged/exchanged/

> +            Hence it is not a secret.
> +            Must be unset when psk is set.
> +          </p>
> +
> +          <p>
> +            ...
> +          </p>
> +
> +        </column>
> +
> +      </group>
> +
>      </group>
>  
>      <group title="Tunnel Options: erspan only">


More information about the dev mailing list