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);