From patchwork Wed Jul 24 16:01:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 1964350 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WTf0G61tkz1ybY for ; Thu, 25 Jul 2024 02:01:49 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id C010F40327; Wed, 24 Jul 2024 16:01:46 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id gtl7gVRT-mK0; Wed, 24 Jul 2024 16:01:43 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 01E6B40192 Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 01E6B40192; Wed, 24 Jul 2024 16:01:43 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id D121EC0A97; Wed, 24 Jul 2024 16:01:42 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 01EFCC0A96 for ; Wed, 24 Jul 2024 16:01:41 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id E65D960781 for ; Wed, 24 Jul 2024 16:01:40 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id urSK0cLVFOUn for ; Wed, 24 Jul 2024 16:01:38 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=217.70.183.201; helo=relay8-d.mail.gandi.net; envelope-from=numans@ovn.org; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 3DE9A6061E Authentication-Results: smtp3.osuosl.org; dmarc=none (p=none dis=none) header.from=ovn.org DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 3DE9A6061E Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by smtp3.osuosl.org (Postfix) with ESMTPS id 3DE9A6061E for ; Wed, 24 Jul 2024 16:01:37 +0000 (UTC) Received: by mail.gandi.net (Postfix) with ESMTPSA id 7053B1BF20A; Wed, 24 Jul 2024 16:01:35 +0000 (UTC) From: numans@ovn.org To: dev@openvswitch.org Date: Wed, 24 Jul 2024 12:01:30 -0400 Message-ID: <20240724160130.551695-1-numans@ovn.org> X-Mailer: git-send-email 2.45.2 MIME-Version: 1.0 X-GND-Sasl: numans@ovn.org Subject: [ovs-dev] [PATCH ovn v2] provider networks: Provide the option to tunnel traffic. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" From: Numan Siddique This patch adds a global config option - 'always_tunnel' and when set to true, any traffic destined to a VIF logical port of a provider logical switch (having localnet port(s)), is tunnelled to the destination chassis, instead of sending it out via the localnet port. This feature is useful for the following reasons: 1. CMS can add both provider logical switches and overlay logical swithes to a logical router. With this option set, E-W routing between these logical switches will be tunnelled all the time. The router port mac addresses are not leaked from multiple chassis to the upstream switches anymore. 2. NATting will work as expected either in the gateway chassis or on the source VIF chassis (if external_mac and logical_port set). 3. With this option set, there is no need to centralize routing for provider logical switches ('reside-on-redirect-chassis'). 4. With the commits [1] now merged, MTU issues arising due to tunnel overhead will be handled gracefully. [1] - 3faadc76ad71 ("northd: Fix pmtud for non routed traffic.") 221476a01f26 ("ovn: Add tunnel PMTUD support.") Reported-at: https://issues.redhat.com/browse/FDP-209 Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- v1 -> v2 ------- * Changed the config option from 'provider_network_overlay' to 'always_tunnel' as suggested by Mark. * Rebased. controller/ovn-controller.c | 27 +++ controller/physical.c | 10 +- controller/physical.h | 1 + northd/en-global-config.c | 5 + ovn-nb.xml | 16 ++ tests/multinode-macros.at | 19 ++ tests/multinode.at | 358 ++++++++++++++++++++++++++++++++++++ tests/ovn.at | 157 ++++++++++++++++ 8 files changed, 592 insertions(+), 1 deletion(-) diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c index b3a265230c..4e30302ea6 100644 --- a/controller/ovn-controller.c +++ b/controller/ovn-controller.c @@ -3349,6 +3349,11 @@ non_vif_data_ovs_iface_handler(struct engine_node *node, void *data OVS_UNUSED) struct ed_type_northd_options { bool explicit_arp_ns_output; + bool always_tunnel; /* Indicates if the traffic to the + * logical port of a bridged logical + * switch (i.e with localnet port) should + * be tunnelled or sent via the localnet + * port. Default value is 'false'. */ }; @@ -3380,6 +3385,12 @@ en_northd_options_run(struct engine_node *node, void *data) false) : false; + n_opts->always_tunnel = + sb_global + ? smap_get_bool(&sb_global->options, "always_tunnel", + false) + : false; + engine_set_node_state(node, EN_UPDATED); } @@ -3403,6 +3414,17 @@ en_northd_options_sb_sb_global_handler(struct engine_node *node, void *data) engine_set_node_state(node, EN_UPDATED); } + bool always_tunnel = + sb_global + ? smap_get_bool(&sb_global->options, "always_tunnel", + false) + : false; + + if (always_tunnel != n_opts->always_tunnel) { + n_opts->always_tunnel = always_tunnel; + engine_set_node_state(node, EN_UPDATED); + } + return true; } @@ -4315,6 +4337,9 @@ static void init_physical_ctx(struct engine_node *node, engine_get_input_data("ct_zones", node); struct simap *ct_zones = &ct_zones_data->ctx.current; + struct ed_type_northd_options *n_opts = + engine_get_input_data("northd_options", node); + parse_encap_ips(ovs_table, &p_ctx->n_encap_ips, &p_ctx->encap_ips); p_ctx->sbrec_port_binding_by_name = sbrec_port_binding_by_name; p_ctx->sbrec_port_binding_by_datapath = sbrec_port_binding_by_datapath; @@ -4332,6 +4357,7 @@ static void init_physical_ctx(struct engine_node *node, p_ctx->local_bindings = &rt_data->lbinding_data.bindings; p_ctx->patch_ofports = &non_vif_data->patch_ofports; p_ctx->chassis_tunnels = &non_vif_data->chassis_tunnels; + p_ctx->always_tunnel = n_opts->always_tunnel; struct controller_engine_ctx *ctrl_ctx = engine_get_context()->client_ctx; p_ctx->if_mgr = ctrl_ctx->if_mgr; @@ -5032,6 +5058,7 @@ main(int argc, char *argv[]) */ engine_add_input(&en_pflow_output, &en_non_vif_data, NULL); + engine_add_input(&en_pflow_output, &en_northd_options, NULL); engine_add_input(&en_pflow_output, &en_ct_zones, pflow_output_ct_zones_handler); engine_add_input(&en_pflow_output, &en_sb_chassis, diff --git a/controller/physical.c b/controller/physical.c index 22756810fd..876ceccf17 100644 --- a/controller/physical.c +++ b/controller/physical.c @@ -1489,6 +1489,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, const struct if_status_mgr *if_mgr, size_t n_encap_ips, const char **encap_ips, + bool always_tunnel, struct ovn_desired_flow_table *flow_table, struct ofpbuf *ofpacts_p) { @@ -1922,7 +1923,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, binding->header_.uuid.parts[0], &match, ofpacts_p, &binding->header_.uuid); } - } else if (access_type == PORT_LOCALNET) { + } else if (access_type == PORT_LOCALNET && !always_tunnel) { /* Remote port connected by localnet port */ /* Table 40, priority 100. * ======================= @@ -1930,6 +1931,11 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name, * Implements switching to localnet port. Each flow matches a * logical output port on remote hypervisor, switch the output port * to connected localnet port and resubmits to same table. + * + * Note: If 'always_tunnel' is true, then + * put_remote_port_redirect_overlay() called from below takes care + * of adding the flow in OFTABLE_REMOTE_OUTPUT table to tunnel to + * the destination chassis. */ ofpbuf_clear(ofpacts_p); @@ -2355,6 +2361,7 @@ physical_eval_port_binding(struct physical_ctx *p_ctx, p_ctx->if_mgr, p_ctx->n_encap_ips, p_ctx->encap_ips, + p_ctx->always_tunnel, flow_table, &ofpacts); ofpbuf_uninit(&ofpacts); } @@ -2482,6 +2489,7 @@ physical_run(struct physical_ctx *p_ctx, p_ctx->if_mgr, p_ctx->n_encap_ips, p_ctx->encap_ips, + p_ctx->always_tunnel, flow_table, &ofpacts); } diff --git a/controller/physical.h b/controller/physical.h index 7fe8ee3c18..4dd228cf8f 100644 --- a/controller/physical.h +++ b/controller/physical.h @@ -69,6 +69,7 @@ struct physical_ctx { size_t n_encap_ips; const char **encap_ips; struct physical_debug debug; + bool always_tunnel; }; void physical_register_ovs_idl(struct ovsdb_idl *); diff --git a/northd/en-global-config.c b/northd/en-global-config.c index 5b71ede1f2..c5e65966b8 100644 --- a/northd/en-global-config.c +++ b/northd/en-global-config.c @@ -521,6 +521,11 @@ check_nb_options_out_of_sync(const struct nbrec_nb_global *nb, return true; } + if (config_out_of_sync(&nb->options, &config_data->nb_options, + "always_tunnel", false)) { + return true; + } + return false; } diff --git a/ovn-nb.xml b/ovn-nb.xml index 9552534f6d..0f9a1005a8 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -391,6 +391,22 @@ non-VXLAN mode tunnel IDs allocation logic. + +

+ If set to true, then the traffic destined to a VIF of a provider + logical switch (having a localnet port) will be tunnelled instead + of sending it via the localnet port. This option will be useful + if CMS wants to connect overlay logical switches (without + localnet port) and provider logical switches to a router. Without + this option set, the traffic path will be a mix of tunnelling and + localnet ports (since routing is distributed) resulting in the + leakage of the router port mac address to the upstream switches + and undefined behavior if NATting is involed. This option is + disabled by default. +

+
+

These options control how routes are advertised between OVN diff --git a/tests/multinode-macros.at b/tests/multinode-macros.at index ef41087ae3..786e564860 100644 --- a/tests/multinode-macros.at +++ b/tests/multinode-macros.at @@ -22,6 +22,25 @@ m4_define([M_NS_CHECK_EXEC], [ AT_CHECK([M_NS_EXEC([$1], [$2], [$3])], m4_shift(m4_shift(m4_shift($@)))) ] ) +# M_DAEMONIZE([fake_node],[command],[pidfile]) +m4_define([M_DAEMONIZE], + [podman exec $1 $2 & echo $! > $3 + echo "kill \`cat $3\`" >> cleanup + ] +) + +# M_START_TCPDUMP([fake_node], [params], [name]) +# +# Helper to properly start tcpdump and wait for the startup. +# The tcpdump output is available in .tcpdump file. +m4_define([M_START_TCPDUMP], + [ + podman exec $1 tcpdump -l $2 >$3.tcpdump 2>$3.stderr & + OVS_WAIT_UNTIL([grep -q "listening" $3.stderr]) + ] +) + + OVS_START_SHELL_HELPERS m_as() { diff --git a/tests/multinode.at b/tests/multinode.at index 1e6eeb6610..a7231130ac 100644 --- a/tests/multinode.at +++ b/tests/multinode.at @@ -1034,3 +1034,361 @@ done M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ip route get 10.0.0.1 dev sw0p1 | grep -q 'mtu 942']) AT_CLEANUP + +AT_SETUP([ovn provider network - always_tunnel]) + +# Check that ovn-fake-multinode setup is up and running +check_fake_multinode_setup + +# Delete the multinode NB and OVS resources before starting the test. +cleanup_multinode_resources + +m_as ovn-chassis-1 ip link del sw0p1-p +m_as ovn-chassis-2 ip link del sw0p2-p + +# Reset geneve tunnels +for c in ovn-chassis-1 ovn-chassis-2 ovn-gw-1 +do + m_as $c ovs-vsctl set open . external-ids:ovn-encap-type=geneve +done + +OVS_WAIT_UNTIL([m_as ovn-chassis-1 ip link show | grep -q genev_sys]) +OVS_WAIT_UNTIL([m_as ovn-chassis-2 ip link show | grep -q genev_sys]) +OVS_WAIT_UNTIL([m_as ovn-gw-1 ip link show | grep -q genev_sys]) + +# The goal of this test case is to see the traffic works for +# E-W switching and routing when the logical switches has localnet ports +# and the option - always_tunnel=true is set. When this option +# is set, traffic is tunneled to the destination chassis instead of using +# localnet ports. + +check multinode_nbctl ls-add sw0 +check multinode_nbctl lsp-add sw0 sw0-port1 +check multinode_nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:03 10.0.0.3 1000::3" +check multinode_nbctl lsp-add sw0 sw0-port2 +check multinode_nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:04 10.0.0.4 1000::4" + +m_as ovn-chassis-1 /data/create_fake_vm.sh sw0-port1 sw0p1 50:54:00:00:00:03 10.0.0.3 24 10.0.0.1 1000::3/64 1000::a +m_as ovn-chassis-2 /data/create_fake_vm.sh sw0-port2 sw0p2 50:54:00:00:00:04 10.0.0.4 24 10.0.0.1 1000::4/64 1000::a + +m_wait_for_ports_up + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +# Create the second logical switch with one port +check multinode_nbctl ls-add sw1 +check multinode_nbctl lsp-add sw1 sw1-port1 +check multinode_nbctl lsp-set-addresses sw1-port1 "40:54:00:00:00:03 20.0.0.3 2000::3" + +# Create a logical router and attach both logical switches +check multinode_nbctl lr-add lr0 +check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::a/64 +check multinode_nbctl lsp-add sw0 sw0-lr0 +check multinode_nbctl lsp-set-type sw0-lr0 router +check multinode_nbctl lsp-set-addresses sw0-lr0 router +check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +check multinode_nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 2000::a/64 +check multinode_nbctl lsp-add sw1 sw1-lr0 +check multinode_nbctl lsp-set-type sw1-lr0 router +check multinode_nbctl lsp-set-addresses sw1-lr0 router +check multinode_nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 + +m_as ovn-chassis-2 /data/create_fake_vm.sh sw1-port1 sw1p1 40:54:00:00:00:03 20.0.0.3 24 20.0.0.1 2000::3/64 2000::a + +# create exteranl connection for N/S traffic +check multinode_nbctl ls-add public +check multinode_nbctl lsp-add public ln-lublic +check multinode_nbctl lsp-set-type ln-lublic localnet +check multinode_nbctl lsp-set-addresses ln-lublic unknown +check multinode_nbctl lsp-set-options ln-lublic network_name=public + +check multinode_nbctl lrp-add lr0 lr0-public 00:11:22:00:ff:01 172.20.0.100/24 +check multinode_nbctl lsp-add public public-lr0 +check multinode_nbctl lsp-set-type public-lr0 router +check multinode_nbctl lsp-set-addresses public-lr0 router +check multinode_nbctl lsp-set-options public-lr0 router-port=lr0-public +check multinode_nbctl lrp-set-gateway-chassis lr0-public ovn-gw-1 10 +check multinode_nbctl lr-route-add lr0 0.0.0.0/0 172.20.0.1 + +check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 10.0.0.0/24 +check multinode_nbctl lr-nat-add lr0 snat 172.20.0.100 20.0.0.0/24 + +# create localnet ports for sw0 and sw1 +check multinode_nbctl lsp-add sw0 ln-sw0 +check multinode_nbctl lsp-set-type ln-sw0 localnet +check multinode_nbctl lsp-set-addresses ln-sw0 unknown +check multinode_nbctl lsp-set-options ln-sw0 network_name=public +check multinode_nbctl set logical_switch_port ln-sw0 tag_request=100 + +check multinode_nbctl lsp-add sw1 ln-sw1 +check multinode_nbctl lsp-set-type ln-sw1 localnet +check multinode_nbctl lsp-set-addresses ln-sw1 unknown +check multinode_nbctl lsp-set-options ln-sw1 network_name=public +check multinode_nbctl set logical_switch_port ln-sw1 tag_request=101 + +check multinode_nbctl --wait=hv sync + +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([cat ch1_eth2.tcpdump | cut -d ' ' -f2-22], [0], [dnl +50:54:00:00:00:03 > 50:54:00:00:00:04, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 10.0.0.3 > 10.0.0.4: ICMP echo request, +50:54:00:00:00:04 > 50:54:00:00:00:03, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 10.0.0.4 > 10.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_genev.tcpdump], [0], [dnl +]) + +m_as ovn-chassis-1 killall tcpdump +rm -f *.tcpdump +rm -f *.stderr + +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([cat ch1_eth2.tcpdump | cut -d ' ' -f2-22], [0], [dnl +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype 802.1Q (0x8100), length 102: vlan 101, p 0, ethertype IPv4 (0x0800), 10.0.0.3 > 20.0.0.3: ICMP echo request, +00:00:00:00:ff:01 > 50:54:00:00:00:03, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 20.0.0.3 > 10.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_genev.tcpdump], [0], [dnl +]) + +# Set the option always_tunnel=true. +# Traffic from sw0p1 to sw0p2 should be tunneled. +check multinode_nbctl set NB_Global . options:always_tunnel=true +check multinode_nbctl --wait=hv sync + +m_as ovn-chassis-1 killall tcpdump +rm -f *.tcpdump +rm -f *.stderr + +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.4 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([cat ch1_genev.tcpdump | cut -d ' ' -f2-15], [0], [dnl +50:54:00:00:00:03 > 50:54:00:00:00:04, ethertype IPv4 (0x0800), length 98: 10.0.0.3 > 10.0.0.4: ICMP echo request, +50:54:00:00:00:04 > 50:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 10.0.0.4 > 10.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_eth2.tcpdump], [0], [dnl +]) + +m_as ovn-chassis-1 killall tcpdump +rm -f *.tcpdump +rm -f *.stderr + +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([cat ch1_genev.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 10.0.0.3 > 20.0.0.3: ICMP echo request, +00:00:00:00:ff:01 > 50:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 20.0.0.3 > 10.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_eth2.tcpdump], [0], [dnl +]) + +m_as ovn-chassis-1 killall tcpdump +rm -f *.tcpdump +rm -f *.stderr + +# Delete ln-sw1. +check multinode_nbctl --wait=hv lsp-del ln-sw1 +# Traffic from sw0p1 to sw1p1 should be tunneled. + +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 20.0.0.3 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CHECK([cat ch1_genev.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 10.0.0.3 > 20.0.0.3: ICMP echo request, +00:00:00:00:ff:01 > 50:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 20.0.0.3 > 10.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_eth2.tcpdump], [0], [dnl +]) + +m_as ovn-chassis-1 killall tcpdump +rm -f *.tcpdump +rm -f *.stderr + +# Make sure that traffic from sw0 still goes out of localnet port +# for IPs not managed by OVN. +# Create a fake vm in br-ex on ovn-gw-1 with IP - 10.0.0.10 +m_as ovn-gw-1 ip netns add sw0-p10 +m_as ovn-gw-1 ovs-vsctl add-port br-ex sw0-p10 -- set interface sw0-p10 type=internal +m_as ovn-gw-1 ovs-vsctl set port sw0-p10 tag=100 +m_as ovn-gw-1 ip link set sw0-p10 netns sw0-p10 +m_as ovn-gw-1 ip netns exec sw0-p10 ip link set sw0-p10 up +m_as ovn-gw-1 ip netns exec sw0-p10 ip link set sw0-p10 address 32:31:8c:da:64:4f +m_as ovn-gw-1 ip netns exec sw0-p10 ip addr add 10.0.0.10/24 dev sw0-p10 + +# Ping from sw0p1 (on ovn-chassis-1) tp sw0-p10 which is in ovn-gw-1 on +# external bridge. The traffic path is +# sw0p1 -> br-int -> localnet port (vlan tagged 100) -> br-ex -> eth2 of ovn-chassis-1 to +# eth2 of ovn-gw-1 -> br-ex -> sw0-p10 + +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) +M_START_TCPDUMP([ovn-gw-1], [-c 2 -neei eth2 icmp], [gw1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.10 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +m_as ovn-chassis-1 killall tcpdump +m_as ovn-gw-1 killall tcpdump + +AT_CHECK([cat ch1_eth2.tcpdump | cut -d ' ' -f2-22], [0], [dnl +50:54:00:00:00:03 > 32:31:8c:da:64:4f, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 10.0.0.3 > 10.0.0.10: ICMP echo request, +32:31:8c:da:64:4f > 50:54:00:00:00:03, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 10.0.0.10 > 10.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_genev.tcpdump], [0], [dnl + +]) + +AT_CHECK([cat gw1_eth2.tcpdump | cut -d ' ' -f2-22], [0], [dnl +50:54:00:00:00:03 > 32:31:8c:da:64:4f, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 10.0.0.3 > 10.0.0.10: ICMP echo request, +32:31:8c:da:64:4f > 50:54:00:00:00:03, ethertype 802.1Q (0x8100), length 102: vlan 100, p 0, ethertype IPv4 (0x0800), 10.0.0.10 > 10.0.0.3: ICMP echo reply, +]) + +rm -f *.tcpdump +rm -f *.stderr + +# Add dnat_and_snat entry for 10.0.0.3 <-> 172.20.0.110 +check multinode_nbctl --wait=hv lr-nat-add lr0 dnat_and_snat 172.20.0.110 10.0.0.3 sw0-port1 30:54:00:00:00:03 + +# Ping from sw1-p1 to 172.20.0.110 +# Traffic path is +# sw1-p1 in ovn-chassis-2 -> tunnel -> ovn-gw-1 -> In ovn-gw-1 SNAT 20.0.0.3 to 172.20.0.100 -> +# -> ln-public -> br-ex -> eth2 -> ovn-chassis-1 -> br-ex -> ln-public -> br-int -> +# -> DNAT 172.20.0.110 to 10.0.0.3 -> sw0-p1 with src ip 172.20.0.100 and dst ip 10.0.0.3. + +M_START_TCPDUMP([ovn-chassis-2], [-c 2 -neei genev_sys_6081 icmp], [ch2_genev]) +M_START_TCPDUMP([ovn-chassis-2], [-c 2 -neei eth2 icmp], [ch2_eth2]) +M_START_TCPDUMP([ovn-gw-1], [-c 2 -neei genev_sys_6081 icmp], [gw1_geneve]) +M_START_TCPDUMP([ovn-gw-1], [-c 2 -neei eth2 icmp], [gw1_eth2]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-2], [sw1p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.110 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +m_as ovn-chassis-1 killall tcpdump +m_as ovn-chassis-2 killall tcpdump +m_as ovn-gw-1 killall tcpdump + +AT_CHECK([cat ch2_genev.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:11:22:00:ff:01 > 30:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 20.0.0.3 > 172.20.0.110: ICMP echo request, +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.110 > 20.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat gw1_geneve.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:11:22:00:ff:01 > 30:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 20.0.0.3 > 172.20.0.110: ICMP echo request, +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.110 > 20.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat gw1_eth2.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:11:22:00:ff:01 > 30:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.100 > 172.20.0.110: ICMP echo request, +30:54:00:00:00:03 > 00:11:22:00:ff:01, ethertype IPv4 (0x0800), length 98: 172.20.0.110 > 172.20.0.100: ICMP echo reply, +]) + +AT_CHECK([cat ch1_eth2.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:11:22:00:ff:01 > 30:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.100 > 172.20.0.110: ICMP echo request, +30:54:00:00:00:03 > 00:11:22:00:ff:01, ethertype IPv4 (0x0800), length 98: 172.20.0.110 > 172.20.0.100: ICMP echo reply, +]) + +AT_CHECK([cat ch1_genev.tcpdump], [0], [dnl + +]) + +rm -f *.tcpdump +rm -f *.stderr + +# Now clear the logical_port of dnat_and_snat entry. ovn-gw-1 should handle the DNAT. +check multinode_nbctl lr-nat-del lr0 dnat_and_snat 172.20.0.110 +check multinode_nbctl --wait=hv lr-nat-add lr0 dnat_and_snat 172.20.0.110 10.0.0.3 +# Ping from sw1-p1 to 172.20.0.110 +# Traffic path is +# sw1-p1 in ovn-chassis-2 -> tunnel -> ovn-gw-1 -> In ovn-gw-1 SNAT 20.0.0.3 to 172.20.0.100 -> +# DNAT 172.20.0.110 -> 10.0.0.3 -> tunnel -> ovn-chassis-1 -> br-int -> sw0p1 + +M_START_TCPDUMP([ovn-chassis-2], [-c 2 -neei genev_sys_6081 icmp], [ch2_genev]) +M_START_TCPDUMP([ovn-chassis-2], [-c 2 -neei eth2 icmp], [ch2_eth2]) +M_START_TCPDUMP([ovn-gw-1], [-c 4 -neei genev_sys_6081 icmp], [gw1_geneve]) +M_START_TCPDUMP([ovn-gw-1], [-c 4 -neei eth2 icmp], [gw1_eth2]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei genev_sys_6081 icmp], [ch1_genev]) +M_START_TCPDUMP([ovn-chassis-1], [-c 2 -neei eth2 icmp], [ch1_eth2]) + +M_NS_CHECK_EXEC([ovn-chassis-2], [sw1p1], [ping -q -c 3 -i 0.3 -w 2 172.20.0.110 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +m_as ovn-chassis-1 killall tcpdump +m_as ovn-chassis-2 killall tcpdump +m_as ovn-gw-1 killall tcpdump + +AT_CHECK([cat ch2_genev.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:11:22:00:ff:01 > 00:11:22:00:ff:01, ethertype IPv4 (0x0800), length 98: 20.0.0.3 > 172.20.0.110: ICMP echo request, +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.110 > 20.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat ch1_eth2.tcpdump], [0], [dnl + +]) + +AT_CHECK([cat gw1_geneve.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:11:22:00:ff:01 > 00:11:22:00:ff:01, ethertype IPv4 (0x0800), length 98: 20.0.0.3 > 172.20.0.110: ICMP echo request, +00:00:00:00:ff:01 > 50:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.100 > 10.0.0.3: ICMP echo request, +00:11:22:00:ff:01 > 00:11:22:00:ff:01, ethertype IPv4 (0x0800), length 98: 10.0.0.3 > 172.20.0.100: ICMP echo reply, +00:00:00:00:ff:02 > 40:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.110 > 20.0.0.3: ICMP echo reply, +]) + +AT_CHECK([cat gw1_eth2.tcpdump], [0], [dnl + +]) + +AT_CHECK([cat ch1_genev.tcpdump | cut -d ' ' -f2-15], [0], [dnl +00:00:00:00:ff:01 > 50:54:00:00:00:03, ethertype IPv4 (0x0800), length 98: 172.20.0.100 > 10.0.0.3: ICMP echo request, +00:11:22:00:ff:01 > 00:11:22:00:ff:01, ethertype IPv4 (0x0800), length 98: 10.0.0.3 > 172.20.0.100: ICMP echo reply, +]) + +AT_CHECK([cat ch1_eth2.tcpdump], [0], [dnl + +]) + +AT_CLEANUP diff --git a/tests/ovn.at b/tests/ovn.at index 13b393932e..2ced7c0b2f 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -38339,6 +38339,163 @@ OVN_CLEANUP([hv1 AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([Provider network - always tunnel]) +ovn_start +net_add n1 + +for hv in 1 2; do + sim_add hv${hv} + as hv${hv} + ovs-vsctl add-br br-phys + ovn_attach n1 br-phys 192.168.0.${hv} + ovs-vsctl set open . external_ids:ovn-bridge-mappings=physnet1:br-phys +done + +check ovn-nbctl ls-add sw0 +check ovn-nbctl lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 "00:00:10:01:02:03 10.0.0.3" +check ovn-nbctl lsp-add sw0 sw0-p2 -- lsp-set-addresses sw0-p2 "00:00:04:01:02:04 10.0.0.4" + +check ovn-nbctl ls-add sw1 +check ovn-nbctl lsp-add sw1 sw1-p1 -- lsp-set-addresses sw1-p1 "00:00:20:01:02:03 20.0.0.3" +check ovn-nbctl lsp-add sw1 sw1-p2 -- lsp-set-addresses sw1-p2 "00:00:20:01:02:04 20.0.0.4" + +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 +check ovn-nbctl lsp-add sw0 sw0-lr0 +check ovn-nbctl lsp-set-type sw0-lr0 router +check ovn-nbctl lsp-set-addresses sw0-lr0 router +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0 + +check ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24 +check ovn-nbctl lsp-add sw1 sw1-lr0 +check ovn-nbctl lsp-set-type sw1-lr0 router +check ovn-nbctl lsp-set-addresses sw1-lr0 router +check ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1 + +as hv1 +ovs-vsctl add-port br-int vif11 -- \ + set Interface vif11 external-ids:iface-id=sw0-p1 \ + options:tx_pcap=hv1/vif11-tx.pcap \ + options:rxq_pcap=hv1/vif11-rx.pcap \ + ofport-request=11 +ovs-vsctl add-port br-int vif12 -- \ + set Interface vif12 external-ids:iface-id=sw1-p1 \ + options:tx_pcap=hv1/vif12-tx.pcap \ + options:rxq_pcap=hv1/vif12-rx.pcap \ + ofport-request=12 + +as hv2 +ovs-vsctl add-port br-int vif21 -- \ + set Interface vif21 external-ids:iface-id=sw0-p2 \ + options:tx_pcap=hv1/vif21-tx.pcap \ + options:rxq_pcap=hv1/vif21-rx.pcap \ + ofport-request=21 +ovs-vsctl add-port br-int vif22 -- \ + set Interface vif22 external-ids:iface-id=sw1-p2 \ + options:tx_pcap=hv1/vif22-tx.pcap \ + options:rxq_pcap=hv1/vif22-rx.pcap \ + ofport-request=22 + +check ovn-nbctl --wait=hv sync +wait_for_ports_up + +sw0_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=sw0)) +sw0p1_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=sw0-p1)) +sw0p2_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=sw0-p2)) + +sw1_dp_key=$(printf "%x" $(fetch_column Datapath_Binding tunnel_key external_ids:name=sw1)) +sw1p1_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=sw1-p1)) +sw1p2_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=sw1-p2)) + +check_output_flows_tunnelled() { + hv=$1 + dp_key=$2 + dp_rport=$3 + AT_CHECK_UNQUOTED([as $hv ovs-ofctl dump-flows br-int table=OFTABLE_REMOTE_OUTPUT,metadata=0x${dp_key},reg15=0x${dp_rport} | ofctl_strip_all | grep -v NXST_FLOW], [0], [dnl + table=OFTABLE_REMOTE_OUTPUT, priority=100,reg13=0/0xffff0000,reg15=0x${dp_rport},metadata=0x${dp_key} actions=load:0x${dp_key}->NXM_NX_TUN_ID[[0..23]],set_field:0x${dp_rport}->tun_metadata0,move:NXM_NX_REG14[[0..14]]->NXM_NX_TUN_METADATA0[[16..30]],output:1,resubmit(,OFTABLE_LOCAL_OUTPUT) +]) +} + +check_output_flows_via_localnet() { + hv=$1 + dp_key=$2 + dp_rport=$3 + lnport_key=$4 + AT_CHECK_UNQUOTED([as $hv ovs-ofctl dump-flows br-int table=OFTABLE_REMOTE_OUTPUT,metadata=0x${dp_key},reg15=0x${dp_rport} | ofctl_strip_all | grep -v NXST_FLOW], [1], [dnl +]) + + AT_CHECK_UNQUOTED([as $hv ovs-ofctl dump-flows br-int table=OFTABLE_LOCAL_OUTPUT,metadata=0x${dp_key},reg15=0x${dp_rport} | ofctl_strip_all | grep -v NXST_FLOW], [0], [dnl + table=OFTABLE_LOCAL_OUTPUT, priority=100,reg15=0x${dp_rport},metadata=0x${dp_key} actions=load:0x${lnport_key}->NXM_NX_REG15[[]],resubmit(,OFTABLE_LOCAL_OUTPUT) +]) +} + +# There are no localnet ports in sw0 and sw1. So the pkts are tunnelled. +check_output_flows_tunnelled hv1 ${sw0_dp_key} ${sw0p2_key} +check_output_flows_tunnelled hv1 ${sw1_dp_key} ${sw1p2_key} +check_output_flows_tunnelled hv2 ${sw0_dp_key} ${sw0p1_key} +check_output_flows_tunnelled hv2 ${sw1_dp_key} ${sw1p1_key} + +# Add localnet port to sw0 +check ovn-nbctl lsp-add sw0 ln-sw0 -- lsp-set-addresses ln-sw0 unknown -- lsp-set-type ln-sw0 localnet +check ovn-nbctl --wait=hv lsp-set-options ln-sw0 network_name=physnet1 -- set logical_switch_port ln-sw0 tag_request=100 +lnsw0_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=ln-sw0)) + +# Flows should be installed to use localnet port for sw0. +check_output_flows_via_localnet hv1 ${sw0_dp_key} ${sw0p2_key} ${lnsw0_key} +check_output_flows_tunnelled hv1 ${sw1_dp_key} ${sw1p2_key} +check_output_flows_via_localnet hv2 ${sw0_dp_key} ${sw0p1_key} ${lnsw0_key} +check_output_flows_tunnelled hv2 ${sw1_dp_key} ${sw1p1_key} + +# Add localnet port to sw1 +check ovn-nbctl lsp-add sw1 ln-sw1 -- lsp-set-addresses ln-sw1 unknown -- lsp-set-type ln-sw1 localnet +check ovn-nbctl --wait=hv lsp-set-options ln-sw1 network_name=physnet1 -- set logical_switch_port ln-sw1 tag_request=101 +lnsw1_key=$(printf "%x" $(fetch_column Port_Binding tunnel_key logical_port=ln-sw1)) + +# Flows should be installed to use localnet port. +check_output_flows_via_localnet hv1 ${sw0_dp_key} ${sw0p2_key} ${lnsw0_key} +check_output_flows_via_localnet hv1 ${sw1_dp_key} ${sw1p2_key} ${lnsw1_key} +check_output_flows_via_localnet hv2 ${sw0_dp_key} ${sw0p1_key} ${lnsw0_key} +check_output_flows_via_localnet hv2 ${sw1_dp_key} ${sw1p1_key} ${lnsw1_key} + +# Set the always_tunnel option to true. +check ovn-nbctl set NB_Global . options:always_tunnel=true +check ovn-nbctl --wait=hv sync + +# Flows should be installed to tunnel. +check_output_flows_tunnelled hv1 ${sw0_dp_key} ${sw0p2_key} +check_output_flows_tunnelled hv1 ${sw1_dp_key} ${sw1p2_key} +check_output_flows_tunnelled hv2 ${sw0_dp_key} ${sw0p1_key} +check_output_flows_tunnelled hv2 ${sw1_dp_key} ${sw1p1_key} + +# Set the always_tunnel option to false. +check ovn-nbctl set NB_Global . options:always_tunnel=false +check ovn-nbctl --wait=hv sync + +# Flows should be installed to use localnet port. +check_output_flows_via_localnet hv1 ${sw0_dp_key} ${sw0p2_key} ${lnsw0_key} +check_output_flows_via_localnet hv1 ${sw1_dp_key} ${sw1p2_key} ${lnsw1_key} +check_output_flows_via_localnet hv2 ${sw0_dp_key} ${sw0p1_key} ${lnsw0_key} +check_output_flows_via_localnet hv2 ${sw1_dp_key} ${sw1p1_key} ${lnsw1_key} + +check ovn-nbctl --wait=hv lsp-del ln-sw0 + +# Flows should be installed to tunnel for sw0 +check_output_flows_tunnelled hv1 ${sw0_dp_key} ${sw0p2_key} +check_output_flows_tunnelled hv2 ${sw0_dp_key} ${sw0p1_key} + +check ovn-nbctl --wait=hv lsp-del ln-sw1 +# Flows should be installed to tunnel. +check_output_flows_tunnelled hv1 ${sw0_dp_key} ${sw0p2_key} +check_output_flows_tunnelled hv1 ${sw1_dp_key} ${sw1p2_key} +check_output_flows_tunnelled hv2 ${sw0_dp_key} ${sw0p1_key} +check_output_flows_tunnelled hv2 ${sw1_dp_key} ${sw1p1_key} + +OVN_CLEANUP([hv1],[hv2]) + +AT_CLEANUP +]) + OVN_FOR_EACH_NORTHD([ AT_SETUP([Delete parent of container port]) ovn_start