From patchwork Wed Jan 22 21:31:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manoj Sharma X-Patchwork-Id: 1227439 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=whitealder.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=nutanix.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=nutanix.com header.i=@nutanix.com header.a=rsa-sha256 header.s=proofpoint20171006 header.b=XKh50bpJ; dkim-atps=neutral Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 482zB70xz0z9s1x for ; Thu, 23 Jan 2020 08:31:43 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id CD9A386DE2; Wed, 22 Jan 2020 21:31:40 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6rn32INxarwh; Wed, 22 Jan 2020 21:31:33 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by whitealder.osuosl.org (Postfix) with ESMTP id 188C686DFC; Wed, 22 Jan 2020 21:31:33 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E7E01C1D85; Wed, 22 Jan 2020 21:31:32 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by lists.linuxfoundation.org (Postfix) with ESMTP id D51D1C1D81 for ; Wed, 22 Jan 2020 21:31:29 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id BDE2C87E7A for ; Wed, 22 Jan 2020 21:31:29 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id WkTIh0piNOZl for ; Wed, 22 Jan 2020 21:31:27 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx0b-002c1b01.pphosted.com (mx0b-002c1b01.pphosted.com [148.163.155.12]) by hemlock.osuosl.org (Postfix) with ESMTPS id 719C887E74 for ; Wed, 22 Jan 2020 21:31:27 +0000 (UTC) Received: from pps.filterd (m0127842.ppops.net [127.0.0.1]) by mx0b-002c1b01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 00MLQaWu001964 for ; Wed, 22 Jan 2020 13:31:26 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nutanix.com; h=from : to : cc : subject : date : message-id : references : in-reply-to : content-type : content-transfer-encoding : mime-version; s=proofpoint20171006; bh=RlflV6zl05CzVFzbpHW2RC8b24G+doM3QUWhA+pkI7E=; b=XKh50bpJ7iHwMfAveJB0wXPkQLjO0X6sE50L//GYzKY0RYf8E9+bJpdOHHEL8NWvOavr qZPAPW1KJlwpoEzm7VUrpyxUZHRxIntXus3YfdZK4iWoaEZxK9q0+ceq1Xy6hNwGHbTI kqqns3dfCSacFe6Lm8pqRSilNS8K3B3Sg9B2ub+bRAKsgxgQ9AqjCCQ07qWGtz+dWc++ dKSi8BQCA21sxp6V3R1t+Geah8H0kULVn44tQkNpXivfWMETNzUiOZF0+cpUr7ZXSIx1 AMdn+2yP4iZv5p5eq0fIbhB2wqIjegxA2ypsnZNby8nFg4/YRY37M8C3d+rCaNpil6WH Ig== Received: from nam11-co1-obe.outbound.protection.outlook.com (mail-co1nam11lp2172.outbound.protection.outlook.com [104.47.56.172]) by mx0b-002c1b01.pphosted.com with ESMTP id 2xm27n0wxy-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Wed, 22 Jan 2020 13:31:26 -0800 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=EjaibvlY9dnL1D+vdk2YWhzalwZCtycaW7VsWZf0G/IetHryrgaj8yV661PSA+1XKWeUzZVstg6iKQ0Y3GPs55kIUPMIsc/8v6aOb+C+EdP+U+wzaDi0bYX+/aGQYoRIx+X58D+U2L5VAB1WlB2mcxd2GVHLuD6ptZOynMxuYyENilp1RIz/iiS8O1mArBh7c9eHf05jtodjIFRT26/O65+xcyHPt+9v+gzym8UheZZ7PBN7l/7ADsgGQOag56/7UkO2J0hqX/5vWafrVd868Lceeaqibu6y35lme91vkHNhgSbtoaPM1yJ1HGGE1G7w6B0CWPJkySQwRaexdgcaxQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=RlflV6zl05CzVFzbpHW2RC8b24G+doM3QUWhA+pkI7E=; b=ZEKq64VxTqg2irPpuOSqza/acD6+DMbNVgtPInj47jhQq5yhL539zhsYBu+puHQ3zXV4wLtBorVILbF6qLKb1az5d1DsxQjRHtm0qJ9kLPCVH71wU+EdhunNBGIez58izhHLMZv78wq4Neu69UX10Oy0KXY+y3vdWhwhCPrLUg5RQrwJ14i7+xI147z27nynpfYAhk5XuqXgqsukr/XFrPc20QnitlmDRMw0MpTGMu1cPG2WSWEVnUUDewx6KpYYvvsvsABiFaMHkPJM0m9vhwq/2pvT+MnZYqg4LTbxbdm0ts2uD08+s8TwNX9fsfD6UHlvAFEXfq/Xaa1Pfg++xQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nutanix.com; dmarc=pass action=none header.from=nutanix.com; dkim=pass header.d=nutanix.com; arc=none Received: from SN6PR02MB4621.namprd02.prod.outlook.com (52.135.120.32) by SN6PR02MB4861.namprd02.prod.outlook.com (52.135.114.76) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2644.23; Wed, 22 Jan 2020 21:31:24 +0000 Received: from SN6PR02MB4621.namprd02.prod.outlook.com ([fe80::9d6c:c42c:af5f:9ba0]) by SN6PR02MB4621.namprd02.prod.outlook.com ([fe80::9d6c:c42c:af5f:9ba0%6]) with mapi id 15.20.2644.027; Wed, 22 Jan 2020 21:31:24 +0000 Received: from PC11S3MA.corp.nutanix.com (192.146.154.3) by BYAPR08CA0017.namprd08.prod.outlook.com (2603:10b6:a03:100::30) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.20.2644.20 via Frontend Transport; Wed, 22 Jan 2020 21:31:22 +0000 From: Manoj Sharma To: "ovs-dev@openvswitch.org" Thread-Topic: [PATCH v4 ovn 1/2] Forwarding group to load balance l2 traffic with liveness detection Thread-Index: AQHV0WtLX1EEDPZai0uFYgyJggp62w== Date: Wed, 22 Jan 2020 21:31:24 +0000 Message-ID: <20200122213118.65988-2-manoj.sharma@nutanix.com> References: <20200122213118.65988-1-manoj.sharma@nutanix.com> In-Reply-To: <20200122213118.65988-1-manoj.sharma@nutanix.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: BYAPR08CA0017.namprd08.prod.outlook.com (2603:10b6:a03:100::30) To SN6PR02MB4621.namprd02.prod.outlook.com (2603:10b6:805:a8::32) x-ms-exchange-messagesentrepresentingtype: 1 x-mailer: git-send-email 2.17.2 (Apple Git-113) x-originating-ip: [192.146.154.3] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: 43191215-81c6-4077-126e-08d79f826da0 x-ms-traffictypediagnostic: SN6PR02MB4861: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-proofpoint-crosstenant: true x-ms-oob-tlc-oobclassifiers: OLM:3383; x-forefront-prvs: 029097202E x-forefront-antispam-report: SFV:NSPM; SFS:(10019020)(366004)(396003)(136003)(346002)(376002)(39860400002)(199004)(189003)(66476007)(66556008)(86362001)(64756008)(66946007)(2906002)(8936002)(66446008)(16526019)(7696005)(316002)(26005)(6486002)(186003)(52116002)(478600001)(6916009)(71200400001)(5660300002)(8676002)(81156014)(30864003)(81166006)(36756003)(107886003)(66574012)(1076003)(44832011)(4326008)(2616005)(956004)(559001)(579004); DIR:OUT; SFP:1102; SCL:1; SRVR:SN6PR02MB4861; H:SN6PR02MB4621.namprd02.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; MX:1; received-spf: None (protection.outlook.com: nutanix.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: XroOc98XDX3u1CGBUmJWXbM45ZMHEjYPjpvYtkiLAn97vv60PbMtk0cKZU2Ev7THCWtyno7HJJ0Hs635zSOOHuNZVhAeTyvcKeVgV852EJpHIr7GvRk/0P54LD0f+jtuZ21wXxcgHSFDIdZX5bXVfOeOJ+e/ccW/RciVxFuMcWgdg/pZng8W8qoHJ+7nf4pmxP/N/oIG7h0FCkOzLMy796ofYCBZO1VCfQm7ycqKGeIXL6N2drGsdktb+oBcS4epSEACR9YkOm2yIBFn0LNIuMTXEZFeayPYpD2xIl3PgQwI9DcEwOJGueUcYouHj8LdaHA/N7FrH/gRWZE67VsG85By5vlOmrwLQrgUfoFSQHXqNtBOSxSQeYV2HjnQ1erGhkIbVGfEvIw6D/cGpyPjpH6b1rKSgyaTdwwaR02RIyMKKWqqdE9KjiBmigbo49ox MIME-Version: 1.0 X-OriginatorOrg: nutanix.com X-MS-Exchange-CrossTenant-Network-Message-Id: 43191215-81c6-4077-126e-08d79f826da0 X-MS-Exchange-CrossTenant-originalarrivaltime: 22 Jan 2020 21:31:24.2163 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: bb047546-786f-4de1-bd75-24e5b6f79043 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: NRdWnYc2Eyo5vOx8o71N75W+sD6w2rCG2cTLoIzqRu1hikFuSVvkLcpAo5w2vSl2ctfTbJu8fU2j3w8pYOQB/ST6k9BQajKe51ELBgyS8Vw= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN6PR02MB4861 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.138, 18.0.572 definitions=2020-01-22_08:2020-01-22, 2020-01-22 signatures=0 X-Proofpoint-Spam-Reason: safe Subject: [ovs-dev] [PATCH v4 ovn 1/2] Forwarding group to load balance l2 traffic with liveness detection 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" Add a forwarding group table and a reference to the logical switch it is configured on. The forwarding group is configured with a virtual IP, virtual MAC and a number of logical switch ports from a logical switch. Unit tests for the forwarding group. Signed-off-by: Manoj Sharma --- NEWS | 1 + northd/ovn-northd.8.xml | 49 +++++++++ ovn-nb.ovsschema | 18 +++- ovn-nb.xml | 35 +++++++ ovn-sb.xml | 19 ++++ tests/ovn-nbctl.at | 37 +++++++ tests/ovn.at | 124 +++++++++++++++++++++++ utilities/ovn-nbctl.8.xml | 38 +++++++ utilities/ovn-nbctl.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 572 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 0ad9677..9e7d601 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Post-OVS-v2.12.0 independently. - Added IPv6 NAT support for OVN routers. - Added Stateless Floating IP support in OVN. + - Added Forwarding Group support in OVN. v2.12.0 - 03 Sep 2019 --------------------- diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index 3628d35..bcb320b 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -767,6 +767,45 @@ output;
  • +

    + For each VIP configured in the table + + a priority-50 logical flow is added with the match + arp.tpa == vip && && arp.op == 1 + and applies the action +

    + +
    +eth.dst = eth.src;
    +eth.src = E;
    +arp.op = 2; /* ARP reply. */
    +arp.tha = arp.sha;
    +arp.sha = E;
    +arp.tpa = arp.spa;
    +arp.spa = A;
    +outport = inport;
    +flags.loopback = 1;
    +output;
    +        
    + +

    + where E is the forwarding group's mac defined in + the . +

    + +

    + A is used as either the destination ip for load balancing + traffic to child ports or as nexthop to hosts behind the child ports. +

    + +

    + These flows are required to respond to an ARP request if an ARP + request is sent for the IP vip. +

    +
  • + +
  • One priority-0 fallback flow that matches all packets and advances to the next table.
  • @@ -1153,6 +1192,16 @@ output; address is only programmed on the redirect-chassis. + +

    + For each forwarding group configured on the logical switch datapath, + a priority-50 flow that matches on eth.dst == VIP + with an action of fwd_group(childports=args + ), where args contains comma separated + logical switch child ports to load balance to. + If liveness is enabled, then action also includes + liveness=true. +

  • diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 12999a4..99b6285 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", "version": "5.18.0", - "cksum": "2806349485 24196", + "cksum": "63300136 24879", "tables": { "NB_Global": { "columns": { @@ -59,7 +59,12 @@ "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", - "min": 0, "max": "unlimited"}}}, + "min": 0, "max": "unlimited"}}, + "forwarding_groups": { + "type": {"key": {"type": "uuid", + "refTable": "Forwarding_Group", + "refType": "strong"}, + "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Logical_Switch_Port": { "columns": { @@ -113,6 +118,15 @@ "min": 0, "max": "unlimited"}}}, "indexes": [["name"]], "isRoot": false}, + "Forwarding_Group": { + "columns": { + "name": {"type": "string"}, + "vip": {"type": "string"}, + "vmac": {"type": "string"}, + "liveness": {"type": "boolean"}, + "child_port": {"type": {"key": "string", + "min": 1, "max": "unlimited"}}}, + "isRoot": false}, "Address_Set": { "columns": { "name": {"type": "string"}, diff --git a/ovn-nb.xml b/ovn-nb.xml index 4515538..8c9ef90 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -197,6 +197,11 @@ Please see the table. + + Groups a set of logical port endpoints for traffic going out of the + logical switch. + +

    These columns provide names for the logical switch. From OVN's @@ -1152,6 +1157,36 @@ + +

    + Each row represents one forwarding group. +

    + + + A name for the forwarding group. This name has no special meaning or + purpose other than to provide convenience for human interaction with + the ovn-nb database. + + + + The virtual IP address assigned to the forwarding group. It will respond + with vmac when an ARP request is sent for vip. + + + + The virtual MAC address assigned to the forwarding group. + + + + If set to true, liveness is enabled for child ports + otherwise it is disabled. + + + + List of child ports in the forwarding group. + +
    +

    Each row in this table represents a named set of addresses. diff --git a/ovn-sb.xml b/ovn-sb.xml index 9635dcc..93bbb86 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -1957,6 +1957,25 @@ + +

    fwd_group(P);
    +
    +

    + Parameters: liveness, list of child ports P. +

    + +

    + It load balances traffic to one or more child ports in a + logical switch. ovn-controller translates the + fwd_group into openflow group with one bucket + for each child port. If liveness is set to true, it also + integrates the bucket selection with BFD status on the tunnel + interface corresponding to child port. +

    + +

    Example: fwd_group(liveness=true, childports=p1,p2 +

    +
    diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index 13fa7c3..fcb6ad7 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -1803,6 +1803,43 @@ $SW1P2 AT_CHECK([ovn-nbctl pg-del pg1], [0], [ignore]) AT_CHECK([ovn-nbctl list port_group], [0], []) ]) +dnl --------------------------------------------------------------------- + +OVN_NBCTL_TEST([ovn_nbctl_fwd_groups], [fwd groups], [ + +dnl Add fwd-group to a non-existent logical switch +AT_CHECK([ovn-nbctl fwd-group-add fwd_grp1 ls0 10.1.1.11 00:11:22:33:44:55 lsp1 lsp2], [1], [], + [ovn-nbctl: ls0: switch name not found +]) + +AT_CHECK([ovn-nbctl ls-add ls0]) + +dnl Add fwd-group with non-existent logical switch ports +AT_CHECK([ovn-nbctl fwd-group-add fwd_grp1 ls0 10.1.1.11 00:11:22:33:44:55 lsp1 lsp2], [1], [], + [ovn-nbctl: lsp1: logical switch port does not exist +]) + +AT_CHECK([ovn-nbctl lsp-add ls0 lsp1]) +AT_CHECK([ovn-nbctl lsp-add ls0 lsp2]) +AT_CHECK([ovn-nbctl fwd-group-add fwd_grp1 ls0 10.1.1.11 00:11:22:33:44:55 lsp1 lsp2]) +AT_CHECK([ovn-nbctl fwd-group-list ls0], [0], [dnl +FWD_GROUP LS VIP VMAC CHILD_PORTS +fwd_grp1 ls0 10.1.1.11 00:11:22:33:44:55 lsp1 lsp2 +]) +AT_CHECK([ovn-nbctl --bare --columns=name list forwarding_group], [0], +[fwd_grp1 +]) + +dnl Add duplicate fwd-group +AT_CHECK([ovn-nbctl fwd-group-add fwd_grp1 ls0 10.1.1.11 00:11:22:33:44:55 lsp1 lsp2], [1], [], + [ovn-nbctl: fwd_grp1: a forwarding group by this name already exists +]) + +dnl Delete fwd-group +AT_CHECK([ovn-nbctl fwd-group-del fwd_grp1], [0], [ignore]) +AT_CHECK([ovn-nbctl list forwarding_group], [0], []) + +]) AT_SETUP([ovn-nbctl - daemon retry connection]) OVN_NBCTL_TEST_START daemon diff --git a/tests/ovn.at b/tests/ovn.at index f8ea88d..b02d376 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -17502,3 +17502,127 @@ done OVN_CLEANUP([hv1]) AT_CLEANUP + +AT_SETUP([ovn -- forwarding group: 3 HVs, 1 LR, 2 LS]) +AT_KEYWORDS([forwarding-group]) +ovn_start + +# Logical network: +# One LR - R1 has a logical switch ls1 and ls2 connected to it. +# Logical switch ls1 has one port while ls2 has two logical switch ports as +# child ports. +ovn-nbctl lr-add R1 +ovn-nbctl ls-add ls1 +ovn-nbctl ls-add ls2 + +# Logical switch ls1 to R1 connectivity +ovn-nbctl lrp-add R1 R1-ls1 00:00:00:01:02:f1 192.168.1.1/24 +ovn-nbctl lsp-add ls1 ls1-R1 -- set Logical_Switch_Port ls1-R1 \ + type=router options:router-port=R1-ls1 -- lsp-set-addresses ls1-R1 router +ovn-nbctl lsp-add ls1 lsp11 \ + -- lsp-set-addresses lsp11 "00:00:00:01:02:01 192.168.1.2" + +# Logical switch ls2 to R1 connectivity +ovn-nbctl lrp-add R1 R1-ls2 00:00:00:01:02:f2 172.16.1.1/24 +ovn-nbctl lsp-add ls2 ls2-R1 -- set Logical_Switch_Port ls2-R1 \ + type=router options:router-port=R1-ls2 -- lsp-set-addresses ls2-R1 router +ovn-nbctl lsp-add ls2 lsp21 \ + -- lsp-set-addresses lsp21 "00:00:00:01:02:01 172.16.1.2" +ovn-nbctl lsp-add ls2 lsp22 \ + -- lsp-set-addresses lsp22 "00:00:00:01:02:02 172.16.1.3" + +# Create a network +net_add n1 + +# Create hypervisor hv1 connected to n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lsp11 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1 + +# Create hypervisor hv2 connected to n1 +sim_add hv2 +as hv2 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 +ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lsp21 options:tx_pcap=hv2/vif2-tx.pcap options:rxq_pcap=hv2/vif2-rx.pcap ofport-request=1 + +# Create hypervisor hv3 connected to n1 +sim_add hv3 +as hv3 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.3 +ovs-vsctl add-port br-int vif3 -- set Interface vif3 external-ids:iface-id=lsp22 options:tx_pcap=hv3/vif3-tx.pcap options:rxq_pcap=hv3/vif3-rx.pcap ofport-request=1 + +# Add a forwarding group on ls2 with lsp21 and lsp22 as child ports +# virtual IP - 172.16.1.11, virtual MAC - 00:11:de:ad:be:ef +ovn-nbctl fwd-group-add fwd_grp1 ls2 172.16.1.11 00:11:de:ad:be:ef lsp21 lsp22 + +# Allow some time for ovn-northd and ovn-controller to catch up. +sleep 1 + +# Check logical flow +AT_CHECK([ovn-sbctl dump-flows | grep ls_in_l2_lkup | grep fwd_group | wc -l], [0], [dnl +1 +]) + +# Check openflow rule with "group" on hypervisor +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | \ + grep "dl_dst=00:11:de:ad:be:ef actions=group" | wc -l], [0], [dnl +1 +]) + +# Verify openflow group members +for child_port in lsp21 lsp22; do + tunnel_key=`ovn-sbctl --bare --column tunnel_key find port_binding logical_port=$child_port` + AT_CHECK([as hv1 ovs-ofctl -O OpenFlow13 dump-groups br-int | \ + grep "bucket=actions=load:0x"$tunnel_key | wc -l], [0], [dnl +1 +]) +done + +# Send a packet to virtual IP +src_mac=00:00:00:01:02:01 +dst_mac=00:00:00:01:02:f1 +src_ip=192.168.1.2 +dst_ip=172.16.1.11 +packet="inport==\"lsp11\" && eth.src==$src_mac && eth.dst==$dst_mac && + ip4 && ip.ttl==64 && ip4.src==$src_ip && ip4.dst==$dst_ip && + udp && udp.src==53 && udp.dst==4369" +as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet" + +# Check if the packet hit the forwarding group policy +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | \ + grep "dl_dst=00:11:de:ad:be:ef actions=group" | \ + grep "n_packets=1" | wc -l], [0], [dnl +1 +]) + +# Delete the forwarding group +ovn-nbctl fwd-group-del fwd_grp1 + +# Add a forwarding group with liveness on ls2 with lsp21 and lsp22 as child +# ports virtual IP - 172.16.1.11, virtual MAC - 00:11:de:ad:be:ef +ovn-nbctl --liveness fwd-group-add fwd_grp1 ls2 172.16.1.11 00:11:de:ad:be:ef lsp21 lsp22 + +# Allow some time for ovn-northd and ovn-controller to catch up. +sleep 1 + +# Verify openflow group members +ofport_lsp21=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-hv2-0) +tunnel_key=`ovn-sbctl --bare --column tunnel_key find port_binding logical_port=lsp21` +AT_CHECK([as hv1 ovs-ofctl -O OpenFlow13 dump-groups br-int | \ + grep "bucket=watch_port:$ofport_lsp21,actions=load:0x"$tunnel_key | wc -l], [0], [dnl +1 +]) + +ofport_lsp22=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-hv3-0) +tunnel_key=`ovn-sbctl --bare --column tunnel_key find port_binding logical_port=lsp22` +AT_CHECK([as hv1 ovs-ofctl -O OpenFlow13 dump-groups br-int | \ + grep "bucket=watch_port:$ofport_lsp22,actions=load:0x"$tunnel_key | wc -l], [0], [dnl +1 +]) + +OVN_CLEANUP([hv1], [hv2], [hv3]) +AT_CLEANUP diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml index f20f992..48aa1f5 100644 --- a/utilities/ovn-nbctl.8.xml +++ b/utilities/ovn-nbctl.8.xml @@ -483,6 +483,44 @@
    +

    Forwarding Group Commands

    + +
    +
    [--liveness]fwd-group-add group switch vip vmac ports
    +
    +

    + Creates a new forwarding group named group as the name + with the provided vip and vmac. vip + should be a virtual IP address and vmac should be a + virtual MAC address to access the forwarding group. ports + are the logical switch port names that are put in the forwarding + group. Example for ports is lsp1 lsp2 ... + Traffic destined to virtual IP of the forwarding group will be load + balanced to all the child ports. +

    +

    + When --liveness is specified then child ports are + expected to be bound to external devices like routers. BFD should + be configured between hypervisors and the external devices. + The child port selection will become dependent on BFD status with + its external device. +

    +
    + +
    [--if-exists] fwd-group-del group +
    +
    + Deletes group. It is an error if group does + not exist, unless --if-exists is specified. +
    + +
    fwd-group-list [switch]
    +
    + Lists all existing forwarding groups, If switch is specified + then only the forwarding groups configured for switch will + be listed. +
    +

    Logical Router Commands

    diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index 0bcb975..5d1370a 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -648,6 +648,13 @@ Logical switch port commands:\n\ lsp-get-dhcpv6-options PORT get the dhcpv6 options for PORT\n\ lsp-get-ls PORT get the logical switch which the port belongs to\n\ \n\ +Forwarding group commands:\n\ + [--liveness]\n\ + fwd-group-add GROUP SWITCH VIP VMAC PORTS...\n\ + add a forwarding group on SWITCH\n\ + fwd-group-del GROUP delete a forwarding group\n\ + fwd-group-list [SWITCH] print forwarding groups\n\ +\n\ Logical router commands:\n\ lr-add [ROUTER] create a logical router named ROUTER\n\ lr-del ROUTER delete ROUTER and all its ports\n\ @@ -4821,6 +4828,244 @@ nbctl_lrp_get_redirect_type(struct ctl_context *ctx) !redirect_type ? "overlay": redirect_type); } +static const struct nbrec_forwarding_group * +fwd_group_by_name_or_uuid(struct ctl_context *ctx, const char *id) +{ + const struct nbrec_forwarding_group *fwd_group = NULL; + struct uuid fwd_uuid; + + bool is_uuid = uuid_from_string(&fwd_uuid, id); + if (is_uuid) { + fwd_group = nbrec_forwarding_group_get_for_uuid(ctx->idl, &fwd_uuid); + } + + if (!fwd_group) { + NBREC_FORWARDING_GROUP_FOR_EACH(fwd_group, ctx->idl) { + if (!strcmp(fwd_group->name, id)) { + break; + } + } + } + + return fwd_group; +} + +static const struct nbrec_logical_switch * +fwd_group_to_logical_switch(struct ctl_context *ctx, + const struct nbrec_forwarding_group *fwd_group) +{ + if (!fwd_group) { + return NULL; + } + + const struct nbrec_logical_switch_port *lsp; + char *error = lsp_by_name_or_uuid(ctx, fwd_group->child_port[0], + false, &lsp); + if (error) { + ctx->error = error; + return NULL; + } + if (!lsp) { + return NULL; + } + + const struct nbrec_logical_switch *ls; + error = lsp_to_ls(ctx->idl, lsp, &ls); + if (error) { + ctx->error = error; + return NULL; + } + + if (!ls) { + return NULL; + } + + return ls; +} + +static void +nbctl_fwd_group_add(struct ctl_context *ctx) +{ + if (ctx->argc <= 5) { + ctl_error(ctx, "Usage : ovn-nbctl fwd-group-add group switch vip vmac " + "child_ports..."); + return; + } + + /* Check if the forwarding group already exists */ + const char *fwd_group_name = ctx->argv[1]; + if (fwd_group_by_name_or_uuid(ctx, fwd_group_name)) { + ctl_error(ctx, "%s: a forwarding group by this name already exists", + fwd_group_name); + return; + } + + /* Check if the logical switch exists */ + const char *ls_name = ctx->argv[2]; + const struct nbrec_logical_switch *ls = NULL; + char *error = ls_by_name_or_uuid(ctx, ls_name, true, &ls); + if (error) { + ctx->error = error; + return; + } + + /* Virtual IP for the group */ + ovs_be32 ipv4 = 0; + const char *fwd_group_vip = ctx->argv[3]; + if (!ip_parse(fwd_group_vip, &ipv4)) { + ctl_error(ctx, "invalid ip address %s", fwd_group_vip); + return; + } + + /* Virtual MAC for the group */ + const char *fwd_group_vmac = ctx->argv[4]; + struct eth_addr ea; + if (!eth_addr_from_string(fwd_group_vmac, &ea)) { + ctl_error(ctx, "invalid mac address %s", fwd_group_vmac); + return; + } + + /* Create the forwarding group */ + struct nbrec_forwarding_group *fwd_group = NULL; + fwd_group = nbrec_forwarding_group_insert(ctx->txn); + nbrec_forwarding_group_set_name(fwd_group, fwd_group_name); + nbrec_forwarding_group_set_vip(fwd_group, fwd_group_vip); + nbrec_forwarding_group_set_vmac(fwd_group, fwd_group_vmac); + + int n_child_port = ctx->argc - 5; + const char **child_port = (const char **)&ctx->argv[5]; + + /* Verify that child ports belong to the logical switch specified */ + for (int i = 5; i < ctx->argc; ++i) { + const struct nbrec_logical_switch_port *lsp; + const char *lsp_name = ctx->argv[i]; + error = lsp_by_name_or_uuid(ctx, lsp_name, false, &lsp); + if (error) { + ctx->error = error; + return; + } + if (lsp) { + error = lsp_to_ls(ctx->idl, lsp, &ls); + if (error) { + ctx->error = error; + return; + } + if (strcmp(ls->name, ls_name)) { + ctl_error(ctx, "%s: port already exists but in logical " + "switch %s", lsp_name, ls->name); + return; + } + } else { + ctl_error(ctx, "%s: logical switch port does not exist", lsp_name); + return; + } + } + nbrec_forwarding_group_set_child_port(fwd_group, child_port, n_child_port); + + /* Liveness option */ + bool liveness = shash_find(&ctx->options, "--liveness") != NULL; + if (liveness) { + nbrec_forwarding_group_set_liveness(fwd_group, true); + } + + struct nbrec_forwarding_group **new_fwd_groups = + xmalloc(sizeof(*new_fwd_groups) * (ls->n_forwarding_groups + 1)); + memcpy(new_fwd_groups, ls->forwarding_groups, + sizeof *new_fwd_groups * ls->n_forwarding_groups); + new_fwd_groups[ls->n_forwarding_groups] = fwd_group; + nbrec_logical_switch_set_forwarding_groups(ls, new_fwd_groups, + (ls->n_forwarding_groups + 1)); + free(new_fwd_groups); + +} + +static void +nbctl_fwd_group_del(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_forwarding_group *fwd_group = NULL; + + fwd_group = fwd_group_by_name_or_uuid(ctx, id); + if (!fwd_group) { + return; + } + + const struct nbrec_logical_switch *ls = NULL; + ls = fwd_group_to_logical_switch(ctx, fwd_group); + if (!ls) { + return; + } + + for (int i = 0; i < ls->n_forwarding_groups; ++i) { + if (!strcmp(ls->forwarding_groups[i]->name, fwd_group->name)) { + struct nbrec_forwarding_group **new_fwd_groups = + xmemdup(ls->forwarding_groups, + sizeof *new_fwd_groups * ls->n_forwarding_groups); + new_fwd_groups[i] = + ls->forwarding_groups[ls->n_forwarding_groups - 1]; + nbrec_logical_switch_set_forwarding_groups(ls, new_fwd_groups, + (ls->n_forwarding_groups - 1)); + free(new_fwd_groups); + nbrec_forwarding_group_delete(fwd_group); + return; + } + } +} + +static void +fwd_group_list_all(struct ctl_context *ctx, const char *ls_name) +{ + const struct nbrec_logical_switch *ls; + struct ds *s = &ctx->output; + const struct nbrec_forwarding_group *fwd_group = NULL; + + if (ls_name) { + char *error = ls_by_name_or_uuid(ctx, ls_name, true, &ls); + if (error) { + ctx->error = error; + return; + } + if (!ls) { + ctl_error( + ctx, "%s: a logical switch with this name does not exist", + ls_name); + return; + } + } + + ds_put_format(s, "%-16.16s%-14.16s%-16.7s%-22.21s%s\n", + "FWD_GROUP", "LS", "VIP", "VMAC", "CHILD_PORTS"); + + NBREC_FORWARDING_GROUP_FOR_EACH(fwd_group, ctx->idl) { + ls = fwd_group_to_logical_switch(ctx, fwd_group); + if (!ls) { + continue; + } + + if (ls_name && (strcmp(ls->name, ls_name))) { + continue; + } + + ds_put_format(s, "%-16.16s%-14.18s%-15.16s%-9.18s ", + fwd_group->name, ls->name, + fwd_group->vip, fwd_group->vmac); + for (int i = 0; i < fwd_group->n_child_port; ++i) { + ds_put_format(s, " %s", fwd_group->child_port[i]); + } + ds_put_char(s, '\n'); + } +} + +static void +nbctl_fwd_group_list(struct ctl_context *ctx) +{ + if (ctx->argc == 1) { + fwd_group_list_all(ctx, NULL); + } else if (ctx->argc == 2) { + fwd_group_list_all(ctx, ctx->argv[1]); + } +} + static int route_cmp_details(const struct nbrec_logical_router_static_route *r1, @@ -5829,6 +6074,14 @@ static const struct ctl_command_syntax nbctl_commands[] = { nbctl_lsp_get_dhcpv6_options, NULL, "", RO }, { "lsp-get-ls", 1, 1, "PORT", NULL, nbctl_lsp_get_ls, NULL, "", RO }, + /* forwarding group commands. */ + { "fwd-group-add", 4, INT_MAX, "SWITCH GROUP VIP VMAC PORT...", + NULL, nbctl_fwd_group_add, NULL, "--liveness", RW }, + { "fwd-group-del", 1, 1, "GROUP", NULL, nbctl_fwd_group_del, NULL, + "--if-exists", RW }, + { "fwd-group-list", 0, 1, "[GROUP]", NULL, nbctl_fwd_group_list, NULL, + "", RO }, + /* logical router commands. */ { "lr-add", 0, 1, "[ROUTER]", NULL, nbctl_lr_add, NULL, "--may-exist,--add-duplicate", RW },