From patchwork Wed Jan 22 20:09:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manoj Sharma X-Patchwork-Id: 1227416 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=A1KnL1wH; 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 482xMR57mfz9sRG for ; Thu, 23 Jan 2020 07:09:39 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id C264E8757F; Wed, 22 Jan 2020 20:09:37 +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 wvZn0qvV07f9; Wed, 22 Jan 2020 20:09:18 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by whitealder.osuosl.org (Postfix) with ESMTP id 2B5FE8757D; Wed, 22 Jan 2020 20:09:14 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E1908C1D84; Wed, 22 Jan 2020 20:09:13 +0000 (UTC) X-Original-To: ovs-dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id BCEEBC0174 for ; Wed, 22 Jan 2020 20:09:11 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id A86BC21FA8 for ; Wed, 22 Jan 2020 20:09:11 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Fd4+XOvuBYMQ for ; Wed, 22 Jan 2020 20:09:06 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx0a-002c1b01.pphosted.com (mx0a-002c1b01.pphosted.com [148.163.151.68]) by silver.osuosl.org (Postfix) with ESMTPS id 9726F21F76 for ; Wed, 22 Jan 2020 20:09:06 +0000 (UTC) Received: from pps.filterd (m0127839.ppops.net [127.0.0.1]) by mx0a-002c1b01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 00MK4Nuu011701 for ; Wed, 22 Jan 2020 12:09:05 -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=bi4Nru08ZXArd4kDUVST0nFqG2UrU+R6+JHaITBKpiE=; b=A1KnL1wHbkUQF9l1khBQy65lm2H+DMeeuOxdMYR58s/yOo/j0pDxKvJL3g3MxZbegzsA /TuakiVywX9hW67ssQHyVWuMXW2BnRuJ3AoM0UPxQePMsaQ2/FnRUU1X9zJk37mr4kle Bj9/3C+Lk7WqHzE7tdVhQpbvM+phPiaQzuaRmBrKsc5AHlPD0pp4alLr00fHSyq+Ty4F akUx1Pi11tU5y1ANHzeycraLN0aS8tn7qfLnyMXEfgGUoFyzs341vUd94iAejtjMKXOs YAst2js141vxNOlCIZMdKNuGB2x32fjFq4Qw15IJWD6vh/fxmdE3q7gYs/MOur61hE27 Fg== Received: from nam11-dm6-obe.outbound.protection.outlook.com (mail-dm6nam11lp2174.outbound.protection.outlook.com [104.47.57.174]) by mx0a-002c1b01.pphosted.com with ESMTP id 2xm1yp8txm-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Wed, 22 Jan 2020 12:09:05 -0800 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=JpLhp9JmJ/xWBXWgDU4sLpXflVkL7MuQnCw67Eqi2lXITu9YEpwUaM0EFX2P9BLKOK/1eYjiCoJlVTAxgZhfGfF/p4FoS9xZrQWX7IFRJKT+dG4wVUpaNU355eKrdoEHa0w+BfnS6+juws/W7UCfz9uw5nuEWfIOIsHdEWpzRdf3ArM93aLSm5vnGhyGUb7ruUaCTO1OxVN8Y7sltT17K++NXTlY9JoxA3EDVkmh1FUWz41hd3b8wZmYo8oAKq6PEAebkQpGl8oysL+G2cO3XkQjVH61pBdArFk2Bz5TNr03pg4fUA434W6I+3PaAFd68DGAFHN/jy2HkFUcp9PFBA== 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=bi4Nru08ZXArd4kDUVST0nFqG2UrU+R6+JHaITBKpiE=; b=E8nReSIodIRg/gcijLYuJ9Q+tvPKUyi1yfqLaB1p3AVFnuMAxnK45Jtqs5Lam2QB6EA3pi4lj098cDwMXtKyVTIN/GBk40X/2zFz6sFOCuwbw24YHy0i/CiHhO+VtbEmQ8vcd+IAhJi4524jabpmfNfyNCIo89p79xOca4TBPkxKvgR9iwnZdUYpNQWbgl2+a2ztod3+uGb39dvHiU3o4moY0xWKsBANjdRENB+Vb0VwGxiScyUufvIRTM4rHxamNf+De28wj02zB5yPe42GK27OZmaHDdwwzqcakZ/SlkPLc77mhkPeRH2S43Sg3AonCSdpQkVTUBUDNaxj2wHENg== 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 SN6PR02MB5710.namprd02.prod.outlook.com (20.177.250.213) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.2644.18; Wed, 22 Jan 2020 20:09:03 +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 20:09:03 +0000 Received: from PC11S3MA.corp.nutanix.com (192.146.154.3) by BYAPR04CA0030.namprd04.prod.outlook.com (2603:10b6:a03:40::43) 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 20:09:02 +0000 From: Manoj Sharma To: "ovs-dev@openvswitch.org" Thread-Topic: [PATCH v3 ovn 1/2] Forwarding group to load balance l2 traffic with liveness detection Thread-Index: AQHV0V/Kn6WsNCHVGEGoZA/rP0A6zQ== Date: Wed, 22 Jan 2020 20:09:03 +0000 Message-ID: <20200122200845.61083-2-manoj.sharma@nutanix.com> References: <20200122200845.61083-1-manoj.sharma@nutanix.com> In-Reply-To: <20200122200845.61083-1-manoj.sharma@nutanix.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: BYAPR04CA0030.namprd04.prod.outlook.com (2603:10b6:a03:40::43) 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: 30bb5299-21d2-40d8-f4a5-08d79f76ecbd x-ms-traffictypediagnostic: SN6PR02MB5710: 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)(376002)(346002)(396003)(39860400002)(136003)(189003)(199004)(30864003)(66946007)(66574012)(16526019)(26005)(66446008)(2906002)(478600001)(64756008)(66556008)(186003)(44832011)(66476007)(1076003)(8936002)(4326008)(81166006)(81156014)(8676002)(6486002)(86362001)(956004)(2616005)(36756003)(6916009)(316002)(7696005)(52116002)(71200400001)(107886003)(5660300002)(559001)(579004); DIR:OUT; SFP:1102; SCL:1; SRVR:SN6PR02MB5710; H:SN6PR02MB4621.namprd02.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; MX:1; A: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: CfmjQYO9jrpGIEE3QBPcEIfC4W0P6k6XEqMnR9mbZZtZ1m0dbrrn6Hm7xiowQs8qawBJoXD2F5sKHioDGOPbEmQYh4bexmQ1WdgE+qqeuhotLqyn+28qrN8hm69q+Z2NzyLxxOqo3ARdUDNzCAIfQKVfy1F6Kz/4TG0f8S8+tZ/myX5KdjlE9TQa+WRLP4jDKqSFOuHjwtaBAjbqPuSRpUTrcxOcKiZ1o5seDj3FAXuJk7oCG8IVev6LqEHnGBGzqcQNh6yXLcuBKdMwjEdYYS+U8jJIjjdXs/LSycNzjAi12jtPHA+QLUP5SY2nDthuqYZDZ9AJhdf9XSr11IYobalztvYVNHruzy9l/zrNo9LgvMKUN4OvcPfQSw+cBXomurGzO6GbGNAoR6+wi58xskBfIXs6EOmmk4ww4pQxkAQTxDsX5/D3VEffQ5G39wDa MIME-Version: 1.0 X-OriginatorOrg: nutanix.com X-MS-Exchange-CrossTenant-Network-Message-Id: 30bb5299-21d2-40d8-f4a5-08d79f76ecbd X-MS-Exchange-CrossTenant-originalarrivaltime: 22 Jan 2020 20:09:03.3604 (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: 5fkYGHlSREXSvxOeg4d2cdi1ucCRWLN4llWisvKTM0cIvFpnaBz+OapESDiOhTpw9JdYmloGb8yGg8of41AFWRHgUTl3orXyJbtGyWp9Y70= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN6PR02MB5710 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 v3 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 4b227ca..47ec328 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 5ae52bb..decb4ae 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 82167c4..ea33481 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2114,6 +2114,25 @@ tcp.flags = RST;

    Example: handle_svc_check(inport);

    + +
    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 2679f1f..077c41e 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -1735,6 +1735,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 adb677c..f128c7e 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -17374,3 +17374,127 @@ AT_CHECK([ovn-trace --ovs lsw0 'inport == "lp1" && eth.type == 0x1234' | grep "d 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 88ebd13..e8572f7 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 46ba3a9..39f53da 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\ @@ -4720,6 +4727,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]); + } +} + struct ipv4_route { int priority; @@ -5704,6 +5949,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 },