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 }, From patchwork Wed Jan 22 21:31:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manoj Sharma X-Patchwork-Id: 1227438 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=hemlock.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=mfVmdgfx; dkim-atps=neutral Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 482zB05W6Qz9s1x for ; Thu, 23 Jan 2020 08:31:36 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id C5EFD877B5; Wed, 22 Jan 2020 21:31:34 +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 OYtmK7hgE18t; Wed, 22 Jan 2020 21:31:32 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by hemlock.osuosl.org (Postfix) with ESMTP id 4BF0E87E7A; Wed, 22 Jan 2020 21:31:32 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 0FEE5C0176; 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 whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 93389C1D81 for ; Wed, 22 Jan 2020 21:31:29 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 7373F86D05 for ; Wed, 22 Jan 2020 21:31:29 +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 UofbgVqVFwnN for ; Wed, 22 Jan 2020 21:31:28 +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 whitealder.osuosl.org (Postfix) with ESMTPS id E439F86B90 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 00MLQaWv001964 for ; Wed, 22 Jan 2020 13:31:27 -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=K4EAnwTPAxVXhf3SubC4ovzfPCAdE5bl09f4lCl9hwg=; b=mfVmdgfxt+sIAIy7tGktZDyJd9fsagCftS7VGv7gcLhnMHUe6ncfuQVnZinkULPf9iW0 Q+43NjM5nrX/FvLLfJ8ROkRw35llPtFtUw+9aVOAqe8yYMnf7H58ATvZVtUaOB/qiXVo 9HBzBVxCPpVpH/xiFe9kwEjXIo2lk28+kvvYi8XVPFj9uQlGt1IFVwKrQ//1MJ0IT1pi hzNDMiJ9WujLkIf6CVcV4gi/xaluKqRyyA3PqiBlIcjWIXpUb+3nh5JVNlYfvV7oQju2 M672GhSrkHrSOXEwdKZKAGQNM0jWSDcNt/KcFE3bBX8zxVXOIe3U4sNILfq/SLEyKJSh 7Q== 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-3 (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=jYttqOAn/IB3+H2qJSAU6rVKmqmMGDtT941N22z6fbPg3plrP+a6GblR0R3EEFhwrw8LhzjzntG3ZNDUoSaFTY0fNL3Y2rdXKSGFWlpoC9YigWm6rMIBss++WyjFb15q24XXX/mEa+5fK3dTtDcUaWA3RkzaPzCqfNdrYi4vWAW/WfqfeG4JSjUgBEzdyw2ulujdUoO09pCnPUV9xevHGsgJonksL+SCZEqMkcAjjbUkX79AyHTjqSnHebfkS5Inriflk/aNVtD0tkJyQqEr3gi5fAuWwrda7n6MjAWqaj5/fLl4g+qiVIeelq6rkba1CykEbbOrN4rSU54FiS/LLQ== 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=K4EAnwTPAxVXhf3SubC4ovzfPCAdE5bl09f4lCl9hwg=; b=UvmsL2rqBv9BZeJUQD9ik+yniohfMqbdwkG3QjekX5xG51fsuQ6kDQ3+Tj2Auqlew+a9C6wf7P5JEKaFQMfNZaOiZHuvc/9McthvIFJJNPX1P2/0nv1m6MViyVMTafjDRJiENIYDD8f/BDziu1z5NHJG/Z2HlpHoJzYuyNICRmgqwlo6spr8+wtpuXf5OHKZ4Xs+ZTb80WHsiqFP1ZIqLokg6t5/IhWJGVGhVXOX+WJOQkel6ldragg6p/+ik2Mdh1LK0fx60Xd2tFXaXzJwPrBhEBNC8ZCStoqlh4DafqwkOnkOmYIPPThkKdI5NeSikJ9YD13aUv6sSr+pGkGQBA== 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:25 +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:25 +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:24 +0000 From: Manoj Sharma To: "ovs-dev@openvswitch.org" Thread-Topic: [PATCH v4 ovn 2/2] Forwarding group to load balance l2 traffic with liveness detection Thread-Index: AQHV0WtMXsdZWl6ypESCoUpfdvjs/w== Date: Wed, 22 Jan 2020 21:31:25 +0000 Message-ID: <20200122213118.65988-3-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: 4585f965-ea4d-4406-e64f-08d79f826eab x-ms-traffictypediagnostic: SN6PR02MB4861: x-ms-exchange-transport-forked: True x-microsoft-antispam-prvs: x-proofpoint-crosstenant: true x-ms-oob-tlc-oobclassifiers: OLM:428; 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); 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: XEUjcXyNaNAp5glKzf8Y8o1Im5b4egAjwe/9LpAaFSm0Qckuq9+SV8k39SmrAQZaJiLcRJaI2tjk3r3RGoCejrvSCmpuu6sYHJnnqZ9jXMvwx9RPRY/6AC+bLwHZLHPWQUE1n5mBNoHs8SHIJW6Jx5e45iC119/KijPjHpbDzcp44jR8ddSRZbDftMdeB6lYCia7MSMdMMtFx9KXr0DsQo25a7X+5bfOWmt9lSsuq91Q6MB6oeTU3SusfHlWMfWGJDIYDvb6H7Pa+850DRL/bGQxmws/qF4B/hU2IbYpxWGXSAfELzzIkycEIflZgFn6rNVFa3YIYZeAzbSSIgPCglfIW904d0DqQerUmwUrdzvxBBIRGw3elufhEzfnr2WPAM9lF+E0Zu4WYwZojWYaV34YrclWPOCbSscnsMntvMhlp7syqkptaY9TjuNgaVfb MIME-Version: 1.0 X-OriginatorOrg: nutanix.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4585f965-ea4d-4406-e64f-08d79f826eab X-MS-Exchange-CrossTenant-originalarrivaltime: 22 Jan 2020 21:31:25.6457 (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: /tYR/PKYOyPUS8xV2pnoWEpC90NHx2F2VwlkYibzkfAogJRFbehYbb0Jw02mv0nBnvyglNvrBRyOckfnTstjH3p45OYP72dVH5C+yPVn3jw= 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 2/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" A forwarding group is an aggregation of logical switch ports of a logical switch to load balance traffic across the ports. It also detects the liveness if the logical switch ports are realized as OVN tunnel ports on the physical topology. In the below logical topology diagram, the logical switch has two ports connected to chassis / external routers R1 and R2. The logical router needs to send traffic to an external network that is connected through R1 and R2. +----+ +----------+ R1 | ***** / +----+ ** ** +----------+ +--------------+ / lsp1 * * | Logical | | Logical |/ * External * | Router +--------+ switch X * Network * | | | |\ * * +----------+ +--------------+ \ lsp2 * * ^ \ +----+ ** ** | +----------+ R2 | ***** | +----+ fwd_group -> (lsp1, lsp2) In the absence of forwarding group, the logical router will have unicast route to point to either R1 or R2. In case of R1 or R2 going down, it will require control plane's intervention to update the route to point to proper nexthop. With forwarding group, a virtual IP (VIP) and virtual MAC (VMAC) address are configured on the forwarding group. The logical router points to the forwarding group's VIP as the nexthop for hosts behind R1 and R2. [root@fwd-group]# ovn-nbctl fwd-group-add fwd ls1 VIP_1 VMAC_1 lsp1 lsp2 [root@fwd-group]# ovn-nbctl fwd-group-list FWD_GROUP LS VIP VMAC CHILD_PORTS fwd ls1 VIP_1 VMAC_1 lsp1 lsp2 [root@fwd-group]# ovn-nbctl lr-route-list lr1 IPv4 Routes external_host_prefix/prefix_len VIP_1 dst-ip The logical switch will install an ARP responder rule to reply with VMAC as the MAC address for ARP requests for VIP. It will also install a MAC lookup rule for VMAC with action to load balance across the logical switch ports of the forwarding group. Datapath: "ls1" Pipeline: ingress table=10(ls_in_arp_rsp ), priority=50 , match=(arp.tpa == VIP_1 && arp.op == 1), action=(eth.dst = eth.src; eth.src = VMAC_1; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = VMAC_1; arp.tpa = arp.spa; arp.spa = VIP; outport = inport; flags.loopback = 1; output;) table=13(ls_in_l2_lkup ), priority=50 , match=(eth.dst == VMAC_1), action=(fwd_group(childports="lsp1","lsp2");) In the physical topology, OVN managed hypervisors are connected to R1 and R2 through overlay tunnels. The logical flow's "fwd_group" action mentioned above, gets translated to openflow group type "select" with one bucket for each logical switch port. cookie=0x0, duration=16.869s, table=29, n_packets=4, n_bytes=392, idle_age=0, priority=111,metadata=0x9,dl_dst=VMAC_1 actions=group:1 group_id=1,type=select,selection_method=dp_hash, bucket=actions=load:0x2->NXM_NX_REG15[0..15], resubmit(,32), bucket=actions=load:0x3->NXM_NX_REG15[0..15],resubmit(,32) where 0x2 and 0x3 are port tunnel keys of lsp1 and lsp2. The openflow group type "select" with selection method "dp_hash" load balances traffic based on source and destination Ethernet address, VLAN ID, Ethernet type, IPv4/v6 source and destination address and protocol, and for TCP and SCTP only, the source and destination ports. To detect path failure between OVN managed hypervisors and (R1, R2), BFD is enabled on the tunnel interfaces. The openflow group is modified to include watch_port for liveness detection of a port. The forwarding group must be configured with --liveness to enable it. With liveness enabled, the logical flow changes to: table=13(ls_in_l2_lkup ), priority=50 , match=(eth.dst == VMAC_1), action=(fwd_group(liveness="true",childports="lsp1","lsp2");) While the openflow group is: group_id=1,type=select,selection_method=dp_hash, bucket=watch_port:31,actions=load:0x2->NXM_NX_REG15[0..15],resubmit(,32), bucket=watch_port:32,actions=load:0x3->NXM_NX_REG15[0..15],resubmit(,32) Where 31 and 32 are ovs port numbers for the tunnel interfaces connecting to R1 and R2. If the BFD forwarding status is down for any of the tunnels, the corresponding bucket will not be selected for packet forwarding. Signed-off-by: Manoj Sharma --- controller/lflow.c | 20 +++++++ controller/physical.c | 13 +++++ controller/physical.h | 4 ++ include/ovn/actions.h | 19 ++++++- lib/actions.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++ northd/ovn-northd.c | 64 +++++++++++++++++++++++ utilities/ovn-trace.c | 3 ++ 7 files changed, 264 insertions(+), 1 deletion(-) diff --git a/controller/lflow.c b/controller/lflow.c index 997c596..9a3c1eb 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -105,6 +105,25 @@ lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp) return false; } +/* Given the OVN port name, get its openflow port */ +static bool +tunnel_ofport_cb(const void *aux_, const char *port_name, ofp_port_t *ofport) +{ + const struct lookup_port_aux *aux = aux_; + + const struct sbrec_port_binding *pb + = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name); + if (!pb || (pb->datapath != aux->dp) || !pb->chassis) { + return false; + } + + if (!get_tunnel_ofport(pb->chassis->name, NULL, ofport)) { + return false; + } + + return true; +} + static bool is_chassis_resident_cb(const void *c_aux_, const char *port_name) { @@ -773,6 +792,7 @@ consider_logical_flow( struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); struct ovnact_encode_params ep = { .lookup_port = lookup_port_cb, + .tunnel_ofport = tunnel_ofport_cb, .aux = &aux, .is_switch = is_switch(ldp), .group_table = group_table, diff --git a/controller/physical.c b/controller/physical.c index 500d419..af1d10f 100644 --- a/controller/physical.c +++ b/controller/physical.c @@ -1794,3 +1794,16 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name, simap_destroy(&new_tunnel_to_ofport); } + +bool +get_tunnel_ofport(const char *chassis_name, char *encap_ip, ofp_port_t *ofport) +{ + struct chassis_tunnel *tun = NULL; + tun = chassis_tunnel_find(chassis_name, encap_ip); + if (!tun) { + return false; + } + + *ofport = tun->ofport; + return true; +} diff --git a/controller/physical.h b/controller/physical.h index c93f6b1..c0e17cd 100644 --- a/controller/physical.h +++ b/controller/physical.h @@ -72,4 +72,8 @@ void physical_handle_mc_group_changes( const struct simap *ct_zones, const struct hmap *local_datapaths, struct ovn_desired_flow_table *); +bool get_tunnel_ofport( + const char *chassis_name, + char *encap_ip, + ofp_port_t *ofport); #endif /* controller/physical.h */ diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 2d4b05b..9b01492 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -90,7 +90,8 @@ struct ovn_extend_table; OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) \ OVNACT(TRIGGER_EVENT, ovnact_controller_event) \ OVNACT(BIND_VPORT, ovnact_bind_vport) \ - OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) + OVNACT(HANDLE_SVC_CHECK, ovnact_handle_svc_check) \ + OVNACT(FWD_GROUP, ovnact_fwd_group) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -374,6 +375,15 @@ struct ovnact_handle_svc_check { struct expr_field port; /* Logical port name. */ }; +/* OVNACT_FWD_GROUP. */ +struct ovnact_fwd_group { + struct ovnact ovnact; + bool liveness; + char **child_ports; /* Logical ports */ + size_t n_child_ports; + uint8_t ltable; /* Logical table ID of next table. */ +}; + /* Internal use by the helpers below. */ void ovnact_init(struct ovnact *, enum ovnact_type, size_t len); void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len); @@ -635,6 +645,13 @@ struct ovnact_encode_params { * '*portp' and returns true; otherwise, returns false. */ bool (*lookup_port)(const void *aux, const char *port_name, unsigned int *portp); + + /* Looks up tunnel port to a chassis by its port name. If found, stores + * its openflow port number in '*ofport' and returns true; + * otherwise, returns false. */ + bool (*tunnel_ofport)(const void *aux, const char *port_name, + ofp_port_t *ofport); + const void *aux; /* 'true' if the flow is for a switch. */ diff --git a/lib/actions.c b/lib/actions.c index cd3f586..6cde9ea 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -2988,6 +2988,146 @@ ovnact_handle_svc_check_free(struct ovnact_handle_svc_check *sc OVS_UNUSED) { } +static void +parse_fwd_group_action(struct action_context *ctx) +{ + char *child_port, **child_port_list = NULL; + size_t allocated_ports = 0; + size_t n_child_ports = 0; + bool liveness = false; + + if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { + if (lexer_match_id(ctx->lexer, "liveness")) { + if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { + return; + } + if (ctx->lexer->token.type != LEX_T_STRING) { + lexer_syntax_error(ctx->lexer, + "expecting true/false"); + return; + } + if (!strcmp(ctx->lexer->token.s, "true")) { + liveness = true; + lexer_get(ctx->lexer); + } + lexer_force_match(ctx->lexer, LEX_T_COMMA); + } + if (lexer_match_id(ctx->lexer, "childports")) { + if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { + return; + } + while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { + if (ctx->lexer->token.type != LEX_T_STRING) { + lexer_syntax_error(ctx->lexer, + "expecting logical switch port"); + if (child_port_list) { + free(child_port_list); + } + return; + } + /* Parse child's logical ports */ + child_port = xstrdup(ctx->lexer->token.s); + lexer_get(ctx->lexer); + lexer_match(ctx->lexer, LEX_T_COMMA); + + if (n_child_ports >= allocated_ports) { + child_port_list = x2nrealloc(child_port_list, + &allocated_ports, + sizeof *child_port_list); + } + child_port_list[n_child_ports++] = child_port; + } + } + } + + struct ovnact_fwd_group *fwd_group = ovnact_put_FWD_GROUP(ctx->ovnacts); + fwd_group->ltable = ctx->pp->cur_ltable + 1; + fwd_group->liveness = liveness; + fwd_group->child_ports = child_port_list; + fwd_group->n_child_ports = n_child_ports; +} + +static void +format_FWD_GROUP(const struct ovnact_fwd_group *fwd_group, struct ds *s) +{ + ds_put_cstr(s, "fwd_group("); + if (fwd_group->liveness) { + ds_put_cstr(s, "liveness=true,"); + } + if (fwd_group->n_child_ports) { + for (size_t i = 0; i < fwd_group->n_child_ports; i++) { + if (i) { + ds_put_cstr(s, ", "); + } + + ds_put_format(s, "childports=%s", fwd_group->child_ports[i]); + } + } + ds_put_cstr(s, ");"); +} + +static void +encode_FWD_GROUP(const struct ovnact_fwd_group *fwd_group, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + if (!fwd_group->n_child_ports) { + /* Nothing to do without child ports */ + return; + } + + uint32_t reg_index = MFF_LOG_OUTPORT - MFF_REG0; + struct ds ds = DS_EMPTY_INITIALIZER; + + ds_put_format(&ds, "type=select,selection_method=dp_hash"); + + for (size_t i = 0; i < fwd_group->n_child_ports; i++) { + uint32_t port_tunnel_key; + ofp_port_t ofport; + + const char *port_name = fwd_group->child_ports[i]; + + /* Find the tunnel key of the logical port */ + if (!ep->lookup_port(ep->aux, port_name, &port_tunnel_key)) { + return; + } + ds_put_format(&ds, ",bucket="); + + if (fwd_group->liveness) { + /* Find the openflow port number of the tunnel port */ + if (!ep->tunnel_ofport(ep->aux, port_name, &ofport)) { + return; + } + + /* Watch port for failure, used with BFD */ + ds_put_format(&ds, "watch_port:%d,", ofport); + } + + ds_put_format(&ds, "load=0x%d->NXM_NX_REG%d[0..15]", + port_tunnel_key, reg_index); + ds_put_format(&ds, ",resubmit(,%d)", ep->output_ptable); + } + + uint32_t table_id = 0; + struct ofpact_group *og; + table_id = ovn_extend_table_assign_id(ep->group_table, ds_cstr(&ds), + ep->lflow_uuid); + ds_destroy(&ds); + if (table_id == EXT_TABLE_ID_INVALID) { + return; + } + + /* Create an action to set the group */ + og = ofpact_put_GROUP(ofpacts); + og->group_id = table_id; +} + +static void +ovnact_fwd_group_free(struct ovnact_fwd_group *fwd_group) +{ + free(fwd_group->child_ports); +} + /* Parses an assignment or exchange or put_dhcp_opts action. */ static void parse_set_action(struct action_context *ctx) @@ -3110,6 +3250,8 @@ parse_action(struct action_context *ctx) parse_bind_vport(ctx); } else if (lexer_match_id(ctx->lexer, "handle_svc_check")) { parse_handle_svc_check(ctx); + } else if (lexer_match_id(ctx->lexer, "fwd_group")) { + parse_fwd_group_action(ctx); } else { lexer_syntax_error(ctx->lexer, "expecting action"); } diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index b1e782e..14a615b 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -5428,6 +5428,61 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs) } static void +build_fwd_group_lflows(struct ovn_datapath *od, struct hmap *lflows) +{ + struct ds match = DS_EMPTY_INITIALIZER; + struct ds actions = DS_EMPTY_INITIALIZER; + + for (int i = 0; i < od->nbs->n_forwarding_groups; ++i) { + const struct nbrec_forwarding_group *fwd_group = NULL; + fwd_group = od->nbs->forwarding_groups[i]; + if (!fwd_group || (fwd_group->n_child_port == 0)) { + continue; + } + + /* ARP responder for the forwarding group's virtual IP */ + ds_put_format(&match, "arp.tpa == %s && arp.op == 1", + fwd_group->vip); + ds_put_format(&actions, + "eth.dst = eth.src; " + "eth.src = %s; " + "arp.op = 2; /* ARP reply */ " + "arp.tha = arp.sha; " + "arp.sha = %s; " + "arp.tpa = arp.spa; " + "arp.spa = %s; " + "outport = inport; " + "flags.loopback = 1; " + "output;", + fwd_group->vmac, fwd_group->vmac, fwd_group->vip); + + ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 50, + ds_cstr(&match), ds_cstr(&actions)); + + /* L2 lookup for the forwarding group's virtual MAC */ + ds_clear(&match); + ds_put_format(&match, "eth.dst == %s", fwd_group->vmac); + + /* Create a comma separated string of child ports */ + struct ds group_ports = DS_EMPTY_INITIALIZER; + if (fwd_group->liveness) { + ds_put_cstr(&group_ports, "liveness=\"true\","); + } + ds_put_cstr(&group_ports, "childports="); + for (i = 0; i < (fwd_group->n_child_port - 1); ++i) { + ds_put_format(&group_ports, "\"%s\",", fwd_group->child_port[i]); + } + ds_put_format(&group_ports, "\"%s\"", + fwd_group->child_port[fwd_group->n_child_port - 1]); + + ds_clear(&actions); + ds_put_format(&actions, "fwd_group(%s);", ds_cstr(&group_ports)); + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 50, + ds_cstr(&match), ds_cstr(&actions)); + } +} + +static void build_lrouter_groups__(struct hmap *ports, struct ovn_datapath *od) { ovs_assert((od && od->nbr && od->lr_group)); @@ -5727,6 +5782,15 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, build_stateful(od, lflows, lbs); } + /* Build logical flows for the forwarding groups */ + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbs || !od->nbs->n_forwarding_groups) { + continue; + } + + build_fwd_group_lflows(od, lflows); + } + /* Logical switch ingress table 0: Admission control framework (priority * 100). */ HMAP_FOR_EACH (od, key_node, datapaths) { diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index c5c40a8..89f1a87 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -2291,6 +2291,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, case OVNACT_HANDLE_SVC_CHECK: break; + + case OVNACT_FWD_GROUP: + break; } } ds_destroy(&s);