Propagate the VNI option from LS to the datapath, this is the last
missing piece required to activate all the code around EVPN in
ovn-controller. The LS is considered to be part of EVPN when
the "dynamic-routing-vni" is set to valid integer.

Reported-at: https://issues.redhat.com/browse/FDP-1385
Signed-off-by: Ales Musil <amu...@redhat.com>
Acked-by: Xavier Simonart <xsimo...@redhat.com>
---
v3: Rebase on top of latest main.
    Add Xavier's ack.

v2: Rebase on top of latest main.
    Add system test.
    Fix typo in documentation.
---
 NEWS                |   2 +
 northd/northd.c     |   6 ++
 ovn-nb.xml          |  20 +++++
 tests/system-ovn.at | 196 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 224 insertions(+)

diff --git a/NEWS b/NEWS
index 6aef31fa4..9a7e2c8a8 100644
--- a/NEWS
+++ b/NEWS
@@ -52,6 +52,8 @@ Post v25.03.0
      * Add the option "external_ids:ovn-evpn-local-ip" into OvS datapath.
        This option allows CMS to set IP address that will be used as source
        for the EVPN traffic egressing through the tunnel.
+     * Add the "other_config:dynamic-routing-vni" to Logical Switches. If set
+       to valid integer the LS is considered to be connected to EVPN L2 domain.
 
 OVN v25.03.0 - 07 Mar 2025
 --------------------------
diff --git a/northd/northd.c b/northd/northd.c
index 2cb69f9aa..f02801f48 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -811,6 +811,12 @@ ovn_datapath_update_external_ids(struct ovn_datapath *od)
             smap_add_format(&ids, "fdb_age_threshold",
                             "%u", age_threshold);
         }
+
+        int64_t vni = ovn_smap_get_llong(&od->nbs->other_config,
+                                         "dynamic-routing-vni", -1);
+        if (ovn_is_valid_vni(vni)) {
+            smap_add_format(&ids, "dynamic-routing-vni", "%"PRIi64, vni);
+        }
     }
 
     /* Set snat-ct-zone */
diff --git a/ovn-nb.xml b/ovn-nb.xml
index cbe9c40bb..c7c1fd6c7 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -866,6 +866,26 @@
         dropped, even if use_ct_inv_match is set to true.
         Default: <code>false</code>.
       </column>
+
+      <column name="other_config" key="dynamic-routing-vni"
+              type='{"type": "integer", "minInteger": 0,
+                     "maxInteger": 16777215}'>
+        <p>
+          This defines the vni number associated with EVPN domain that the
+          Logical Switch is supposed to connect to.
+        </p>
+
+        <p>
+          The ovn-controller expects three interfaces to exist within the
+          BGP vrf: <code>br-$vni</code>, <code>lo-$vni</code> and
+          <code>vxlan-$vni</code>.
+        </p>
+
+        <p>
+          NOTE: this feature is experimental and may be subject to
+          removal/change in the future.
+        </p>
+      </column>
     </group>
 
     <group title="IP Multicast Snooping Options">
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 066703418..9c1e0e0dd 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -18251,3 +18251,199 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port 
patch-.*/d
 /connection dropped.*/d"])
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([dynamic-routing - EVPN])
+AT_KEYWORDS([dynamic-routing])
+
+CHECK_VRF()
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+
+vni=10
+VRF_RESERVE([$vni])
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone])
+
+# Set external-ids in br-int needed for ovn-controller.
+check ovs-vsctl \
+    -- set Open_vSwitch . external-ids:system-id=hv1 \
+    -- set Open_vSwitch . 
external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+    -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+    -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+    -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext \
+    -- set Open_vSwitch . external_ids:ovn-evpn-local-ip=169.0.0.1 \
+    -- set Open_vSwitch . external_ids:ovn-evpn-vxlan-ports=4789 \
+    -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller.
+start_daemon ovn-controller
+
+OVS_WAIT_WHILE([ip link | grep -q ovnvrf$vni:.*UP])
+
+check ovn-nbctl                                                    \
+    -- ls-add ls-evpn                                              \
+    -- lsp-add ls-evpn workload1                                   \
+    -- lsp-set-addresses workload1 "f0:00:0f:16:01:10 172.16.1.10" \
+    -- lsp-add ls-evpn workload2                                   \
+    -- lsp-set-addresses workload2 "f0:00:0f:16:01:20 172.16.1.20" \
+    -- lsp-add ls-evpn ln_port                                     \
+    -- lsp-set-addresses ln_port unknown                           \
+    -- lsp-set-type ln_port localnet                               \
+    -- lsp-set-options ln_port network_name=phynet
+
+ADD_NAMESPACES(workload1)
+ADD_VETH(workload1, workload1, br-int, "172.16.1.1/24", "f0:00:0f:16:01:10", \
+         "172.16.1.10")
+
+ADD_NAMESPACES(workload2)
+ADD_VETH(workload2, workload2, br-int, "172.16.1.1/24", "f0:00:0f:16:01:20", \
+         "172.16.1.20")
+
+OVN_POPULATE_ARP
+check ovn-nbctl --wait=hv sync
+wait_for_ports_up
+
+# Setup a VRF for the VNI.
+check ip link add vrf-$vni type vrf table $vni
+on_exit "ip link del vrf-$vni"
+check ip link set vrf-$vni up
+
+# Add VNI bridge.
+check ip link add br-$vni type bridge
+on_exit "ip link del br-$vni"
+check ip link set br-$vni master vrf-$vni addrgenmode none
+check ip link set dev br-$vni up
+
+# Add VXLAN VTEP for the VNI (linked to the OVS vxlan_sys_<port> interface).
+# Use a dstport different than the one used by OVS.
+# This is fine because we don't actually want traffic to pass through
+# the $vxlan interface.  FRR should read the dstport from the linked
+# vxlan_sys_${vxlan_port} device.
+dstport=$((60000 + $vni))
+check ip link add vxlan-$vni type vxlan \
+    id $vni dstport $dstport local 169.0.0.1 nolearning
+on_exit "ip link del vxlan-$vni"
+check ip link set dev vxlan-$vni up
+check ip link set vxlan-$vni master br-$vni
+
+# Add a dummy loopback to the VNI bridge to be used for advertising local
+# MACs.
+check ip link add name lo-$vni type dummy
+on_exit "ip link del lo-$vni"
+check ip link set lo-$vni master br-$vni
+check ip link set lo-$vni up
+
+check ovn-nbctl --wait=hv set logical_switch ls-evpn 
other_config:dynamic-routing-vni=$vni
+ofport=$(ovs-vsctl --bare --columns ofport find Interface name="ovn-evpn-4789")
+dp_key=$(fetch_column Datapath tunnel_key external_ids:name=ls-evpn)
+
+AT_CHECK([ovn-appctl evpn/remote-vtep-list], [0], [dnl
+])
+
+AT_CHECK([ovn-appctl evpn/vtep-binding-list], [0], [dnl
+])
+
+AT_CHECK([ovn-appctl evpn/vtep-multicast-group-list], [0], [dnl
+])
+
+# Simulate remote VTEP.
+check bridge fdb append 00:00:00:00:00:00 dev vxlan-$vni dst 169.0.0.10 static 
permanent
+
+OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/remote-vtep-list], [0], [dnl
+IP: 169.0.0.10, port: 4789, vni: $vni
+])
+
+OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-binding-list | cut -d',' 
-f2-], [0], [dnl
+ Remote IP: 169.0.0.10, vni: $vni, binding_key: 0x80000001, tunnel_ofport: 
$ofport, dp_key: $dp_key
+])
+
+OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-multicast-group-list | cut 
-d',' -f2-], [0], [dnl
+ Remote IPs: 169.0.0.10, vni: $vni
+])
+
+AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG | grep 
priority=1050 | \
+                   awk '{print $7, $8}' | sort], [0], [dnl
+priority=1050,tun_id=0xa,tun_src=169.0.0.10,tun_dst=169.0.0.1,in_port=$ofport 
actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000001->NXM_NX_REG14[[]],resubmit(,OFTABLE_LOG_INGRESS_PIPELINE)
+])
+
+AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int 
table=OFTABLE_REMOTE_VTEP_OUTPUT | grep output | \
+                   awk '{print $7, $8}' | sort], [0], [dnl
+priority=50,reg15=0x8000,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT)
+priority=50,reg15=0x80000001,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport
+priority=50,reg15=0x8001,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT)
+priority=50,reg15=0x8004,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT)
+priority=55,reg10=0x1/0x1,reg15=0x80000001,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport
+])
+
+# Simulate second remote VTEP.
+check bridge fdb append 00:00:00:00:00:00 dev vxlan-$vni dst 169.0.0.20 static 
permanent
+
+OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/remote-vtep-list | sort], [0], 
[dnl
+IP: 169.0.0.10, port: 4789, vni: $vni
+IP: 169.0.0.20, port: 4789, vni: $vni
+])
+
+OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-binding-list | cut -d',' 
-f2- | sort], [0], [dnl
+ Remote IP: 169.0.0.10, vni: $vni, binding_key: 0x80000001, tunnel_ofport: 
$ofport, dp_key: $dp_key
+ Remote IP: 169.0.0.20, vni: $vni, binding_key: 0x80000002, tunnel_ofport: 
$ofport, dp_key: $dp_key
+])
+
+# We cannot check the output directly because the order might change.
+OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q 
"169.0.0.10"])
+OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q 
"169.0.0.20"])
+AT_CHECK([ovn-appctl evpn/vtep-multicast-group-list | wc -l], [0], [1
+])
+
+AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG | grep 
priority=1050 | \
+                   awk '{print $7, $8}' | sort], [0], [dnl
+priority=1050,tun_id=0xa,tun_src=169.0.0.10,tun_dst=169.0.0.1,in_port=$ofport 
actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000001->NXM_NX_REG14[[]],resubmit(,OFTABLE_LOG_INGRESS_PIPELINE)
+priority=1050,tun_id=0xa,tun_src=169.0.0.20,tun_dst=169.0.0.1,in_port=$ofport 
actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000002->NXM_NX_REG14[[]],resubmit(,OFTABLE_LOG_INGRESS_PIPELINE)
+])
+
+ovs-ofctl dump-flows br-int table=OFTABLE_REMOTE_VTEP_OUTPUT > 
oftable_remote_vtep_output
+AT_CHECK_UNQUOTED([grep "output" oftable_remote_vtep_output | grep -v resubmit 
| \
+                   awk '{print $7, $8}' | sort], [0], [dnl
+priority=50,reg15=0x80000001,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport
+priority=50,reg15=0x80000002,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa9000014->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport
+priority=55,reg10=0x1/0x1,reg15=0x80000001,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport
+priority=55,reg10=0x1/0x1,reg15=0x80000002,metadata=0x$dp_key 
actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa9000014->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport
+])
+
+AT_CHECK([grep "resubmit" oftable_remote_vtep_output | grep -c 
"load:0xa900000a"], [0], [3
+])
+AT_CHECK([grep "resubmit" oftable_remote_vtep_output | grep -c 
"load:0xa9000014"], [0], [3
+])
+
+# Check that the recompute won't change the UUIDs and tunnel keys.
+ovn-appctl evpn/vtep-binding-list > bindings_before
+ovn-appctl evpn/vtep-multicast-group-list > mc_groups_before
+
+check ovn-appctl inc-engine/recompute
+check ovn-nbctl --wait=hv sync
+
+ovn-appctl evpn/vtep-binding-list > bindings_after
+ovn-appctl evpn/vtep-multicast-group-list > mc_groups_after
+
+check diff -q bindings_before bindings_after
+check diff -q mc_groups_before mc_groups_after
+
+OVN_CLEANUP_CONTROLLER([hv1])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/Failed to acquire.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
-- 
2.50.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to