From patchwork Thu Jun 3 18:49:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1487383 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=VemO5h0u; dkim-atps=neutral Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fww1S0jzKz9sVb for ; Fri, 4 Jun 2021 04:49:47 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id E44E983FAA; Thu, 3 Jun 2021 18:49:44 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Tw0qvuGY2anl; Thu, 3 Jun 2021 18:49:42 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp1.osuosl.org (Postfix) with ESMTP id 94D7483E22; Thu, 3 Jun 2021 18:49:41 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E04F4C0023; Thu, 3 Jun 2021 18:49:39 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id EE7F8C0001 for ; Thu, 3 Jun 2021 18:49:37 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id D0084405A7 for ; Thu, 3 Jun 2021 18:49:37 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp4.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dpoJM6h1lSBE for ; Thu, 3 Jun 2021 18:49:36 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by smtp4.osuosl.org (Postfix) with ESMTPS id 6DB574055B for ; Thu, 3 Jun 2021 18:49:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1622746175; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sEsvJrxiz7l/QAdNXMiSpb7l7eQ83V7azwZWA2mX4nk=; b=VemO5h0uMSlUG21xK6JlBYb/epWhf5egN3nzELVg2wxCWd0bSUolrO2Gs9vw7pHM4I/sAM Y+U+ZXVtaSFSmH5Ooyd1RtrKywDeH7hveodq2QmOdTXdyDJqkn7EoF2yrZomTplLUqKXdr /mRxNrdNQWuLZSC/669TVyMuBamPZik= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-375-nMkmWzkBO7SwjhVlC9z7_Q-1; Thu, 03 Jun 2021 14:49:33 -0400 X-MC-Unique: nMkmWzkBO7SwjhVlC9z7_Q-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 860B48DD127 for ; Thu, 3 Jun 2021 18:49:32 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-115-92.rdu2.redhat.com [10.10.115.92]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3E54760657 for ; Thu, 3 Jun 2021 18:49:32 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 3 Jun 2021 14:49:26 -0400 Message-Id: <20210603184931.1425441-2-mmichels@redhat.com> In-Reply-To: <20210603184931.1425441-1-mmichels@redhat.com> References: <20210603184931.1425441-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v8 1/6] northd: Swap src and dst eth addresses in router egress loop. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" When a hairpin scenario is detected (i.e. egressing on gateway router port to a NAT external address), we loop back to the ingress router pipeline and swap the inport and outport. This is intended to allow for the traffic to get DNATted. In practice, though, the ethernet destination has not been updated to be the distributed gateway port's address, and so the packet is immediately dropped. This fixes the issue by swapping source and dest eth addressses when doing the hairpin redirection. This way, the packet appears to be arriving on the gateway port, and it gets handled as expected. Reported at: https://bugzilla.redhat.com/show_bug.cgi?id=1929901 Signed-off-by: Mark Michelson --- northd/ovn-northd.c | 1 + northd/ovn_northd.dl | 1 + tests/system-ovn.at | 113 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index bf09eed59..46c590d70 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -11809,6 +11809,7 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od, ds_put_format(actions, "clone { ct_clear; " "inport = outport; outport = \"\"; " + "eth.dst <-> eth.src; " "flags = 0; flags.loopback = 1; "); for (int j = 0; j < MFF_N_LOG_REGS; j++) { ds_put_format(actions, "reg%d = 0; ", j); diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index cb8418540..60ace7e61 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -5924,6 +5924,7 @@ for (r in &Router(._uuid = lr_uuid, var actions = "clone { ct_clear; " "inport = outport; outport = \"\"; " + "eth.dst <-> eth.src; " "flags = 0; flags.loopback = 1; " ++ regs.join("") ++ "${rEGBIT_EGRESS_LOOPBACK()} = 1; " diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 76fc21c87..3824788c4 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -6054,6 +6054,7 @@ AT_CLEANUP ]) OVN_FOR_EACH_NORTHD([ +<<<<<<< HEAD AT_SETUP([ovn -- No ct_state matches in dp flows when no ACLs in an LS]) AT_KEYWORDS([no ct_state match]) ovn_start @@ -6092,7 +6093,6 @@ check ovn-nbctl pg-add pg1 sw1-p1 check ovn-nbctl acl-add pg1 from-lport 1002 "ip" allow-related check ovn-nbctl acl-add pg1 to-lport 1002 "ip" allow-related - OVN_POPULATE_ARP ovn-nbctl --wait=hv sync @@ -6180,5 +6180,116 @@ OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE]) as OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) + +AT_CLEANUP +]) + +AT_SETUP(ovn -- DNAT LR hairpin IPv4) +AT_KEYWORDS(hairpin) + +ovn_start + +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +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 bridge br-int fail-mode=secure other-config:disable-in-band=true + +start_daemon ovn-controller + +# Logical network: +# Two VMs +# * VM1 with IP address 192.168.100.5 +# * VM2 with IP address 192.168.100.6 +# The VMs connect to logical switch ls1. +# +# An external router with IP address 172.18.1.2. We simulate this with a network namespace. +# There will be no traffic going here in this test. +# The external router connects to logical switch ls-pub +# +# One logical router (lr1) connects to ls1 and ls-pub. The router port connected to ls-pub is +# a gateway port. +# * The subnet connected to ls1 is 192.168.100.0/24. The Router IP address is 192.168.100.1 +# * The subnet connected to ls-pub is 172.18.1.0/24. The Router IP address is 172.168.1.1 +# lr1 has the following attributes: +# * It has a "default" static route that sends traffic out the gateway router port. +# * It has a DNAT rule that translates 172.18.2.10 to 192.168.100.6 (VM2) +# +# In this test, we want to ensure that a ping from VM1 to IP address 172.18.2.10 reaches VM2. + +ovn-nbctl ls-add ls1 +ovn-nbctl lsp-add ls1 vm1 -- lsp-set-addresses vm1 "00:00:00:00:00:05 192.168.100.5" +ovn-nbctl lsp-add ls1 vm2 -- lsp-set-addresses vm2 "00:00:00:00:00:06 192.168.100.6" + +ovn-nbctl ls-add ls-pub +ovn-nbctl lsp-add ls-pub ext-router -- lsp-set-addresses ext-router "00:00:00:00:01:02 172.18.1.2" + +ovn-nbctl lr-add lr1 +ovn-nbctl lrp-add lr1 lr1-ls1 00:00:00:00:00:01 192.168.100.1/24 +ovn-nbctl lsp-add ls1 ls1-lr1 \ + -- lsp-set-type ls1-lr1 router \ + -- lsp-set-addresses ls1-lr1 00:00:00:00:00:01 \ + -- lsp-set-options ls1-lr1 router-port=lr1-ls1 + +ovn-nbctl lrp-add lr1 lr1-ls-pub 00:00:00:00:01:01 172.18.1.1/24 +ovn-nbctl lrp-set-gateway-chassis lr1-ls-pub hv1 +ovn-nbctl lsp-add ls-pub ls-pub-lr1 \ + -- lsp-set-type ls-pub-lr1 router \ + -- lsp-set-addresses ls-pub-lr1 00:00:00:00:01:01 \ + -- lsp-set-options ls-pub-lr1 router-port=lr1-ls-pub + +ovn-nbctl lr-nat-add lr1 snat 172.18.1.1 192.168.100.0/24 +ovn-nbctl lr-nat-add lr1 dnat_and_snat 172.18.2.10 192.168.100.6 +ovn-nbctl lr-route-add lr1 0.0.0.0/0 172.18.1.2 + +#ls1_uuid=$(fetch_column Port_Binding datapath logical_port=vm1) +#ovn-sbctl create MAC_Binding ip=172.18.2.10 datapath=$ls1_uuid logical_port=vm2 mac="00:00:00:00:00:06" + +OVN_POPULATE_ARP +ovn-nbctl --wait=hv sync + +ADD_NAMESPACES(vm1) +ADD_VETH(vm1, vm1, br-int, "192.168.100.5/24", "00:00:00:00:00:05", \ + "192.168.100.1") + +ADD_NAMESPACES(vm2) +ADD_VETH(vm2, vm2, br-int, "192.168.100.6/24", "00:00:00:00:00:06", \ + "192.168.100.1") + +ADD_NAMESPACES(ext-router) +ADD_VETH(ext-router, ext-router, br-int, "172.18.1.2/24", "00:00:00:00:01:02", \ + "172.18.1.1") + +# Let's take a quick look at the logical flows +ovn-sbctl lflow-list + +# Let's check what ovn-trace says... +ovn-trace ls1 'inport == "vm1" && eth.src == 00:00:00:00:00:05 && ip4.src == 192.168.100.5 && eth.dst == 00:00:00:00:00:01 && ip4.dst == 172.18.2.10 && ip.ttl == 32' + +# A ping from vm1 should hairpin in lr1 and successfully DNAT to vm2 +NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.10 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +kill $(pidof ovn-controller) + +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([NORTHD_TYPE]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d +/.*terminating with signal 15.*/d"]) + AT_CLEANUP ]) From patchwork Thu Jun 3 18:49:27 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1487385 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=gOrXE11U; dkim-atps=neutral Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fww1W4jjGz9sVt for ; Fri, 4 Jun 2021 04:49:51 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id B38F660BA3; Thu, 3 Jun 2021 18:49:47 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id E805k5FBBA60; Thu, 3 Jun 2021 18:49:45 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTP id D201260811; Thu, 3 Jun 2021 18:49:42 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 9FFD0C002A; Thu, 3 Jun 2021 18:49:41 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 75837C0001 for ; Thu, 3 Jun 2021 18:49:38 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 573E34055B for ; Thu, 3 Jun 2021 18:49:38 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp4.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id wto1uSw45Whc for ; Thu, 3 Jun 2021 18:49:36 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp4.osuosl.org (Postfix) with ESMTPS id 468B24023D for ; Thu, 3 Jun 2021 18:49:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1622746175; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=F894uDTtwrjEH7x6N7hB24CFOYkLvdzwjA9OUtgIK2E=; b=gOrXE11Uur96WlIuFcUT6f970JniNoM/5Y5VrtFOOpa3kFRKs+/W4C83EWlvttkkFKVhtD dl+xv98fdeoBvWWb84TwLrkpEWjutu3lvP/1c8x4H4PsaVvN0z7U4jd5aQS3YZ8BmomaX3 r2qUqDFdUMv0sofQ8g2eZ27a7VteCNg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-434-sXVXEt30OLGPscOT5eoxcg-1; Thu, 03 Jun 2021 14:49:33 -0400 X-MC-Unique: sXVXEt30OLGPscOT5eoxcg-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 09266801817 for ; Thu, 3 Jun 2021 18:49:33 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-115-92.rdu2.redhat.com [10.10.115.92]) by smtp.corp.redhat.com (Postfix) with ESMTP id B2F70189CE for ; Thu, 3 Jun 2021 18:49:32 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 3 Jun 2021 14:49:27 -0400 Message-Id: <20210603184931.1425441-3-mmichels@redhat.com> In-Reply-To: <20210603184931.1425441-1-mmichels@redhat.com> References: <20210603184931.1425441-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v8 2/6] ovn-sb: Remove redundant "nat-addresses" information from Port_Binding. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Signed-off-by: Mark Michelson --- ovn-sb.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ovn-sb.xml b/ovn-sb.xml index 258a12b4e..b853a5031 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -3015,16 +3015,6 @@ tcp.flags = RST; The chassis in which the port resides. - - MAC address of the l3gateway port followed by a list of - SNAT and DNAT external IP addresses. This is used to send gratuitous - ARPs for SNAT and DNAT external IP addresses via localnet. - Example: 80:fa:5b:06:72:b7 158.36.44.22 158.36.44.24. - This would result in generation of gratuitous ARPs for IP addresses - 158.36.44.22 and 158.36.44.24 with a MAC address of 80:fa:5b:06:72:b7. - This is used in OVS versions prior to 2.8. - - MAC address of the l3gateway port followed by a list of SNAT and DNAT external IP addresses. This is used to send gratuitous From patchwork Thu Jun 3 18:49:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1487384 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=OcZcRdge; dkim-atps=neutral Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fww1T1KvYz9sVt for ; Fri, 4 Jun 2021 04:49:49 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 9F47860BA0; Thu, 3 Jun 2021 18:49:47 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id JWDMrRXZw9aM; Thu, 3 Jun 2021 18:49:46 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTP id E635960B88; Thu, 3 Jun 2021 18:49:43 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 75EFAC002B; Thu, 3 Jun 2021 18:49:42 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 4FE40C001C for ; Thu, 3 Jun 2021 18:49:39 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 221D8834E8 for ; Thu, 3 Jun 2021 18:49:39 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp1.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ioHPPDP7_3vG for ; Thu, 3 Jun 2021 18:49:37 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id 21BC083474 for ; Thu, 3 Jun 2021 18:49:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1622746176; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=skd6siwZUzXMJxSUHG3Lls6bbsZjEPg2ieLun8vC7Ls=; b=OcZcRdge3bmxyNytsMP6l9Mk8zgRNNU9kY5Sp2YIHBN9VodiXTWSEzX8es87zKLg4JsIBG B/poNOx+psoeKR8KcYhnydlAkF/WRoPSgVfkPO+JdKcktDDFxbpLI0UG6wl36EPGPqrdLb 8H8Pycd1sCIkVl7nZ+Zs8Kuyaeebc5A= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-315-zo4mdQSQPcG_gkIPWdyTZw-1; Thu, 03 Jun 2021 14:49:34 -0400 X-MC-Unique: zo4mdQSQPcG_gkIPWdyTZw-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7D3EA1804688 for ; Thu, 3 Jun 2021 18:49:33 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-115-92.rdu2.redhat.com [10.10.115.92]) by smtp.corp.redhat.com (Postfix) with ESMTP id 313BD189CE for ; Thu, 3 Jun 2021 18:49:33 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 3 Jun 2021 14:49:28 -0400 Message-Id: <20210603184931.1425441-4-mmichels@redhat.com> In-Reply-To: <20210603184931.1425441-1-mmichels@redhat.com> References: <20210603184931.1425441-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v8 3/6] northd: Factor peer retrieval into its own function. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" The same pattern is repeated several times throughout ovn-northd.c, so this puts it in its own function. This will be used even more in an upcoming commit. Signed-off-by: Mark Michelson --- northd/ovn-northd.c | 70 ++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 46c590d70..ef4f5b790 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -1546,6 +1546,21 @@ lrport_is_enabled(const struct nbrec_logical_router_port *lrport) return !lrport->enabled || *lrport->enabled; } +static struct ovn_port * +ovn_port_get_peer(struct hmap *ports, struct ovn_port *op) +{ + if (!op->nbsp || !lsp_is_router(op->nbsp) || op->derived) { + return NULL; + } + + const char *peer_name = smap_get(&op->nbsp->options, "router-port"); + if (!peer_name) { + return NULL; + } + + return ovn_port_find(ports, peer_name); +} + static void ipam_insert_ip_for_datapath(struct ovn_datapath *od, uint32_t ip) { @@ -2373,12 +2388,7 @@ join_logical_ports(struct northd_context *ctx, struct ovn_port *op; HMAP_FOR_EACH (op, key_node, ports) { if (op->nbsp && lsp_is_router(op->nbsp) && !op->derived) { - const char *peer_name = smap_get(&op->nbsp->options, "router-port"); - if (!peer_name) { - continue; - } - - struct ovn_port *peer = ovn_port_find(ports, peer_name); + struct ovn_port *peer = ovn_port_get_peer(ports, op); if (!peer || !peer->nbrp) { continue; } @@ -10172,14 +10182,8 @@ build_arp_resolve_flows_for_lrouter_port( /* Get the Logical_Router_Port that the * Logical_Switch_Port is connected to, as * 'peer'. */ - const char *peer_name = smap_get( - &op->od->router_ports[k]->nbsp->options, - "router-port"); - if (!peer_name) { - continue; - } - - struct ovn_port *peer = ovn_port_find(ports, peer_name); + struct ovn_port *peer = ovn_port_get_peer( + ports, op->od->router_ports[k]); if (!peer || !peer->nbrp) { continue; } @@ -10209,14 +10213,8 @@ build_arp_resolve_flows_for_lrouter_port( /* Get the Logical_Router_Port that the * Logical_Switch_Port is connected to, as * 'peer'. */ - const char *peer_name = smap_get( - &op->od->router_ports[k]->nbsp->options, - "router-port"); - if (!peer_name) { - continue; - } - - struct ovn_port *peer = ovn_port_find(ports, peer_name); + struct ovn_port *peer = ovn_port_get_peer( + ports, op->od->router_ports[k]); if (!peer || !peer->nbrp) { continue; } @@ -10264,14 +10262,8 @@ build_arp_resolve_flows_for_lrouter_port( !op->sb->chassis) { /* The virtual port is not claimed yet. */ for (size_t i = 0; i < op->od->n_router_ports; i++) { - const char *peer_name = smap_get( - &op->od->router_ports[i]->nbsp->options, - "router-port"); - if (!peer_name) { - continue; - } - - struct ovn_port *peer = ovn_port_find(ports, peer_name); + struct ovn_port *peer = ovn_port_get_peer( + ports, op->od->router_ports[i]); if (!peer || !peer->nbrp) { continue; } @@ -10306,15 +10298,8 @@ build_arp_resolve_flows_for_lrouter_port( /* Get the Logical_Router_Port that the * Logical_Switch_Port is connected to, as * 'peer'. */ - const char *peer_name = smap_get( - &vp->od->router_ports[j]->nbsp->options, - "router-port"); - if (!peer_name) { - continue; - } - struct ovn_port *peer = - ovn_port_find(ports, peer_name); + ovn_port_get_peer(ports, vp->od->router_ports[j]); if (!peer || !peer->nbrp) { continue; } @@ -10351,14 +10336,7 @@ build_arp_resolve_flows_for_lrouter_port( * we need to add logical flows such that it can resolve * ARP entries for all the other router ports connected to * the switch in question. */ - - const char *peer_name = smap_get(&op->nbsp->options, - "router-port"); - if (!peer_name) { - return; - } - - struct ovn_port *peer = ovn_port_find(ports, peer_name); + struct ovn_port *peer = ovn_port_get_peer(ports, op); if (!peer || !peer->nbrp) { return; } From patchwork Thu Jun 3 18:49:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1487387 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Z7o00Ljc; dkim-atps=neutral Received: from smtp2.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fww1b2byFz9sVt for ; Fri, 4 Jun 2021 04:49:55 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id EE249405A4; Thu, 3 Jun 2021 18:49:51 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id UNXyA-Ug9T8S; Thu, 3 Jun 2021 18:49:48 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTP id BA39E4058D; Thu, 3 Jun 2021 18:49:46 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id A38F7C000D; Thu, 3 Jun 2021 18:49:45 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 55520C0001 for ; Thu, 3 Jun 2021 18:49:44 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 33DED83F4B for ; Thu, 3 Jun 2021 18:49:44 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp1.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zEeSrsn-JJiH for ; Thu, 3 Jun 2021 18:49:42 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id 2B9A683E17 for ; Thu, 3 Jun 2021 18:49:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1622746180; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tF8keNlagjTLSthff6u4rCa2iv3CxUuiXjNN2jBEnuw=; b=Z7o00Ljc77irv/Ayjv7VaF6rVxbWR2RIG+E3JmdG/fBAznYxWHy//6+tI0jtF8K8GP0tQM I+1X/+1B5Z7tHHi5Zo3tYfJgHuS0RoEeyGKB//qmLoeaZDNFAwFo8HKfvNoNnvYbPn7bPf AFCh7V6Wdqqg8VpyWhdiSq2xLIUN4bw= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-592-11xrqKyRMg6uTFT-drytCQ-1; Thu, 03 Jun 2021 14:49:34 -0400 X-MC-Unique: 11xrqKyRMg6uTFT-drytCQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 12D27101371E for ; Thu, 3 Jun 2021 18:49:34 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-115-92.rdu2.redhat.com [10.10.115.92]) by smtp.corp.redhat.com (Postfix) with ESMTP id A9712189CE for ; Thu, 3 Jun 2021 18:49:33 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 3 Jun 2021 14:49:29 -0400 Message-Id: <20210603184931.1425441-5-mmichels@redhat.com> In-Reply-To: <20210603184931.1425441-1-mmichels@redhat.com> References: <20210603184931.1425441-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v8 4/6] northd: Add IP routing and ARP resolution flows for NAT/LB addresses. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Dealing with NAT and load balancer IPs has been a bit of a pain point. It requires creating static routes if east-west traffic to those addresses is desired. Further, it requires ARPs to be sent between the logical routers in order to create MAC Bindings. This commit seeks to make things easier. NAT and load balancer addresess automatically have IP routing logical flows and ARP resolution logical flows created for reachable routers. This eliminates the need to create static routes, and it also eliminates the need for ARPs to be sent between logical routers. In this commit, the behavior is not optional. The next commit will introduce configuration to make the behavior optional. Signed-off-by: Mark Michelson --- northd/ovn-northd.c | 133 ++++++++++++++++++++++++++- northd/ovn_northd.dl | 57 ++++++++++++ tests/ovn-northd.at | 214 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 399 insertions(+), 5 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index ef4f5b790..3b9cad80b 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -1353,6 +1353,21 @@ build_datapaths(struct northd_context *ctx, struct hmap *datapaths, } } +/* Structure representing logical router port + * routable addresses. This includes DNAT and Load Balancer + * addresses. This structure will only be filled in if the + * router port is a gateway router port. Otherwise, all pointers + * will be NULL and n_addrs will be 0. + */ +struct ovn_port_routable_addresses { + /* Array of address strings suitable for writing to a database table */ + char **addresses; + /* The addresses field parsed into component parts */ + struct lport_addresses *laddrs; + /* Number of items in each of the above arrays */ + size_t n_addrs; +}; + /* A logical switch port or logical router port. * * In steady state, an ovn_port points to a northbound Logical_Switch_Port @@ -1396,6 +1411,8 @@ struct ovn_port { struct lport_addresses lrp_networks; + struct ovn_port_routable_addresses routables; + /* Logical port multicast data. */ struct mcast_port_info mcast_info; @@ -1422,6 +1439,48 @@ struct ovn_port { struct ovs_list list; /* In list of similar records. */ }; +static void +destroy_routable_addresses(struct ovn_port_routable_addresses *ra) +{ + if (ra->n_addrs == 0) { + return; + } + + for (size_t i = 0; i < ra->n_addrs; i++) { + free(ra->addresses[i]); + destroy_lport_addresses(&ra->laddrs[i]); + } + free(ra->addresses); + free(ra->laddrs); +} + +static char **get_nat_addresses(const struct ovn_port *op, size_t *n); + +static void +assign_routable_addresses(struct ovn_port *op) +{ + size_t n; + char **nats = get_nat_addresses(op, &n); + + if (!nats) { + return; + } + + struct lport_addresses *laddrs = xcalloc(n, sizeof(*laddrs)); + for (size_t i = 0; i < n; i++) { + int ofs; + if (!extract_addresses(nats[i], &laddrs[i], &ofs)){ + continue; + } + } + + /* Everything seems to have worked out */ + op->routables.addresses = nats; + op->routables.laddrs = laddrs; + op->routables.n_addrs = n; +} + + static void ovn_port_set_nb(struct ovn_port *op, const struct nbrec_logical_switch_port *nbsp, @@ -1471,6 +1530,8 @@ ovn_port_destroy(struct hmap *ports, struct ovn_port *port) } free(port->ps_addrs); + destroy_routable_addresses(&port->routables); + destroy_lport_addresses(&port->lrp_networks); free(port->json_key); free(port->key); @@ -2378,6 +2439,8 @@ join_logical_ports(struct northd_context *ctx, * use during flow creation. */ od->l3dgw_port = op; od->l3redirect_port = crp; + + assign_routable_addresses(op); } } } @@ -2501,7 +2564,7 @@ get_nat_addresses(const struct ovn_port *op, size_t *n) { size_t n_nats = 0; struct eth_addr mac; - if (!op->nbrp || !op->od || !op->od->nbr + if (!op || !op->nbrp || !op->od || !op->od->nbr || (!op->od->nbr->n_nat && !op->od->nbr->n_load_balancer) || !eth_addr_from_string(op->nbrp->mac, &mac)) { *n = n_nats; @@ -3089,7 +3152,6 @@ ovn_port_update_sbrec(struct northd_context *ctx, } else { sbrec_port_binding_set_options(op->sb, NULL); } - const char *nat_addresses = smap_get(&op->nbsp->options, "nat-addresses"); size_t n_nats = 0; @@ -3145,6 +3207,7 @@ ovn_port_update_sbrec(struct northd_context *ctx, if (add_router_port_garp) { struct ds garp_info = DS_EMPTY_INITIALIZER; ds_put_format(&garp_info, "%s", op->peer->lrp_networks.ea_s); + for (size_t i = 0; i < op->peer->lrp_networks.n_ipv4_addrs; i++) { ds_put_format(&garp_info, " %s", @@ -3161,7 +3224,6 @@ ovn_port_update_sbrec(struct northd_context *ctx, nats[n_nats - 1] = ds_steal_cstr(&garp_info); ds_destroy(&garp_info); } - sbrec_port_binding_set_nat_addresses(op->sb, (const char **) nats, n_nats); for (size_t i = 0; i < n_nats; i++) { @@ -9876,7 +9938,7 @@ build_ND_RA_flows_for_lrouter(struct ovn_datapath *od, struct hmap *lflows) */ static void build_ip_routing_flows_for_lrouter_port( - struct ovn_port *op, struct hmap *lflows) + struct ovn_port *op, struct hmap *ports,struct hmap *lflows) { if (op->nbrp) { @@ -9893,6 +9955,31 @@ build_ip_routing_flows_for_lrouter_port( op->lrp_networks.ipv6_addrs[i].plen, NULL, false, &op->nbrp->header_, false); } + } else if (lsp_is_router(op->nbsp)) { + struct ovn_port *peer = ovn_port_get_peer(ports, op); + if (!peer || !peer->nbrp || !peer->lrp_networks.n_ipv4_addrs) { + return; + } + + for (int i = 0; i < op->od->n_router_ports; i++) { + struct ovn_port *router_port = ovn_port_get_peer( + ports, op->od->router_ports[i]); + if (!router_port || !router_port->nbrp || router_port == peer) { + continue; + } + + struct ovn_port_routable_addresses *ra = &router_port->routables; + for (size_t j = 0; j < ra->n_addrs; j++) { + struct lport_addresses *laddrs = &ra->laddrs[j]; + for (size_t k = 0; k < laddrs->n_ipv4_addrs; k++) { + add_route(lflows, peer->od, peer, + peer->lrp_networks.ipv4_addrs[0].addr_s, + laddrs->ipv4_addrs[k].network_s, + laddrs->ipv4_addrs[k].plen, NULL, false, + &peer->nbrp->header_, false); + } + } + } } } @@ -10071,6 +10158,36 @@ build_arp_resolve_flows_for_lrouter( } } +static void +routable_addresses_to_lflows(struct hmap *lflows, struct ovn_port *router_port, + struct ovn_port *peer, struct ds *match, + struct ds *actions) +{ + struct ovn_port_routable_addresses *ra = &router_port->routables; + if (!ra->n_addrs) { + return; + } + + for (size_t i = 0; i < ra->n_addrs; i++) { + ds_clear(match); + ds_put_format(match, "outport == %s && "REG_NEXT_HOP_IPV4" == {", peer->json_key); + bool first = true; + for (size_t j = 0; j < ra->laddrs[i].n_ipv4_addrs; j++) { + if (!first) { + ds_put_cstr(match, ", "); + } + ds_put_cstr(match, ra->laddrs[i].ipv4_addrs[j].addr_s); + first = false; + } + ds_put_cstr(match, "}"); + + ds_clear(actions); + ds_put_format(actions, "eth.dst = %s; next;", ra->laddrs[i].ea_s); + ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE, 100, + ds_cstr(match), ds_cstr(actions)); + } +} + /* Local router ingress table ARP_RESOLVE: ARP Resolution. * * Any unicast packet that reaches this table is an IP packet whose @@ -10393,6 +10510,12 @@ build_arp_resolve_flows_for_lrouter_port( ds_cstr(match), ds_cstr(actions), &op->nbsp->header_); } + + if (smap_get(&peer->od->nbr->options, "chassis") || + (peer->od->l3dgw_port && peer == peer->od->l3dgw_port)) { + routable_addresses_to_lflows(lflows, router_port, peer, + match, actions); + } } } @@ -11946,7 +12069,7 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op, &lsi->actions); build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, &lsi->actions); - build_ip_routing_flows_for_lrouter_port(op, lsi->lflows); + build_ip_routing_flows_for_lrouter_port(op, lsi->ports, lsi->lflows); build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, &lsi->actions); build_arp_resolve_flows_for_lrouter_port(op, lsi->lflows, lsi->ports, diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index 60ace7e61..610bfbfb2 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -23,6 +23,7 @@ import multicast import helpers import ipam import vec +import set index Logical_Flow_Index() on sb::Out_Logical_Flow() @@ -6468,6 +6469,45 @@ Route(key, dst.port, dst.src_ip, Some{dst.nexthop}) :- dsts.size() == 1, Some{var dst} = dsts.nth(0). +/* Create routes from peer to port's routable addresses */ +Route(key, peer, src_ip, None) :- + RouterPortRoutableAddresses(port, addresses), + FirstHopRouterPortRoutableAddresses(port, peer_uuid), + peer_lrp in &nb::Logical_Router_Port(._uuid = peer_uuid), + peer in &RouterPort(.lrp = peer_lrp, .networks = networks), + Some{var src} = networks.ipv4_addrs.first(), + var src_ip = IPv4{src.addr}, + var addr = FlatMap(addresses), + var ip4_addr = FlatMap(addr.ipv4_addrs), + var key = RouteKey{DstIp, IPv4{ip4_addr.addr}, ip4_addr.plen}. + +/* This relation indicates that logical router port "port" has routable + * addresses (i.e. DNAT and Load Balancer VIPs) and that logical router + * port "peer" is reachable via a hop across a single logical switch. + */ +relation FirstHopRouterPortRoutableAddresses( + port: uuid, + peer: uuid) +FirstHopRouterPortRoutableAddresses(port_uuid, peer_uuid) :- + FirstHopLogicalRouter(r1, ls), + FirstHopLogicalRouter(r2, ls), + r1 != r2, + LogicalRouterPort(port_uuid, r1), + LogicalRouterPort(peer_uuid, r2), + RouterPortRoutableAddresses(.rport = port_uuid), + lrp in &nb::Logical_Router_Port(._uuid = port_uuid), + peer_lrp in &nb::Logical_Router_Port(._uuid = peer_uuid), + LogicalSwitchRouterPort(_, lrp.name, ls), + LogicalSwitchRouterPort(_, peer_lrp.name, ls). + +relation RouterPortRoutableAddresses( + rport: uuid, + addresses: Set) +RouterPortRoutableAddresses(port.lrp._uuid, addresses) :- + port in &RouterPort(.is_redirect = true), + var addresses = get_nat_addresses(port).filter_map(extract_addresses), + addresses != set_empty(). + /* Return a vector of pairs (1, set[0]), ... (n, set[n - 1]). */ function numbered_vec(set: Set<'A>) : Vec<(bit<16>, 'A)> = { var vec = vec_with_capacity(set.size()); @@ -6956,6 +6996,23 @@ Flow(.logical_datapath = lr_uuid, snat_ips.contains_key(IPv6{addr.addr}), var match_ips = "${addr.addr}".group_by((lr_uuid, lrp_uuid)).to_vec(). +/* Create ARP resolution flows for NAT and LB addresses for first hop + * logical routers + */ +Flow(.logical_datapath = peer.router._uuid, + .stage = s_ROUTER_IN_ARP_RESOLVE(), + .priority = 100, + .__match = "outport == ${peer.json_name} && " ++ rEG_NEXT_HOP() ++ " == {${ips}}", + .actions = "eth.dst = ${addr.ea}; next;", + .external_ids = stage_hint(lrp._uuid)) :- + RouterPortRoutableAddresses(port, addresses), + FirstHopRouterPortRoutableAddresses(port, peer_uuid), + peer in &RouterPort(.lrp = lrp), + lrp._uuid == peer_uuid, + not peer.router.options.get_bool_def("dynamic_neigh_routers", false), + var addr = FlatMap(addresses), + var ips = addr.ipv4_addrs.map(|a| a.addr.to_string()).join(", "). + /* This is a logical switch port that backs a VM or a container. * Extract its addresses. For each of the address, go through all * the router ports attached to the switch (to which this port diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 3c2aef4b0..12a8e47b4 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -3594,3 +3594,217 @@ check ovn-nbctl --wait=sb sync OVS_APP_EXIT_AND_WAIT([NORTHD_TYPE]) AT_CLEANUP ]) + +# XXX This test currently only runs for ovn-northd.c. The test fails +# with ovn-northd-ddlog because of the section where 2 HA_Chassis_Groups +# are used by 2 routers. For some reason, this causes ovn-northd-ddlog +# to stop processing new changes to the northbound database and to +# seemingly infinitely loop. This issue has been reported, but there is +# currently no fix for it. Once this issue is fixed, we can run this +# test for both C and DDLog versions of northd. +AT_SETUP([ovn -- NAT and Load Balancer flows]) + +# Determine if expected flows are present. The only parameter to this +# function is the number of expected flows per NAT destination address. +# This should always be either 0 or 1. 0 means that we do not expect +# lflows to be present. 1 means we expect an lflow to be present +check_lflows() { + expected=$1 + ro1_flows=$(ovn-sbctl lflow-list ro1) + + ro1_ip_routing=$(grep lr_in_ip_routing <<< "$ro1_flows") + match=$(grep -c "match=(ip4.dst == 20.0.0.100/32)" <<< "$ro1_ip_routing") + AT_CHECK([test "$expected" = "$match"]) + + ro1_arp_resolve=$(grep lr_in_arp_resolve <<< "$ro1_flows") + match=$(grep -c 'match=(outport == "ro1-sw" && reg0 == {20.0.0.100})' <<< "$ro1_arp_resolve") + AT_CHECK([test "$expected" = "$match"]) + + ro2_flows=$(ovn-sbctl lflow-list ro2) + + ro2_ip_routing=$(grep lr_in_ip_routing <<< "$ro2_flows") + match=$(grep -c "match=(ip4.dst == 10.0.0.100/32)" <<< "$ro2_ip_routing") + AT_CHECK([test "$expected" = "$match"]) + + ro2_arp_resolve=$(grep lr_in_arp_resolve <<< "$ro2_flows") + match=$(grep -c 'match=(outport == "ro2-sw" && reg0 == {10.0.0.100})' <<< "$ro2_arp_resolve") + AT_CHECK([test "$expected" = "$match"]) +} + +ovn_start + +AS_BOX([Setting up the logical network]) + +check ovn-nbctl ls-add sw + +check ovn-nbctl lr-add ro1 +check ovn-nbctl lrp-add ro1 ro1-sw 00:00:00:00:00:01 10.0.0.1/24 +check ovn-nbctl lsp-add sw sw-ro1 + +check ovn-nbctl lr-add ro2 +check ovn-nbctl lrp-add ro2 ro2-sw 00:00:00:00:00:02 20.0.0.1/24 +check ovn-nbctl --wait=sb lsp-add sw sw-ro2 + +check ovn-nbctl ls-add ls1 +check ovn-nbctl lsp-add ls1 vm1 +check ovn-nbctl lsp-set-addresses vm1 "00:00:00:00:01:02 192.168.1.2" +check ovn-nbctl lrp-add ro1 ro1-ls1 00:00:00:00:01:01 192.168.1.1/24 +check ovn-nbctl lsp-add ls1 ls1-ro1 +check ovn-nbctl lsp-set-type ls1-ro1 router +check ovn-nbctl lsp-set-addresses ls1-ro1 router +check ovn-nbctl lsp-set-options ls1-ro1 router-port=ro1-ls1 + +check ovn-nbctl ls-add ls2 +check ovn-nbctl lsp-add ls2 vm2 +check ovn-nbctl lsp-set-addresses vm2 "00:00:00:00:02:02 192.168.2.2" +check ovn-nbctl lrp-add ro2 ro2-ls2 00:00:00:00:02:01 192.168.2.1/24 +check ovn-nbctl lsp-add ls2 ls2-ro2 +check ovn-nbctl lsp-set-type ls2-ro2 router +check ovn-nbctl lsp-set-addresses ls2-ro2 router +check ovn-nbctl lsp-set-options ls2-ro2 router-port=ro2-ls2 + +check ovn-nbctl ha-chassis-group-add grp1 +check ovn-nbctl ha-chassis-group-add-chassis grp1 hv1 100 +grp1_uuid=$(ovn-nbctl --columns=_uuid --bare find HA_Chassis_group name=grp1) + +check ovn-nbctl ha-chassis-group-add grp2 +check ovn-nbctl ha-chassis-group-add-chassis grp2 hv2 100 +grp2_uuid=$(ovn-nbctl --columns=_uuid --bare find HA_Chassis_group name=grp2) + +AS_BOX([Checking that unconnected logical switch ports generate no lflows]) + +check_lflows 0 + +AS_BOX([Checking that connected logical switch ports have no lflows for non-gateway ports]) + +check ovn-nbctl lsp-set-type sw-ro1 router +check ovn-nbctl lsp-set-addresses sw-ro1 router +check ovn-nbctl lsp-set-options sw-ro1 router-port=ro1-sw + +check ovn-nbctl lsp-set-type sw-ro2 router +check ovn-nbctl lsp-set-addresses sw-ro2 router +check ovn-nbctl --wait=sb lsp-set-options sw-ro2 router-port=ro2-sw + +check_lflows 0 + +AS_BOX([Checking that NAT flows are not installed for non-gateway routers]) + +check ovn-nbctl lr-nat-add ro1 dnat 10.0.0.100 192.168.1.100 +check ovn-nbctl lr-nat-add ro2 dnat 20.0.0.100 192.168.2.100 + +check_lflows 0 + +AS_BOX([Checking that NAT flows are installed for gateway routers]) + +check ovn-nbctl lrp-set-gateway-chassis ro1-sw hv1 100 +check ovn-nbctl --wait=sb lrp-set-gateway-chassis ro2-sw hv2 100 + +check_lflows 1 + +AS_BOX([Checking that NAT flows are not installed for routers with gateway chassis removed]) + +check ovn-nbctl lrp-del-gateway-chassis ro1-sw hv1 +check ovn-nbctl --wait=sb lrp-del-gateway-chassis ro2-sw hv2 + +check_lflows 0 + +AS_BOX([Checking that NAT flows are installed for routers with HA_Chassis_Group]) + +check ovn-nbctl set logical_router_port ro1-sw ha_chassis_group="$grp1_uuid" +check ovn-nbctl --wait=sb set logical_router_port ro2-sw ha_chassis_group="$grp2_uuid" + +check_lflows 1 + +AS_BOX([Checking that NAT flows are not installed for routers with HA_Chassis_Group removed]) + +check ovn-nbctl clear logical_router_port ro1-sw ha_chassis_group +check ovn-nbctl --wait=sb clear logical_router_port ro2-sw ha_chassis_group + +check_lflows 0 + +AS_BOX([Checking that Floating IP NAT flows are not installed with no gateway port set]) + +check ovn-nbctl lr-nat-del ro1 +check ovn-nbctl lr-nat-del ro2 + +check ovn-nbctl lr-nat-add ro1 dnat_and_snat 10.0.0.100 192.168.1.2 vm1 00:00:00:00:00:01 +check ovn-nbctl lr-nat-add ro2 dnat_and_snat 20.0.0.100 192.168.2.2 vm2 00:00:00:00:00:02 + +check_lflows 0 + +AS_BOX([Checking that Floating IP NAT flows are installed for gateway routers]) + +check ovn-nbctl lrp-set-gateway-chassis ro1-sw hv1 100 +check ovn-nbctl --wait=sb lrp-set-gateway-chassis ro2-sw hv2 100 + +check_lflows 1 + +AS_BOX([Checking that Floating IP NAT flows are not installed for routers with gateway chassis removed]) + +check ovn-nbctl lrp-del-gateway-chassis ro1-sw hv1 +check ovn-nbctl --wait=sb lrp-del-gateway-chassis ro2-sw hv2 + +check_lflows 0 + +AS_BOX([Checking that Floating IP NAT flows are installed for routers with ha_chassis_group]) + +grp1_uuid=$(ovn-nbctl --columns=_uuid --bare find HA_Chassis_group name=grp1) +check ovn-nbctl set logical_router_port ro1-sw ha_chassis_group="$grp1_uuid" + +grp2_uuid=$(ovn-nbctl --columns=_uuid --bare find HA_Chassis_group name=grp2) +check ovn-nbctl --wait=sb set logical_router_port ro2-sw ha_chassis_group="$grp2_uuid" + +check_lflows 1 + +AS_BOX([Checking that Floating IP NAT flows are not installed for routers with HA_Chassis_Group removed]) + +check ovn-nbctl clear logical_router_port ro1-sw ha_chassis_group +check ovn-nbctl --wait=sb clear logical_router_port ro2-sw ha_chassis_group + +check_lflows 0 + +AS_BOX([Checking that Load Balancer VIP flows are not installed for routers with no gateway port]) + +check ovn-nbctl lr-nat-del ro1 +check ovn-nbctl lr-nat-del ro2 + +check ovn-nbctl lb-add lb1 10.0.0.100 192.168.1.2 +check ovn-nbctl lr-lb-add ro1 lb1 + +check ovn-nbctl lb-add lb2 20.0.0.100 192.168.2.2 +check ovn-nbctl lr-lb-add ro2 lb2 + +check_lflows 0 + +AS_BOX([Checking that Load Balancer VIP flows are installed for gateway routers]) + +check ovn-nbctl lrp-set-gateway-chassis ro1-sw hv1 100 +check ovn-nbctl --wait=sb lrp-set-gateway-chassis ro2-sw hv2 100 + +check_lflows 1 + +AS_BOX([Checking that Load Balancer VIP flows are not installed for routers with gateway chassis removed]) + +check ovn-nbctl lrp-del-gateway-chassis ro1-sw hv1 +check ovn-nbctl --wait=sb lrp-del-gateway-chassis ro2-sw hv2 + +check_lflows 0 + +AS_BOX([Checking that Load Balancer VIP flows are installed for routers with ha_chassis_group]) + +grp1_uuid=$(ovn-nbctl --columns=_uuid --bare find HA_Chassis_group name=grp1) +check ovn-nbctl set logical_router_port ro1-sw ha_chassis_group="$grp1_uuid" + +grp2_uuid=$(ovn-nbctl --columns=_uuid --bare find HA_Chassis_group name=grp2) +check ovn-nbctl --wait=sb set logical_router_port ro2-sw ha_chassis_group="$grp2_uuid" + +check_lflows 1 + +AS_BOX([Checking that Load Balancer VIP flows are not iinstalled for routers with HA_Chassis_Group removed]) + +check ovn-nbctl clear logical_router_port ro1-sw ha_chassis_group +check ovn-nbctl --wait=sb clear logical_router_port ro2-sw ha_chassis_group + +check_lflows 0 + +AT_CLEANUP From patchwork Thu Jun 3 18:49:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1487386 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=GVfjFOoq; dkim-atps=neutral Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fww1W4jTrz9sVb for ; Fri, 4 Jun 2021 04:49:51 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id A788240597; Thu, 3 Jun 2021 18:49:48 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id uK5sTKFI5jj7; Thu, 3 Jun 2021 18:49:46 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp2.osuosl.org (Postfix) with ESMTP id 5D08F40582; Thu, 3 Jun 2021 18:49:45 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 88C92C0028; Thu, 3 Jun 2021 18:49:43 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7CC05C0019 for ; Thu, 3 Jun 2021 18:49:41 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 76AD340568 for ; Thu, 3 Jun 2021 18:49:41 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Gdi9Zc6PtelL for ; Thu, 3 Jun 2021 18:49:39 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by smtp2.osuosl.org (Postfix) with ESMTPS id 4518840546 for ; Thu, 3 Jun 2021 18:49:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1622746178; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VmdXWSWvLiQEywZ0AFBcUFWlqwTRCJAmpIlNGRVMfho=; b=GVfjFOoqpQBU41Yzt/P3mzFKrs7gAJB/1yP/+QnLlIFfc5pL2y6u6W1jBzVDuWsdVTsqI1 7Rm+7M9beHp45kJpqRJfhOire42eJHKSuPlOIJx3jT2X+GWIizjxN3FskEtTNlppZuW2GS hv0fVKWoRG/+sM5aGq7PlbtMSrsTH7c= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-479-0dX14a9UPQ-5-8lv0dAIFQ-1; Thu, 03 Jun 2021 14:49:35 -0400 X-MC-Unique: 0dX14a9UPQ-5-8lv0dAIFQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 9F2B36409D for ; Thu, 3 Jun 2021 18:49:34 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-115-92.rdu2.redhat.com [10.10.115.92]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3E86160657 for ; Thu, 3 Jun 2021 18:49:34 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 3 Jun 2021 14:49:30 -0400 Message-Id: <20210603184931.1425441-6-mmichels@redhat.com> In-Reply-To: <20210603184931.1425441-1-mmichels@redhat.com> References: <20210603184931.1425441-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v8 5/6] northd: Add options to automatically add routes for NATs and LBs. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Load_Balancer and NAT entries have a new option, "add_route" that can be set to automatically add routes to those addresses to neighbor routers, therefore eliminating the need to create static routes. Signed-off-by: Mark Michelson --- northd/ovn-northd.c | 29 +++++++++++++++++++++-------- northd/ovn_northd.dl | 23 ++++++++++++++++------- ovn-nb.xml | 29 ++++++++++++++++++++++++++++- tests/ovn-nbctl.at | 3 +++ tests/ovn-northd.at | 40 ++++++++++++++++++++++++++++++++++++---- utilities/ovn-nbctl.c | 25 +++++++++++++++++++------ 6 files changed, 123 insertions(+), 26 deletions(-) diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 3b9cad80b..414bf9c48 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -1454,13 +1454,14 @@ destroy_routable_addresses(struct ovn_port_routable_addresses *ra) free(ra->laddrs); } -static char **get_nat_addresses(const struct ovn_port *op, size_t *n); +static char **get_nat_addresses(const struct ovn_port *op, size_t *n, + bool routable_only); static void assign_routable_addresses(struct ovn_port *op) { size_t n; - char **nats = get_nat_addresses(op, &n); + char **nats = get_nat_addresses(op, &n, true); if (!nats) { return; @@ -2510,7 +2511,7 @@ join_logical_ports(struct northd_context *ctx, } static void -get_router_load_balancer_ips(const struct ovn_datapath *od, +get_router_load_balancer_ips(const struct ovn_datapath *od, bool routable_only, struct sset *all_ips_v4, struct sset *all_ips_v6) { if (!od->nbr) { @@ -2522,6 +2523,11 @@ get_router_load_balancer_ips(const struct ovn_datapath *od, struct smap *vips = &lb->vips; struct smap_node *node; + if (routable_only && + !smap_get_bool(&lb->options, "add_route", false)) { + continue; + } + SMAP_FOR_EACH (node, vips) { /* node->key contains IP:port or just IP. */ char *ip_address; @@ -2560,7 +2566,7 @@ get_router_load_balancer_ips(const struct ovn_datapath *od, * The caller must free each of the n returned strings with free(), * and must free the returned array when it is no longer needed. */ static char ** -get_nat_addresses(const struct ovn_port *op, size_t *n) +get_nat_addresses(const struct ovn_port *op, size_t *n, bool routable_only) { size_t n_nats = 0; struct eth_addr mac; @@ -2583,6 +2589,12 @@ get_nat_addresses(const struct ovn_port *op, size_t *n) const struct nbrec_nat *nat = op->od->nbr->nat[i]; ovs_be32 ip, mask; + if (routable_only && + (!strcmp(nat->type, "snat") || + !smap_get_bool(&nat->options, "add_route", false))) { + continue; + } + char *error = ip_parse_masked(nat->external_ip, &ip, &mask); if (error || mask != OVS_BE32_MAX) { free(error); @@ -2636,7 +2648,8 @@ get_nat_addresses(const struct ovn_port *op, size_t *n) /* Two sets to hold all load-balancer vips. */ struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); - get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + get_router_load_balancer_ips(op->od, routable_only, + &all_ips_v4, &all_ips_v6); const char *ip_address; SSET_FOR_EACH (ip_address, &all_ips_v4) { @@ -3159,7 +3172,7 @@ ovn_port_update_sbrec(struct northd_context *ctx, if (nat_addresses && !strcmp(nat_addresses, "router")) { if (op->peer && op->peer->od && (chassis || op->peer->od->l3redirect_port)) { - nats = get_nat_addresses(op->peer, &n_nats); + nats = get_nat_addresses(op->peer, &n_nats, false); } /* Only accept manual specification of ethernet address * followed by IPv4 addresses on type "l3gateway" ports. */ @@ -6615,7 +6628,7 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op, struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); - get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + get_router_load_balancer_ips(op->od, false, &all_ips_v4, &all_ips_v6); const char *ip_addr; const char *ip_addr_next; @@ -11177,7 +11190,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, /* A set to hold all load-balancer vips that need ARP responses. */ struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); - get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6); + get_router_load_balancer_ips(op->od, false, &all_ips_v4, &all_ips_v6); const char *ip_address; if (sset_count(&all_ips_v4)) { diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index 610bfbfb2..794b86ee1 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -201,7 +201,7 @@ OutProxy_Port_Binding(._uuid = lsp._uuid, Some{"router"} -> match ((l3dgw_port, opt_chassis, peer)) { (None, None, _) -> set_empty(), (_, _, None) -> set_empty(), - (_, _, Some{rport}) -> get_nat_addresses(rport) + (_, _, Some{rport}) -> get_nat_addresses(rport, false) }, Some{nat_addresses} -> { /* Only accept manual specification of ethernet address @@ -298,12 +298,16 @@ OutProxy_Port_Binding(._uuid = lrp._uuid, }. /* */ -function get_router_load_balancer_ips(router: Intern) : +function get_router_load_balancer_ips(router: Intern, + routable_only: bool) : (Set, Set) = { var all_ips_v4 = set_empty(); var all_ips_v6 = set_empty(); for (lb in router.lbs) { + if (routable_only and not lb.options.get_bool_def("add_route", false)) { + continue; + }; for (kv in lb.vips) { (var vip, _) = kv; /* node->key contains IP:port or just IP. */ @@ -325,7 +329,7 @@ function get_router_load_balancer_ips(router: Intern) : * external IP addresses of all NAT rules defined on that router, and all * of the IP addresses used in load balancer VIPs defined on that router. */ -function get_nat_addresses(rport: Intern): Set = +function get_nat_addresses(rport: Intern, routable_only: bool): Set = { var addresses = set_empty(); var has_redirect = rport.router.l3dgw_port.is_some(); @@ -337,6 +341,11 @@ function get_nat_addresses(rport: Intern): Set = /* Get NAT IP addresses. */ for (nat in rport.router.nats) { + if (routable_only and + (nat.nat.__type == "snat" or + not nat.nat.options.get_bool_def("add_route", false))) { + continue; + }; /* Determine whether this NAT rule satisfies the conditions for * distributed NAT processing. */ if (has_redirect and nat.nat.__type == "dnat_and_snat" and @@ -379,7 +388,7 @@ function get_nat_addresses(rport: Intern): Set = }; /* A set to hold all load-balancer vips. */ - (var all_ips_v4, var all_ips_v6) = get_router_load_balancer_ips(rport.router); + (var all_ips_v4, var all_ips_v6) = get_router_load_balancer_ips(rport.router, routable_only); for (ip_address in set_union(all_ips_v4, all_ips_v6)) { c_addresses = c_addresses ++ " ${ip_address}"; @@ -4099,7 +4108,7 @@ function get_arp_forward_ips(rp: Intern): (Set, Set) var all_ips_v6 = set_empty(); (var lb_ips_v4, var lb_ips_v6) - = get_router_load_balancer_ips(rp.router); + = get_router_load_balancer_ips(rp.router, false); for (a in lb_ips_v4) { /* Check if the ovn port has a network configured on which we could * expect ARP requests for the LB VIP. @@ -5091,7 +5100,7 @@ var residence_check = match (is_redirect) { true -> Some{"is_chassis_resident(${router.redirect_port_name})"}, false -> None } in { - (var all_ips_v4, _) = get_router_load_balancer_ips(router) in { + (var all_ips_v4, _) = get_router_load_balancer_ips(router, false) in { if (not all_ips_v4.is_empty()) { LogicalRouterArpFlow(.lr = router, .lrp = Some{lrp}, @@ -6505,7 +6514,7 @@ relation RouterPortRoutableAddresses( addresses: Set) RouterPortRoutableAddresses(port.lrp._uuid, addresses) :- port in &RouterPort(.is_redirect = true), - var addresses = get_nat_addresses(port).filter_map(extract_addresses), + var addresses = get_nat_addresses(port, true).filter_map(extract_addresses), addresses != set_empty(). /* Return a vector of pairs (1, set[0]), ... (n, set[n - 1]). */ diff --git a/ovn-nb.xml b/ovn-nb.xml index 47f25eac1..6edf62d98 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -1697,6 +1697,16 @@ option, the force_snat_for_lb option configured for the router pipeline will not be applied for this load balancer. + + + If set to true, then neighbor routers will have logical + flows added that will allow for routing to the VIP IP. It also will + have ARP resolution logical flows added. By setting this option, it + means there is no reason to create a + from neighbor routers to + this NAT address. It also means that no ARP request is required for + neighbor routers to learn the IP-MAC mapping for this VIP IP. + @@ -2034,7 +2044,13 @@ false by default. It is recommended to set to true when a large number of logical routers are connected to the same logical switch but most of them never need to - send traffic between each other. + send traffic between each other. By default, ovn-northd does not + create mappings to NAT and load balancer addresess. However, for NAT + and load balancer addresses that have the add_route + option added, ovn-northd will create logical flows that map NAT and + load balancer IP addresses to the appropriate MAC address. Setting + dynamic_neigh_routers to true will prevent + the automatic creation of these logical flows.

@@ -3007,6 +3023,17 @@ tracking state or not. + + If set to true, then neighbor routers will have logical + flows added that will allow for routing to the NAT address. It also will + have ARP resolution logical flows added. By setting this option, it means + there is no reason to create a + from neighbor routers to this NAT address. It also means that no ARP + request is required for neighbor routers to learn the IP-MAC mapping for + this NAT address. This option only applies to NATs of type + dnat and dnat_and_snat. + + See External IDs at the beginning of this document. diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 1058d418a..828777b82 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -465,6 +465,9 @@ AT_CHECK([ovn-nbctl lsp-add ls0 lp0]) AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02], [1], [], [ovn-nbctl: invalid mac address 00:00:00:01:02. ]) +AT_CHECK([ovn-nbctl --add-route lr-nat-add lr0 snat 30.0.0.2 192.168.1.2], [1], [], +[ovn-nbctl: routes cannot be added for snat types. +]) dnl Add snat and dnat AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24]) diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 12a8e47b4..9e4f2ca62 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -3694,11 +3694,20 @@ check ovn-nbctl lr-nat-add ro2 dnat 20.0.0.100 192.168.2.100 check_lflows 0 -AS_BOX([Checking that NAT flows are installed for gateway routers]) +AS_BOX([Checking that non-routable NAT flows are not installed for gateway routers]) check ovn-nbctl lrp-set-gateway-chassis ro1-sw hv1 100 check ovn-nbctl --wait=sb lrp-set-gateway-chassis ro2-sw hv2 100 +check_lflows 0 + +AS_BOX([Checking that routable NAT flows are installed when gateway chassis exists]) + +check ovn-nbctl lr-nat-del ro1 +check ovn-nbctl lr-nat-del ro2 +check ovn-nbctl --add-route lr-nat-add ro1 dnat 10.0.0.100 192.168.1.100 +check ovn-nbctl --add-route lr-nat-add ro2 dnat 20.0.0.100 192.168.2.100 + check_lflows 1 AS_BOX([Checking that NAT flows are not installed for routers with gateway chassis removed]) @@ -3732,11 +3741,20 @@ check ovn-nbctl lr-nat-add ro2 dnat_and_snat 20.0.0.100 192.168.2.2 vm2 00:00:00 check_lflows 0 -AS_BOX([Checking that Floating IP NAT flows are installed for gateway routers]) +AS_BOX([Checking that non-routable Floating IP NAT flows are not installed for gateway routers]) check ovn-nbctl lrp-set-gateway-chassis ro1-sw hv1 100 check ovn-nbctl --wait=sb lrp-set-gateway-chassis ro2-sw hv2 100 +check_lflows 0 + +AS_BOX([Checking that routable Floating IP NAT flows are installed for gateway routers]) +check ovn-nbctl lr-nat-del ro1 +check ovn-nbctl lr-nat-del ro2 + +check ovn-nbctl --add-route lr-nat-add ro1 dnat_and_snat 10.0.0.100 192.168.1.2 vm1 00:00:00:00:00:01 +check ovn-nbctl --add-route lr-nat-add ro2 dnat_and_snat 20.0.0.100 192.168.2.2 vm2 00:00:00:00:00:02 + check_lflows 1 AS_BOX([Checking that Floating IP NAT flows are not installed for routers with gateway chassis removed]) @@ -3772,15 +3790,29 @@ check ovn-nbctl lb-add lb1 10.0.0.100 192.168.1.2 check ovn-nbctl lr-lb-add ro1 lb1 check ovn-nbctl lb-add lb2 20.0.0.100 192.168.2.2 -check ovn-nbctl lr-lb-add ro2 lb2 +check ovn-nbctl --wait=sb lr-lb-add ro2 lb2 check_lflows 0 -AS_BOX([Checking that Load Balancer VIP flows are installed for gateway routers]) +AS_BOX([Checking that non-routable Load Balancer VIP flows are not installed for gateway routers]) check ovn-nbctl lrp-set-gateway-chassis ro1-sw hv1 100 check ovn-nbctl --wait=sb lrp-set-gateway-chassis ro2-sw hv2 100 +check_lflows 0 + +AS_BOX([Checking that routable Load Balancer VIP flows are installed for gateway routers]) + +check ovn-nbctl lr-lb-del ro1 lb1 +check ovn-nbctl lr-lb-del ro2 lb2 +check ovn-nbctl lb-del lb1 +check ovn-nbctl lb-del lb2 + +check ovn-nbctl --add-route lb-add lb1 10.0.0.100 192.168.1.2 +check ovn-nbctl --add-route lb-add lb2 20.0.0.100 192.168.2.2 +check ovn-nbctl lr-lb-add ro1 lb1 +check ovn-nbctl --wait=sb lr-lb-add ro2 lb2 + check_lflows 1 AS_BOX([Checking that Load Balancer VIP flows are not installed for routers with gateway chassis removed]) diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index dc13fa9ca..f4a859495 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -367,6 +367,7 @@ Policy commands:\n\ NAT commands:\n\ [--stateless]\n\ [--portrange]\n\ + [--add-route]\n\ lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]\n\ [EXTERNAL_PORT_RANGE]\n\ add a NAT to ROUTER\n\ @@ -2703,6 +2704,7 @@ nbctl_lb_add(struct ctl_context *ctx) bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") != NULL; bool empty_backend_rej = shash_find(&ctx->options, "--reject") != NULL; bool empty_backend_event = shash_find(&ctx->options, "--event") != NULL; + bool add_route = shash_find(&ctx->options, "--add-route") != NULL; if (empty_backend_event && empty_backend_rej) { ctl_error(ctx, @@ -2822,14 +2824,18 @@ nbctl_lb_add(struct ctl_context *ctx) smap_add(CONST_CAST(struct smap *, &lb->vips), lb_vip_normalized, ds_cstr(&lb_ips_new)); nbrec_load_balancer_set_vips(lb, &lb->vips); + struct smap options = SMAP_INITIALIZER(&options); if (empty_backend_rej) { - const struct smap options = SMAP_CONST1(&options, "reject", "true"); - nbrec_load_balancer_set_options(lb, &options); + smap_add(&options, "reject", "true"); } if (empty_backend_event) { - const struct smap options = SMAP_CONST1(&options, "event", "true"); - nbrec_load_balancer_set_options(lb, &options); + smap_add(&options, "event", "true"); + } + if (add_route) { + smap_add(&options, "add_route", "true"); } + nbrec_load_balancer_set_options(lb, &options); + smap_destroy(&options); out: ds_destroy(&lb_ips_new); @@ -4400,6 +4406,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx) char *new_logical_ip = NULL; char *new_external_ip = NULL; bool is_portrange = shash_find(&ctx->options, "--portrange") != NULL; + bool add_route = shash_find(&ctx->options, "--add-route") != NULL; char *error = lr_by_name_or_uuid(ctx, ctx->argv[1], true, &lr); if (error) { @@ -4516,6 +4523,11 @@ nbctl_lr_nat_add(struct ctl_context *ctx) } int is_snat = !strcmp("snat", nat_type); + + if (is_snat && add_route) { + ctl_error(ctx, "routes cannot be added for snat types."); + goto cleanup; + } for (size_t i = 0; i < lr->n_nat; i++) { const struct nbrec_nat *nat = lr->nat[i]; @@ -4596,6 +4608,7 @@ nbctl_lr_nat_add(struct ctl_context *ctx) } smap_add(&nat_options, "stateless", stateless ? "true":"false"); + smap_add(&nat_options, "add_route", add_route ? "true": "false"); nbrec_nat_set_options(nat, &nat_options); smap_destroy(&nat_options); @@ -6714,7 +6727,7 @@ static const struct ctl_command_syntax nbctl_commands[] = { "ROUTER TYPE EXTERNAL_IP LOGICAL_IP" "[LOGICAL_PORT EXTERNAL_MAC] [EXTERNAL_PORT_RANGE]", nbctl_pre_lr_nat_add, nbctl_lr_nat_add, - NULL, "--may-exist,--stateless,--portrange", RW }, + NULL, "--may-exist,--stateless,--portrange,--add-route", RW }, { "lr-nat-del", 1, 3, "ROUTER [TYPE [IP]]", nbctl_pre_lr_nat_del, nbctl_lr_nat_del, NULL, "--if-exists", RW }, { "lr-nat-list", 1, 1, "ROUTER", nbctl_pre_lr_nat_list, @@ -6725,7 +6738,7 @@ static const struct ctl_command_syntax nbctl_commands[] = { /* load balancer commands. */ { "lb-add", 3, 4, "LB VIP[:PORT] IP[:PORT]... [PROTOCOL]", nbctl_pre_lb_add, nbctl_lb_add, NULL, - "--may-exist,--add-duplicate,--reject,--event", RW }, + "--may-exist,--add-duplicate,--reject,--event,--add-route", RW }, { "lb-del", 1, 2, "LB [VIP]", nbctl_pre_lb_del, nbctl_lb_del, NULL, "--if-exists", RW }, { "lb-list", 0, 1, "[LB]", nbctl_pre_lb_list, nbctl_lb_list, NULL, "", RO }, From patchwork Thu Jun 3 18:49:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1487388 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=smtp1.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XuDS6+OZ; dkim-atps=neutral Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4Fww1g1W3nz9sPf for ; Fri, 4 Jun 2021 04:49:59 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 6791683E25; Thu, 3 Jun 2021 18:49:57 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id RzgRIic3BIWj; Thu, 3 Jun 2021 18:49:53 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp1.osuosl.org (Postfix) with ESMTP id D0271842F0; Thu, 3 Jun 2021 18:49:49 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id A133FC000D; Thu, 3 Jun 2021 18:49:49 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 19506C0001 for ; Thu, 3 Jun 2021 18:49:48 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id F152E83E25 for ; Thu, 3 Jun 2021 18:49:47 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id RMKergeQzxe1 for ; Thu, 3 Jun 2021 18:49:44 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id 8809B83E5B for ; Thu, 3 Jun 2021 18:49:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1622746181; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ynetX8dPoepoIESIvzWdEjxdKrcbmOlsyMvpyJEGGHw=; b=XuDS6+OZ7UWuaLy39t3DmNCt6p5npvvzmAoZk/tF18f+EZyuWR4sUu0bSAIK6Cz4uBp0OI bnskoj/ep5eeYxfC9shj+zJuSY64rUgXhKbt2GN4y8padEuWPhISF5gtdhfeUBsKV+2M6O yoydKAUNNDyijq+zoI6hRkKfa+EmZPg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-523-dFGa_sxRODyi6T48drYCiA-1; Thu, 03 Jun 2021 14:49:36 -0400 X-MC-Unique: dFGa_sxRODyi6T48drYCiA-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1FC4D640A1 for ; Thu, 3 Jun 2021 18:49:35 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-115-92.rdu2.redhat.com [10.10.115.92]) by smtp.corp.redhat.com (Postfix) with ESMTP id CC5EC189CE for ; Thu, 3 Jun 2021 18:49:34 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Thu, 3 Jun 2021 14:49:31 -0400 Message-Id: <20210603184931.1425441-7-mmichels@redhat.com> In-Reply-To: <20210603184931.1425441-1-mmichels@redhat.com> References: <20210603184931.1425441-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v8 6/6] northd: Flood ARPs to routers for "unreachable" addresses. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Previously, ARP TPAs were filtered down only to "reachable" addresses. Reachable addresses are all router interface addresses, as well as NAT external addresses and load balancer VIPs that are within the subnet handled by a router's port. However, it is possible that in some configurations, CMSes purposely configure NAT or load balancer addresses on a router that are outside the router's subnets, and they expect the router to respond to ARPs for those addresses. This commit adds a higher priority flow to logical switches that makes it so ARPs targeted at "unreachable" addresses are flooded to all ports. This way, the ARPs can reach the router appropriately and receive a response. Reported at: https://bugzilla.redhat.com/show_bug.cgi?id=1929901 Signed-off-by: Mark Michelson --- northd/ovn-northd.8.xml | 8 +++ northd/ovn-northd.c | 153 +++++++++++++++++++++++++++------------- northd/ovn_northd.dl | 101 ++++++++++++++++++++------ tests/ovn-northd.at | 99 ++++++++++++++++++++++++++ tests/system-ovn.at | 102 +++++++++++++++++++++++++++ 5 files changed, 391 insertions(+), 72 deletions(-) diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index bb77689de..8bb77bf6c 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -1549,6 +1549,14 @@ output; logical ports. +
  • + Priority-80 flows for each IP address/VIP/NAT address configured + outside its owning router port's subnet. These flows match ARP + requests and ND packets for the specific IP addresses. Matched packets + are forwarded to the MC_FLOOD multicast group which + contains all connected logical ports. +
  • +
  • Priority-75 flows for each port connected to a logical router matching self originated ARP request/ND packets. These packets diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 414bf9c48..eacbab96a 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -6539,44 +6539,48 @@ build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op, ds_destroy(&match); } -/* - * Ingress table 19: Flows that forward ARP/ND requests only to the routers - * that own the addresses. Other ARP/ND packets are still flooded in the - * switching domain as regular broadcast. - */ static void -build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, - int addr_family, - struct ovn_port *patch_op, - struct ovn_datapath *od, - uint32_t priority, - struct hmap *lflows, - const struct ovsdb_idl_row *stage_hint) +arp_nd_ns_match(struct sset *ips, int addr_family, struct ds *match) { - struct ds match = DS_EMPTY_INITIALIZER; - struct ds actions = DS_EMPTY_INITIALIZER; /* Packets received from VXLAN tunnels have already been through the * router pipeline so we should skip them. Normally this is done by the * multicast_group implementation (VXLAN packets skip table 32 which * delivers to patch ports) but we're bypassing multicast_groups. */ - ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && "); + ds_put_cstr(match, FLAGBIT_NOT_VXLAN " && "); if (addr_family == AF_INET) { - ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { "); + ds_put_cstr(match, "arp.op == 1 && arp.tpa == {"); } else { - ds_put_cstr(&match, "nd_ns && nd.target == { "); + ds_put_cstr(match, "nd_ns && nd.target == {"); } const char *ip_address; SSET_FOR_EACH (ip_address, ips) { - ds_put_format(&match, "%s, ", ip_address); + ds_put_format(match, "%s, ", ip_address); } - ds_chomp(&match, ' '); - ds_chomp(&match, ','); - ds_put_cstr(&match, "}"); + ds_chomp(match, ' '); + ds_chomp(match, ','); + ds_put_cstr(match, "}"); +} + +/* + * Ingress table 19: Flows that forward ARP/ND requests only to the routers + * that own the addresses. Other ARP/ND packets are still flooded in the + * switching domain as regular broadcast. + */ +static void +build_lswitch_rport_arp_req_flow_for_reachable_ip(struct sset *ips, + int addr_family, struct ovn_port *patch_op, struct ovn_datapath *od, + uint32_t priority, struct hmap *lflows, + const struct ovsdb_idl_row *stage_hint) +{ + struct ds match = DS_EMPTY_INITIALIZER; + struct ds actions = DS_EMPTY_INITIALIZER; + + arp_nd_ns_match(ips, addr_family, &match); /* Send a the packet to the router pipeline. If the switch has non-router * ports then flood it there as well. @@ -6599,6 +6603,30 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips, ds_destroy(&actions); } +/* + * Ingress table 19: Flows that forward ARP/ND requests for "unreachable" IPs + * (NAT or load balancer IPs configured on a router that are outside the + * router's configured subnets). + * These ARP/ND packets are flooded in the switching domain as regular + * broadcast. + */ +static void +build_lswitch_rport_arp_req_flow_for_unreachable_ip(struct sset *ips, + int addr_family, struct ovn_datapath *od, uint32_t priority, + struct hmap *lflows, const struct ovsdb_idl_row *stage_hint) +{ + struct ds match = DS_EMPTY_INITIALIZER; + + arp_nd_ns_match(ips, addr_family, &match); + + ovn_lflow_add_unique_with_hint(lflows, od, S_SWITCH_IN_L2_LKUP, + priority, ds_cstr(&match), + "outport = \""MC_FLOOD"\"; output;", + stage_hint); + + ds_destroy(&match); +} + /* * Ingress table 19: Flows that forward ARP/ND requests only to the routers * that own the addresses. @@ -6625,39 +6653,48 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op, * router port. * Priority: 80. */ - struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4); - struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6); + struct sset lb_ips_v4 = SSET_INITIALIZER(&lb_ips_v4); + struct sset lb_ips_v6 = SSET_INITIALIZER(&lb_ips_v6); - get_router_load_balancer_ips(op->od, false, &all_ips_v4, &all_ips_v6); + get_router_load_balancer_ips(op->od, false, &lb_ips_v4, &lb_ips_v6); + + struct sset reachable_ips_v4 = SSET_INITIALIZER(&reachable_ips_v4); + struct sset reachable_ips_v6 = SSET_INITIALIZER(&reachable_ips_v6); + struct sset unreachable_ips_v4 = SSET_INITIALIZER(&unreachable_ips_v4); + struct sset unreachable_ips_v6 = SSET_INITIALIZER(&unreachable_ips_v6); const char *ip_addr; const char *ip_addr_next; - SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &all_ips_v4) { + SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &lb_ips_v4) { ovs_be32 ipv4_addr; /* Check if the ovn port has a network configured on which we could * expect ARP requests for the LB VIP. */ - if (ip_parse(ip_addr, &ipv4_addr) && - lrouter_port_ipv4_reachable(op, ipv4_addr)) { - continue; + if (ip_parse(ip_addr, &ipv4_addr)) { + if (lrouter_port_ipv4_reachable(op, ipv4_addr)) { + sset_add(&reachable_ips_v4, ip_addr); + } else { + sset_add(&unreachable_ips_v4, ip_addr); + } } - - sset_delete(&all_ips_v4, SSET_NODE_FROM_NAME(ip_addr)); } - SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &all_ips_v6) { + SSET_FOR_EACH_SAFE (ip_addr, ip_addr_next, &lb_ips_v6) { struct in6_addr ipv6_addr; /* Check if the ovn port has a network configured on which we could * expect NS requests for the LB VIP. */ - if (ipv6_parse(ip_addr, &ipv6_addr) && - lrouter_port_ipv6_reachable(op, &ipv6_addr)) { - continue; + if (ipv6_parse(ip_addr, &ipv6_addr)) { + if (lrouter_port_ipv6_reachable(op, &ipv6_addr)) { + sset_add(&reachable_ips_v6, ip_addr); + } else { + sset_add(&unreachable_ips_v6, ip_addr); + } } - - sset_delete(&all_ips_v6, SSET_NODE_FROM_NAME(ip_addr)); } + sset_destroy(&lb_ips_v4); + sset_destroy(&lb_ips_v6); for (size_t i = 0; i < op->od->nbr->n_nat; i++) { struct ovn_nat *nat_entry = &op->od->nat_entries[i]; @@ -6678,37 +6715,53 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op, struct in6_addr *addr = &nat_entry->ext_addrs.ipv6_addrs[0].addr; if (lrouter_port_ipv6_reachable(op, addr)) { - sset_add(&all_ips_v6, nat->external_ip); + sset_add(&reachable_ips_v6, nat->external_ip); + } else { + sset_add(&unreachable_ips_v6, nat->external_ip); } } else { ovs_be32 addr = nat_entry->ext_addrs.ipv4_addrs[0].addr; if (lrouter_port_ipv4_reachable(op, addr)) { - sset_add(&all_ips_v4, nat->external_ip); + sset_add(&reachable_ips_v4, nat->external_ip); + } else { + sset_add(&unreachable_ips_v4, nat->external_ip); } } } for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { - sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s); + sset_add(&reachable_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s); } for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) { - sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s); + sset_add(&reachable_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s); } - if (!sset_is_empty(&all_ips_v4)) { - build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op, - sw_od, 80, lflows, - stage_hint); + if (!sset_is_empty(&reachable_ips_v4)) { + build_lswitch_rport_arp_req_flow_for_reachable_ip(&reachable_ips_v4, + AF_INET, sw_op, + sw_od, 80, lflows, + stage_hint); + } + if (!sset_is_empty(&reachable_ips_v6)) { + build_lswitch_rport_arp_req_flow_for_reachable_ip(&reachable_ips_v6, + AF_INET6, sw_op, + sw_od, 80, lflows, + stage_hint); } - if (!sset_is_empty(&all_ips_v6)) { - build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op, - sw_od, 80, lflows, - stage_hint); + if (!sset_is_empty(&unreachable_ips_v4)) { + build_lswitch_rport_arp_req_flow_for_unreachable_ip( + &unreachable_ips_v4, AF_INET, sw_od, 90, lflows, stage_hint); + } + if (!sset_is_empty(&unreachable_ips_v6)) { + build_lswitch_rport_arp_req_flow_for_unreachable_ip( + &unreachable_ips_v6, AF_INET6, sw_od, 90, lflows, stage_hint); } - sset_destroy(&all_ips_v4); - sset_destroy(&all_ips_v6); + sset_destroy(&reachable_ips_v4); + sset_destroy(&reachable_ips_v6); + sset_destroy(&unreachable_ips_v4); + sset_destroy(&unreachable_ips_v6); /* Self originated ARP requests/ND need to be flooded as usual. * diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index 794b86ee1..1061fae8b 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -4103,9 +4103,13 @@ UniqueFlow[Flow{.logical_datapath = sw._uuid, * router port. * Priority: 80. */ -function get_arp_forward_ips(rp: Intern): (Set, Set) = { - var all_ips_v4 = set_empty(); - var all_ips_v6 = set_empty(); +function get_arp_forward_ips(rp: Intern): + (Set, Set, Set, Set) = +{ + var reachable_ips_v4 = set_empty(); + var reachable_ips_v6 = set_empty(); + var unreachable_ips_v4 = set_empty(); + var unreachable_ips_v6 = set_empty(); (var lb_ips_v4, var lb_ips_v6) = get_router_load_balancer_ips(rp.router, false); @@ -4115,7 +4119,9 @@ function get_arp_forward_ips(rp: Intern): (Set, Set) */ match (ip_parse(a)) { Some{ipv4} -> if (lrouter_port_ip_reachable(rp, IPv4{ipv4})) { - all_ips_v4.insert(a) + reachable_ips_v4.insert(a) + } else { + unreachable_ips_v4.insert(a) }, _ -> () } @@ -4126,7 +4132,9 @@ function get_arp_forward_ips(rp: Intern): (Set, Set) */ match (ipv6_parse(a)) { Some{ipv6} -> if (lrouter_port_ip_reachable(rp, IPv6{ipv6})) { - all_ips_v6.insert(a) + reachable_ips_v6.insert(a) + } else { + unreachable_ips_v6.insert(a) }, _ -> () } @@ -4139,22 +4147,45 @@ function get_arp_forward_ips(rp: Intern): (Set, Set) */ if (lrouter_port_ip_reachable(rp, nat.external_ip)) { match (nat.external_ip) { - IPv4{_} -> all_ips_v4.insert(nat.nat.external_ip), - IPv6{_} -> all_ips_v6.insert(nat.nat.external_ip) + IPv4{_} -> reachable_ips_v4.insert(nat.nat.external_ip), + IPv6{_} -> reachable_ips_v6.insert(nat.nat.external_ip) + } + } else { + match (nat.external_ip) { + IPv4{_} -> unreachable_ips_v4.insert(nat.nat.external_ip), + IPv6{_} -> unreachable_ips_v6.insert(nat.nat.external_ip), } } } }; for (a in rp.networks.ipv4_addrs) { - all_ips_v4.insert("${a.addr}") + reachable_ips_v4.insert("${a.addr}") }; for (a in rp.networks.ipv6_addrs) { - all_ips_v6.insert("${a.addr}") + reachable_ips_v6.insert("${a.addr}") }; - (all_ips_v4, all_ips_v6) + (reachable_ips_v4, reachable_ips_v6, unreachable_ips_v4, unreachable_ips_v6) } + +relation &SwitchPortARPForwards( + port: Intern, + reachable_ips_v4: Set, + reachable_ips_v6: Set, + unreachable_ips_v4: Set, + unreachable_ips_v6: Set +) + +&SwitchPortARPForwards(.port = port, + .reachable_ips_v4 = reachable_ips_v4, + .reachable_ips_v6 = reachable_ips_v6, + .unreachable_ips_v4 = unreachable_ips_v4, + .unreachable_ips_v6 = unreachable_ips_v6) :- + port in &SwitchPort(.peer = Some{rp}), + rp.is_enabled(), + (var reachable_ips_v4, var reachable_ips_v6, var unreachable_ips_v4, var unreachable_ips_v6) = get_arp_forward_ips(rp). + /* Packets received from VXLAN tunnels have already been through the * router pipeline so we should skip them. Normally this is done by the * multicast_group implementation (VXLAN packets skip table 32 which @@ -4165,8 +4196,8 @@ AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid, .stage = s_SWITCH_IN_L2_LKUP(), .priority = 80, .__match = fLAGBIT_NOT_VXLAN() ++ - " && arp.op == 1 && arp.tpa == { " ++ - all_ips_v4.to_vec().join(", ") ++ "}", + " && arp.op == 1 && arp.tpa == {" ++ + ipv4.to_vec().join(", ") ++ "}", .actions = if (sw.has_non_router_port) { "clone {outport = ${sp.json_name}; output; }; " "outport = ${mc_flood_l2}; output;" @@ -4175,17 +4206,16 @@ AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid, }, .external_ids = stage_hint(sp.lsp._uuid)}, .shared = not sw.has_non_router_port) :- - sp in &SwitchPort(.sw = sw, .peer = Some{rp}), - rp.is_enabled(), - (var all_ips_v4, _) = get_arp_forward_ips(rp), - not all_ips_v4.is_empty(), + sp in &SwitchPort(.sw = sw), + &SwitchPortARPForwards(.port = sp, .reachable_ips_v4 = ipv4), + not ipv4.is_empty(), var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0). AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid, .stage = s_SWITCH_IN_L2_LKUP(), .priority = 80, .__match = fLAGBIT_NOT_VXLAN() ++ - " && nd_ns && nd.target == { " ++ - all_ips_v6.to_vec().join(", ") ++ "}", + " && nd_ns && nd.target == {" ++ + ipv6.to_vec().join(", ") ++ "}", .actions = if (sw.has_non_router_port) { "clone {outport = ${sp.json_name}; output; }; " "outport = ${mc_flood_l2}; output;" @@ -4194,12 +4224,39 @@ AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid, }, .external_ids = stage_hint(sp.lsp._uuid)}, .shared = not sw.has_non_router_port) :- - sp in &SwitchPort(.sw = sw, .peer = Some{rp}), - rp.is_enabled(), - (_, var all_ips_v6) = get_arp_forward_ips(rp), - not all_ips_v6.is_empty(), + sp in &SwitchPort(.sw = sw), + &SwitchPortARPForwards(.port = sp, .reachable_ips_v6 = ipv6), + not ipv6.is_empty(), var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0). +AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid, + .stage = s_SWITCH_IN_L2_LKUP(), + .priority = 90, + .__match = fLAGBIT_NOT_VXLAN() ++ + " && arp.op == 1 && arp.tpa == {" ++ + ipv4.to_vec().join(", ") ++ "}", + .actions = "outport = ${flood}; output;", + .external_ids = stage_hint(sp.lsp._uuid)}, + .shared = not sw.has_non_router_port) :- + sp in &SwitchPort(.sw = sw), + &SwitchPortARPForwards(.port = sp, .unreachable_ips_v4 = ipv4), + not ipv4.is_empty(), + var flood = json_string_escape(mC_FLOOD().0). + +AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid, + .stage = s_SWITCH_IN_L2_LKUP(), + .priority = 90, + .__match = fLAGBIT_NOT_VXLAN() ++ + " && nd_ns && nd.target == {" ++ + ipv6.to_vec().join(", ") ++ "}", + .actions = "outport = ${flood}; output;", + .external_ids = stage_hint(sp.lsp._uuid)}, + .shared = not sw.has_non_router_port) :- + sp in &SwitchPort(.sw = sw), + &SwitchPortARPForwards(.port = sp, .unreachable_ips_v6 = ipv6), + not ipv6.is_empty(), + var flood = json_string_escape(mC_FLOOD().0). + for (SwitchPortNewDynamicAddress(.port = &SwitchPort{.lsp = lsp, .json_name = json_name, .sw = sw}, .address = Some{addrs}) if lsp.__type != "external") { diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 9e4f2ca62..13ead49ba 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -3840,3 +3840,102 @@ check ovn-nbctl --wait=sb clear logical_router_port ro2-sw ha_chassis_group check_lflows 0 AT_CLEANUP + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn -- ARP flood for unreachable addresses]) +ovn_start + +AS_BOX([Setting up the logical network]) + +# This network is the same as the one from "Router Address Propagation" +check ovn-nbctl ls-add sw + +check ovn-nbctl lr-add ro1 +check ovn-nbctl lrp-add ro1 ro1-sw 00:00:00:00:00:01 10.0.0.1/24 +check ovn-nbctl lsp-add sw sw-ro1 +check ovn-nbctl lsp-set-type sw-ro1 router +check ovn-nbctl lsp-set-addresses sw-ro1 router +check ovn-nbctl lsp-set-options sw-ro1 router-port=ro1-sw + +check ovn-nbctl lr-add ro2 +check ovn-nbctl lrp-add ro2 ro2-sw 00:00:00:00:00:02 20.0.0.1/24 +check ovn-nbctl lsp-add sw sw-ro2 +check ovn-nbctl lsp-set-type sw-ro2 router +check ovn-nbctl lsp-set-addresses sw-ro2 router +check ovn-nbctl --wait=sb lsp-set-options sw-ro2 router-port=ro2-sw + +check ovn-nbctl ls-add ls1 +check ovn-nbctl lsp-add ls1 vm1 +check ovn-nbctl lsp-set-addresses vm1 "00:00:00:00:01:02 192.168.1.2" +check ovn-nbctl lrp-add ro1 ro1-ls1 00:00:00:00:01:01 192.168.1.1/24 +check ovn-nbctl lsp-add ls1 ls1-ro1 +check ovn-nbctl lsp-set-type ls1-ro1 router +check ovn-nbctl lsp-set-addresses ls1-ro1 router +check ovn-nbctl lsp-set-options ls1-ro1 router-port=ro1-ls1 + +check ovn-nbctl ls-add ls2 +check ovn-nbctl lsp-add ls2 vm2 +check ovn-nbctl lsp-set-addresses vm2 "00:00:00:00:02:02 192.168.2.2" +check ovn-nbctl lrp-add ro2 ro2-ls2 00:00:00:00:02:01 192.168.2.1/24 +check ovn-nbctl lsp-add ls2 ls2-ro2 +check ovn-nbctl lsp-set-type ls2-ro2 router +check ovn-nbctl lsp-set-addresses ls2-ro2 router +check ovn-nbctl lsp-set-options ls2-ro2 router-port=ro2-ls2 + +AS_BOX([Ensure that unreachable flood flows are not installed, since no addresses are unreachable]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep "ls_in_l2_lkup" | grep "priority=90" -c], [1], [dnl +0 +]) + +AS_BOX([Adding some reachable NAT addresses]) + +check ovn-nbctl lr-nat-add ro1 dnat 10.0.0.100 192.168.1.100 +check ovn-nbctl lr-nat-add ro1 snat 10.0.0.200 192.168.1.200/30 + +check ovn-nbctl lr-nat-add ro2 dnat 20.0.0.100 192.168.2.100 +check ovn-nbctl --wait=sb lr-nat-add ro2 snat 20.0.0.200 192.168.2.200/30 + +AS_BOX([Ensure that unreachable flood flows are not installed, since all addresses are reachable]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep "ls_in_l2_lkup" | grep "priority=90" -c], [1], [dnl +0 +]) + +AS_BOX([Adding some unreachable NAT addresses]) + +check ovn-nbctl lr-nat-add ro1 dnat 30.0.0.100 192.168.1.130 +check ovn-nbctl lr-nat-add ro1 snat 30.0.0.200 192.168.1.148/30 + +check ovn-nbctl lr-nat-add ro2 dnat 40.0.0.100 192.168.2.130 +check ovn-nbctl --wait=sb lr-nat-add ro2 snat 40.0.0.200 192.168.2.148/30 + +AS_BOX([Ensure that unreachable flood flows are installed, since there are unreachable addresses]) + +ovn-sbctl lflow-list + +# We expect two flows to be installed, one per connected router port on sw +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep priority=90 -c], [0], [dnl +2 +]) + +# We expect that the installed flows will match the unreachable DNAT addresses only. +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep priority=90 | grep "arp.tpa == {30.0.0.100}" -c], [0], [dnl +1 +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep priority=90 | grep "arp.tpa == {40.0.0.100}" -c], [0], [dnl +1 +]) + +# Ensure that we do not create flows for SNAT addresses +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep priority=90 | grep "arp.tpa == {30.0.0.200}" -c], [1], [dnl +0 +]) + +AT_CHECK([ovn-sbctl lflow-list sw | grep ls_in_l2_lkup | grep priority=90 | grep "arp.tpa == {40.0.0.200}" -c], [1], [dnl +0 +]) + +AT_CLEANUP +]) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 3824788c4..6539a66cb 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -6293,3 +6293,105 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn -- Floating IP outside router subnet IPv4]) +AT_KEYWORDS(NAT) + +ovn_start + +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-int]) + +# Set external-ids in br-int needed for ovn-controller +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 bridge br-int fail-mode=secure other-config:disable-in-band=true + +start_daemon ovn-controller + +# Logical network: +# Two VMs +# * VM1 with IP address 192.168.100.5 +# * VM2 with IP address 192.168.200.5 +# +# VM1 connects to logical switch ls1. ls1 connects to logical router lr1. +# VM2 connects to logical switch ls2. ls2 connects to logical router lr2. +# lr1 and lr2 both connect to logical switch ls-pub. +# * lr1's interface that connects to ls-pub has IP address 172.18.2.110/24 +# * lr2's interface that connects to ls-pub has IP address 172.18.1.173/24 +# +# lr1 has the following attributes: +# * It has a DNAT rule that translates 172.18.2.11 to 192.168.100.5 (VM1) +# +# lr2 has the following attributes: +# * It has a DNAT rule that translates 172.18.2.12 to 192.168.200.5 (VM2) +# +# In this test, we want to ensure that a ping from VM1 to IP address 172.18.2.12 reaches VM2. +# When the NAT rules are set up, there should be MAC_Bindings created that allow for traffic +# to exit lr1, go through ls-pub, and reach the NAT external IP configured on lr2. + +check ovn-nbctl ls-add ls1 +check ovn-nbctl lsp-add ls1 vm1 -- lsp-set-addresses vm1 "00:00:00:00:01:05 192.168.100.5" + +check ovn-nbctl ls-add ls2 +check ovn-nbctl lsp-add ls2 vm2 -- lsp-set-addresses vm2 "00:00:00:00:02:05 192.168.200.5" + +check ovn-nbctl ls-add ls-pub + +check ovn-nbctl lr-add lr1 +check ovn-nbctl lrp-add lr1 lr1-ls1 00:00:00:00:01:01 192.168.100.1/24 +check ovn-nbctl lsp-add ls1 ls1-lr1 \ + -- lsp-set-type ls1-lr1 router \ + -- lsp-set-addresses ls1-lr1 router \ + -- lsp-set-options ls1-lr1 router-port=lr1-ls1 + +check ovn-nbctl lr-add lr2 +check ovn-nbctl lrp-add lr2 lr2-ls2 00:00:00:00:02:01 192.168.200.1/24 +check ovn-nbctl lsp-add ls2 ls2-lr2 \ + -- lsp-set-type ls2-lr2 router \ + -- lsp-set-addresses ls2-lr2 router \ + -- lsp-set-options ls2-lr2 router-port=lr2-ls2 + +check ovn-nbctl lrp-add lr1 lr1-ls-pub 00:00:00:00:03:01 172.18.2.110/24 +check ovn-nbctl lrp-set-gateway-chassis lr1-ls-pub hv1 +check ovn-nbctl lsp-add ls-pub ls-pub-lr1 \ + -- lsp-set-type ls-pub-lr1 router \ + -- lsp-set-addresses ls-pub-lr1 router \ + -- lsp-set-options ls-pub-lr1 router-port=lr1-ls-pub + +check ovn-nbctl lrp-add lr2 lr2-ls-pub 00:00:00:00:03:02 172.18.1.173/24 +check ovn-nbctl lrp-set-gateway-chassis lr2-ls-pub hv1 +check ovn-nbctl lsp-add ls-pub ls-pub-lr2 \ + -- lsp-set-type ls-pub-lr2 router \ + -- lsp-set-addresses ls-pub-lr2 router \ + -- lsp-set-options ls-pub-lr2 router-port=lr2-ls-pub + +# Putting --add-route on these NAT rules means there is no need to +# add any static routes. +check ovn-nbctl --add-route lr-nat-add lr1 dnat_and_snat 172.18.2.11 192.168.100.5 vm1 00:00:00:00:03:01 +check ovn-nbctl --add-route lr-nat-add lr2 dnat_and_snat 172.18.2.12 192.168.200.5 vm2 00:00:00:00:03:02 + +ADD_NAMESPACES(vm1) +ADD_VETH(vm1, vm1, br-int, "192.168.100.5/24", "00:00:00:00:01:05", \ + "192.168.100.1") + +ADD_NAMESPACES(vm2) +ADD_VETH(vm2, vm2, br-int, "192.168.200.5/24", "00:00:00:00:02:05", \ + "192.168.200.1") + +OVN_POPULATE_ARP +check ovn-nbctl --wait=hv sync + +AS_BOX([Testing a ping]) + +NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.12 | FORMAT_PING], \ +[0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +AT_CLEANUP +])