diff mbox series

[ovs-dev,v3] ovn-controller: Add 'local_ip' option to tunnel ports for IPsec case

Message ID 20210216115533.1586837-1-mark.d.gray@redhat.com
State New
Headers show
Series [ovs-dev,v3] ovn-controller: Add 'local_ip' option to tunnel ports for IPsec case | expand

Commit Message

Mark Gray Feb. 16, 2021, 11:55 a.m. UTC
If a chassis has multiple interfaces, 'ovn-encap-ip' can be used
to specify the IP address of the interface that is used for tunnel
traffic. OVN uses that IP address to configure the 'remote_ip' of
a tunnel port. OVS tunnel ports also accept 'options:local_ip', which,
according to the OVS documentation specifies "the tunnel destination
IP that received packets must match. Default is to match all addresses".
OVN does not set 'local_ip'.

'ovs-monitor-ipsec' is an OVS daemon that is used to configure and IPsec
IKE daemon on the host. In order to correctly specify an IPsec
connection, it requires the source and destination IP address of
that connection. In the OVN case, as 'local_ip' is not specified, it
is unable to infer the IP address of both sides of a tunnel and, therefore,
cannot setup an IPsec connection.

This patch configures 'local_ip' on tunnel ports when IPsec has
been enabled. This allows for OVS/OVN IPsec to work when 'ovn-encap-ip'
is not specified as the chassis default gateway interface.

This patch also adds some unit tests. The OVS daemon 'ovs-monitor-ipsec'
requires a number of options to be configured on OVS tunnel ports in order
to function correctly. These unit tests ensure that these options are
configured correctly when IPsec has been enabled through the northbound
database.

Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1924041
Signed-off-by: Mark Gray <mark.d.gray@redhat.com>
---

v2: Updated topic filter to "PATCH ovn"
v3: Rebased due to 0-day bot warning

 controller/chassis.c |   5 +++
 controller/encaps.c  |  26 ++++++++++-
 tests/automake.mk    |   3 +-
 tests/ovn-ipsec.at   | 104 +++++++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at   |   1 +
 5 files changed, 137 insertions(+), 2 deletions(-)
 create mode 100644 tests/ovn-ipsec.at
diff mbox series

Patch

diff --git a/controller/chassis.c b/controller/chassis.c
index 310132d09d2e..9b0a36cf076f 100644
--- a/controller/chassis.c
+++ b/controller/chassis.c
@@ -279,6 +279,11 @@  chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table *ovs_table,
         return false;
     }
 
+    /* 'ovn-encap-ip' can accept a comma-delimited list of IP addresses instead
+     * of a single IP address. Although this is undocumented, it can be used
+     * to enable certain hardware-offloaded use cases in which a host has
+     * multiple NICs and is assigning SR-IOV VFs to a guest (as logical ports).
+     */
     if (!chassis_parse_ovs_encap_ip(encap_ips, &ovs_cfg->encap_ip_set)) {
         sset_destroy(&ovs_cfg->encap_type_set);
         return false;
diff --git a/controller/encaps.c b/controller/encaps.c
index 7eac4bb064ac..fc93bf1eeb87 100644
--- a/controller/encaps.c
+++ b/controller/encaps.c
@@ -59,6 +59,7 @@  struct tunnel_ctx {
 
     struct ovsdb_idl_txn *ovs_txn;
     const struct ovsrec_bridge *br_int;
+    const struct sbrec_chassis *this_chassis;
 };
 
 struct chassis_node {
@@ -176,6 +177,28 @@  tunnel_add(struct tunnel_ctx *tc, const struct sbrec_sb_global *sbg,
 
     /* Add auth info if ipsec is enabled. */
     if (sbg->ipsec) {
+        const struct sbrec_chassis *this_chassis = tc->this_chassis;
+        const char *local_ip = NULL;
+
+        /* Determine 'ovn-encap-ip' of the local chassis as this will be the
+         * tunnel port's 'local_ip'. We do not support the case in which
+         * 'ovn-encap-ip' holds multiple comma-delimited IP addresses.
+         */
+        for (int i = 0; i < this_chassis->n_encaps; i++) {
+            if (local_ip && strcmp(local_ip, this_chassis->encaps[i]->ip)) {
+                VLOG_ERR("ovn-encap-ip has been configured as a list. This "
+                         "is unsupported for IPsec.");
+                /* No need to loop further as we know this condition has been
+                 * hit */
+                break;
+            } else {
+                local_ip = this_chassis->encaps[i]->ip;
+            }
+        }
+
+        if (local_ip) {
+            smap_add(&options, "local_ip", local_ip);
+        }
         smap_add(&options, "remote_name", new_chassis_id);
     }
 
@@ -310,7 +333,8 @@  encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
     struct tunnel_ctx tc = {
         .chassis = SHASH_INITIALIZER(&tc.chassis),
         .port_names = SSET_INITIALIZER(&tc.port_names),
-        .br_int = br_int
+        .br_int = br_int,
+        .this_chassis = this_chassis
     };
 
     tc.ovs_txn = ovs_idl_txn;
diff --git a/tests/automake.mk b/tests/automake.mk
index df6d0a2a9074..bef40bde07bb 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -34,7 +34,8 @@  TESTSUITE_AT = \
 	tests/ovn-performance.at \
 	tests/ovn-ofctrl-seqno.at \
 	tests/ovn-ipam.at \
-	tests/ovn-lflow-cache.at
+	tests/ovn-lflow-cache.at \
+	tests/ovn-ipsec.at
 
 SYSTEM_KMOD_TESTSUITE_AT = \
 	tests/system-common-macros.at \
diff --git a/tests/ovn-ipsec.at b/tests/ovn-ipsec.at
new file mode 100644
index 000000000000..887281d5be0e
--- /dev/null
+++ b/tests/ovn-ipsec.at
@@ -0,0 +1,104 @@ 
+AT_BANNER([OVN - IPsec])
+
+AT_SETUP([ovn -- ipsec -- basic configuration])
+ovn_start
+
+# Configure the Northbound database
+ovn-nbctl ls-add lsw0
+
+ovn-nbctl lsp-add lsw0 lp1
+ovn-nbctl lsp-set-addresses lp1 "f0:00:00:00:00:01 10.1.1.1"
+
+ovn-nbctl lsp-add lsw0 lp2
+ovn-nbctl lsp-set-addresses lp2 "f0:00:00:00:00:02 10.1.1.2"
+
+net_add n1               # Network to connect hv1 and hv2
+
+# Enable IPsec
+ovn-nbctl set nb_global . ipsec=true
+
+# Create hypervisor hv1 connected to n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1
+ovs-vsctl \
+    -- set Open_vSwitch . external-ids:system-id=hv1 \
+    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip=192.168.0.1 \
+    -- set Open_vSwitch . other_config:certificate=dummy-cert.pem \
+    -- set Open_vSwitch . other_config:private_key=dummy-privkey.pem \
+    -- set Open_vSwitch . other_config:ca_cert=dummy-cacert.pem
+
+# Create hypervisor hv2 connected to n1
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2
+ovs-vsctl \
+    -- set Open_vSwitch . external-ids:system-id=hv2 \
+    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip=192.168.0.2 \
+    -- set Open_vSwitch . other_config:certificate=dummy-cert.pem \
+    -- set Open_vSwitch . other_config:private_key=dummy-privkey.pem \
+    -- set Open_vSwitch . other_config:ca_cert=dummy-cacert.pem
+
+AT_CHECK([as hv2 ovs-vsctl get Interface ovn-hv1-0 options:remote_ip | tr -d '"\n'], [0], [192.168.0.1])
+AT_CHECK([as hv2 ovs-vsctl get Interface ovn-hv1-0 options:local_ip | tr -d '"\n'], [0], [192.168.0.2])
+AT_CHECK([as hv2 ovs-vsctl get Interface ovn-hv1-0 options:remote_name | tr -d '\n'], [0], [hv1])
+AT_CHECK([as hv1 ovs-vsctl get Interface ovn-hv2-0 options:remote_ip | tr -d '"\n'], [0], [192.168.0.2])
+AT_CHECK([as hv1 ovs-vsctl get Interface ovn-hv2-0 options:local_ip | tr -d '"\n'], [0], [192.168.0.1])
+AT_CHECK([as hv1 ovs-vsctl get Interface ovn-hv2-0 options:remote_name | tr -d '\n'], [0], [hv2])
+
+AT_CLEANUP
+
+AT_SETUP([ovn -- ipsec -- unsupported multiple ovn-encap-ip values])
+ovn_start
+
+# Configure the Northbound database
+ovn-nbctl ls-add lsw0
+
+ovn-nbctl lsp-add lsw0 lp1
+ovn-nbctl lsp-set-addresses lp1 "f0:00:00:00:00:01 10.1.1.1"
+
+ovn-nbctl lsp-add lsw0 lp2
+ovn-nbctl lsp-set-addresses lp2 "f0:00:00:00:00:02 10.1.1.2"
+
+net_add n1               # Network to connect hv1 and hv2
+
+# Enable IPsec
+ovn-nbctl set nb_global . ipsec=true
+
+# Create hypervisor hv1 connected to n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1
+ovs-vsctl \
+    -- set Open_vSwitch . external-ids:system-id=hv1 \
+    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip="192.168.0.1, 192.169.0.1" \
+    -- set Open_vSwitch . other_config:certificate=dummy-cert.pem \
+    -- set Open_vSwitch . other_config:private_key=dummy-privkey.pem \
+    -- set Open_vSwitch . other_config:ca_cert=dummy-cacert.pem
+
+# Create hypervisor hv2 connected to n1
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2
+ovs-vsctl \
+    -- set Open_vSwitch . external-ids:system-id=hv2 \
+    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip="192.168.0.2, 192.169.0.2" \
+    -- set Open_vSwitch . other_config:certificate=dummy-cert.pem \
+    -- set Open_vSwitch . other_config:private_key=dummy-privkey.pem \
+    -- set Open_vSwitch . other_config:ca_cert=dummy-cacert.pem
+
+AT_CHECK([grep "ovn-encap-ip has been configured as a list. This is unsupported for IPsec." hv1/ovn-controller.log],[0], ignore)
+
+AT_CLEANUP
\ No newline at end of file
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 35908ad3f3fe..ddc3f11d6850 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -36,3 +36,4 @@  m4_include([tests/ovn-controller.at])
 m4_include([tests/ovn-controller-vtep.at])
 m4_include([tests/ovn-ic.at])
 m4_include([tests/checkpatch.at])
+m4_include([tests/ovn-ipsec.at])