From patchwork Tue Nov 5 12:53:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dumitru Ceara X-Patchwork-Id: 1189677 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.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="JfMMIvaN"; dkim-atps=neutral Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 476qQ03x6Hz9sNT for ; Tue, 5 Nov 2019 23:55:04 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id A0CFE147F; Tue, 5 Nov 2019 12:53:40 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 3F586FFB for ; Tue, 5 Nov 2019 12:53:39 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [205.139.110.61]) by smtp1.linuxfoundation.org (Postfix) with ESMTP id BEF248AA for ; Tue, 5 Nov 2019 12:53:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1572958415; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Q+AdG5/9uFrT/E3KAZJzkmALiU3qaRsFJn+hN5wUrQs=; b=JfMMIvaN4hEpmvDrRprkZ4+KluoN/WMe5qy0UTuT3/twzbRS25RTeAiJWr2K2zafNtZfTt FmmLEiw8wU6MoH6CcDtjxjnCVNnWp+Oec/DPyl9ZEr8Jo5qeq7rjOTltpjy6v4yJynno9J OmWjrZAlVTn2xj056jlxGn3JG/O4evY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-286-3FwlmuLeNTS5bec9JScmQg-1; Tue, 05 Nov 2019 07:53:32 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 37685477; Tue, 5 Nov 2019 12:53:31 +0000 (UTC) Received: from dceara.remote.csb (ovpn-117-128.ams2.redhat.com [10.36.117.128]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2285D5C1B2; Tue, 5 Nov 2019 12:53:28 +0000 (UTC) From: Dumitru Ceara To: dev@openvswitch.org Date: Tue, 5 Nov 2019 13:53:26 +0100 Message-Id: <20191105125322.4760.83141.stgit@dceara.remote.csb> In-Reply-To: <20191105125242.4760.67348.stgit@dceara.remote.csb> References: <20191105125242.4760.67348.stgit@dceara.remote.csb> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-MC-Unique: 3FwlmuLeNTS5bec9JScmQg-1 X-Mimecast-Spam-Score: 0 X-Spam-Status: No, score=-4.3 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Cc: hzhou@ovn.org Subject: [ovs-dev] [RFC PATCH v2 ovn 2/5] ovn-northd: Add support for CoPP. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Add new 'Copp' (Control plane protection) table to OVN Northbound DB: - this stores mappings between control plane protocol names and meters that should be used to rate limit controller-destined traffic for those protocols. Add new 'copp' columns to the following OVN Northbound DB tables: - Logical_Switch - Logical_Switch_Port - Logical_Router - Logical_Router_Port This allows defining control plane policies with different granularities. For example a user can decide to enforce a general policy for the logical switch but at the same time configure a different policy on some of the ports of the logical switch. Control plane protocol policies applied to a logical port take precedence over the ones defined at logical switch level. For logical routers and logical router ports we take the same approach. For now, no control plane protection policy is installed for any of the existing flows that punt packets to ovn-controller. This will be added in follow-up patches. Add CLI commands in 'ovn-nbctl' to allow the user to manage Control Plane Protection Policies at different levels (logical switch, logical router, logical port). Signed-off-by: Dumitru Ceara --- lib/automake.mk | 2 lib/copp.c | 99 +++++++++++ lib/copp.h | 58 ++++++ northd/ovn-northd.c | 43 +++-- ovn-nb.ovsschema | 24 ++- ovn-nb.xml | 91 ++++++++++ utilities/ovn-nbctl.8.xml | 94 ++++++++++ utilities/ovn-nbctl.c | 412 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 808 insertions(+), 15 deletions(-) create mode 100644 lib/copp.c create mode 100644 lib/copp.h diff --git a/lib/automake.mk b/lib/automake.mk index 0c8245c..7ba7ca0 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -9,6 +9,8 @@ lib_libovn_la_SOURCES = \ lib/actions.c \ lib/chassis-index.c \ lib/chassis-index.h \ + lib/copp.c \ + lib/copp.h \ lib/ovn-dirs.h \ lib/expr.c \ lib/extend-table.h \ diff --git a/lib/copp.c b/lib/copp.c new file mode 100644 index 0000000..820cc29 --- /dev/null +++ b/lib/copp.c @@ -0,0 +1,99 @@ +/* Copyright (c) 2019, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "openvswitch/shash.h" +#include "smap.h" +#include "lib/ovn-nb-idl.h" +#include "lib/copp.h" + +static char *copp_proto_names[COPP_PROTO_MAX] = { + [COPP_ARP] = "arp", + [COPP_ARP_RESOLVE] = "arp-resolve", + [COPP_DHCPV4_OPTS] = "dhcpv4-opts", + [COPP_DHCPV6_OPTS] = "dhcpv6-opts", + [COPP_DNS] = "dns", + [COPP_EVENT_ELB] = "event-elb", + [COPP_ICMP4_ERR] = "icmp4-error", + [COPP_ICMP6_ERR] = "icmp6-error", + [COPP_IGMP] = "igmp", + [COPP_ND_NA] = "nd-na", + [COPP_ND_NS] = "nd-ns", + [COPP_ND_NS_RESOLVE] = "nd-ns-resolve", + [COPP_ND_RA_OPTS] = "nd-ra-opts", + [COPP_TCP_RESET] = "tcp-reset", +}; + +static bool copp_port_support[COPP_PROTO_MAX] = { + [COPP_DHCPV4_OPTS] = true, + [COPP_DHCPV6_OPTS] = true, + [COPP_ICMP4_ERR] = true, + [COPP_ICMP6_ERR] = true, + [COPP_ND_RA_OPTS] = true, + [COPP_TCP_RESET] = true, +}; + +/* Return true if the copp meter can be configured on a logical port. Return + * false if the meter is only supported on a logical switch/router. + */ +bool copp_port_meter_supported(enum copp_proto proto) +{ + if (proto >= COPP_PROTO_MAX) { + return false; + } + + return copp_port_support[proto]; +} + +const char * +copp_proto_get(enum copp_proto proto) +{ + if (proto >= COPP_PROTO_MAX) { + return ""; + } + return copp_proto_names[proto]; +} + +const char * +copp_meter_get(enum copp_proto proto, const struct nbrec_copp *copp, + const struct shash *meter_groups) +{ + if (!copp || proto >= COPP_PROTO_MAX) { + return NULL; + } + + const char *meter = smap_get(&copp->meters, copp_proto_names[proto]); + + if (meter && shash_find(meter_groups, meter)) { + return meter; + } + + return NULL; +} + +const char * +copp_port_meter_get(enum copp_proto proto, const struct nbrec_copp *port_copp, + const struct nbrec_copp *dp_copp, + const struct shash *meter_groups) +{ + const char *meter = copp_meter_get(proto, port_copp, meter_groups); + + if (!meter) { + return copp_meter_get(proto, dp_copp, meter_groups); + } + return meter; +} diff --git a/lib/copp.h b/lib/copp.h new file mode 100644 index 0000000..e989206 --- /dev/null +++ b/lib/copp.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2019, Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_COPP_H +#define OVN_COPP_H 1 + +/* + * Control plane protection - metered actions. + */ +enum copp_proto { + COPP_PROTO_FIRST, + COPP_ARP = COPP_PROTO_FIRST, + COPP_ARP_RESOLVE, + COPP_DHCPV4_OPTS, + COPP_DHCPV6_OPTS, + COPP_DNS, + COPP_EVENT_ELB, + COPP_ICMP4_ERR, + COPP_ICMP6_ERR, + COPP_IGMP, + COPP_ND_NA, + COPP_ND_NS, + COPP_ND_NS_RESOLVE, + COPP_ND_RA_OPTS, + COPP_TCP_RESET, + COPP_PROTO_MAX, + COPP_PROTO_INVALID = COPP_PROTO_MAX, +}; + +struct nbrec_copp; + +bool copp_port_meter_supported(enum copp_proto proto); + +const char *copp_proto_get(enum copp_proto); + +const char *copp_meter_get(enum copp_proto proto, + const struct nbrec_copp *copp, + const struct shash *meter_groups); + +const char *copp_port_meter_get(enum copp_proto proto, + const struct nbrec_copp *port_copp, + const struct nbrec_copp *dp_copp, + const struct shash *meter_groups); + + +#endif /* lib/copp.h */ diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index c23c270..4808299 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -29,6 +29,7 @@ #include "openvswitch/json.h" #include "ovn/lex.h" #include "lib/chassis-index.h" +#include "lib/copp.h" #include "lib/ip-mcast-index.h" #include "lib/mcast-group-index.h" #include "lib/ovn-l7.h" @@ -3351,6 +3352,7 @@ struct ovn_lflow { char *match; char *actions; char *stage_hint; + char *ctrl_meter; const char *where; }; @@ -3371,14 +3373,15 @@ ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b) && a->stage == b->stage && a->priority == b->priority && !strcmp(a->match, b->match) - && !strcmp(a->actions, b->actions)); + && !strcmp(a->actions, b->actions) + && nullable_string_is_equal(a->ctrl_meter, b->ctrl_meter)); } static void ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, - char *match, char *actions, char *stage_hint, - const char *where) + char *match, char *actions, char *ctrl_meter, + char *stage_hint, const char *where) { lflow->od = od; lflow->stage = stage; @@ -3386,6 +3389,7 @@ ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od, lflow->match = match; lflow->actions = actions; lflow->stage_hint = stage_hint; + lflow->ctrl_meter = ctrl_meter; lflow->where = where; } @@ -3394,36 +3398,44 @@ static void ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, const char *match, const char *actions, - const char *stage_hint, const char *where) + const char *ctrl_meter, const char *stage_hint, + const char *where) { ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od)); struct ovn_lflow *lflow = xmalloc(sizeof *lflow); ovn_lflow_init(lflow, od, stage, priority, xstrdup(match), xstrdup(actions), + nullable_xstrdup(ctrl_meter), nullable_xstrdup(stage_hint), where); hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow)); } /* Adds a row with the specified contents to the Logical_Flow table. */ #define ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ - ACTIONS, STAGE_HINT) \ + ACTIONS, CTRL_METER, STAGE_HINT) \ ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ - STAGE_HINT, OVS_SOURCE_LOCATOR) + CTRL_METER, STAGE_HINT, OVS_SOURCE_LOCATOR) #define ovn_lflow_add(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \ ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ - ACTIONS, NULL) + ACTIONS, NULL, NULL) + +#define ovn_lflow_add_ctrl(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ + CTRL_METER) \ + ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ + ACTIONS, CTRL_METER, NULL) static struct ovn_lflow * ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, - const char *match, const char *actions, uint32_t hash) + const char *match, const char *actions, const char *ctrl_meter, + uint32_t hash) { struct ovn_lflow target; ovn_lflow_init(&target, od, stage, priority, CONST_CAST(char *, match), CONST_CAST(char *, actions), - NULL, NULL); + CONST_CAST(char *, ctrl_meter), NULL, NULL); struct ovn_lflow *lflow; HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) { @@ -3442,6 +3454,7 @@ ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow) free(lflow->match); free(lflow->actions); free(lflow->stage_hint); + free(lflow->ctrl_meter); free(lflow); } } @@ -4528,7 +4541,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, ovn_lflow_add_with_hint(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, acl->match, ds_cstr(&actions), - stage_hint); + NULL, stage_hint); ds_destroy(&actions); } else { struct ds match = DS_EMPTY_INITIALIZER; @@ -4557,7 +4570,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, acl->priority + OVN_ACL_PRI_OFFSET, ds_cstr(&match), ds_cstr(&actions), - stage_hint); + NULL, stage_hint); /* Match on traffic in the request direction for an established * connection tracking entry that has not been marked for @@ -4577,7 +4590,7 @@ consider_acl(struct hmap *lflows, struct ovn_datapath *od, ovn_lflow_add_with_hint(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, ds_cstr(&match), ds_cstr(&actions), - stage_hint); + NULL, stage_hint); ds_destroy(&match); ds_destroy(&actions); @@ -8843,7 +8856,8 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths, = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT; struct ovn_lflow *lflow = ovn_lflow_find( &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id), - sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash); + sbflow->priority, sbflow->match, sbflow->actions, + sbflow->controller_meter, sbflow->hash); if (lflow) { ovn_lflow_destroy(&lflows, lflow); } else { @@ -8862,6 +8876,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths, sbrec_logical_flow_set_priority(sbflow, lflow->priority); sbrec_logical_flow_set_match(sbflow, lflow->match); sbrec_logical_flow_set_actions(sbflow, lflow->actions); + sbrec_logical_flow_set_controller_meter(sbflow, lflow->ctrl_meter); /* Trim the source locator lflow->where, which looks something like * "ovn/northd/ovn-northd.c:1234", down to just the part following the @@ -10272,6 +10287,8 @@ main(int argc, char *argv[]) add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions); + add_column_noalert(ovnsb_idl_loop.idl, + &sbrec_logical_flow_col_controller_meter); ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group); add_column_noalert(ovnsb_idl_loop.idl, diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index 084305b..f4bf000 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "5.17.0", - "cksum": "1128988054 23237", + "version": "5.18.0", + "cksum": "600367587 24306", "tables": { "NB_Global": { "columns": { @@ -26,6 +26,14 @@ "ipsec": {"type": "boolean"}}, "maxRows": 1, "isRoot": true}, + "Copp": { + "columns": { + "meters": { + "type": {"key": "string", + "value": "string", + "min": 0, + "max": "unlimited"}}}, + "isRoot": true}, "Logical_Switch": { "columns": { "name": {"type": "string"}, @@ -54,6 +62,9 @@ "refType": "weak"}, "min": 0, "max": "unlimited"}}, + "copp": {"type": {"key": {"type": "uuid", "refTable": "Copp", + "refType": "weak"}, + "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, @@ -108,6 +119,9 @@ "refType": "strong"}, "min": 0, "max": 1}}, + "copp": {"type": {"key": {"type": "uuid", "refTable": "Copp", + "refType": "weak"}, + "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, @@ -265,6 +279,9 @@ "refType": "weak"}, "min": 0, "max": "unlimited"}}, + "copp": {"type": {"key": {"type": "uuid", "refTable": "Copp", + "refType": "weak"}, + "min": 0, "max": 1}}, "options": { "type": {"key": "string", "value": "string", @@ -303,6 +320,9 @@ "ipv6_ra_configs": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, + "copp": {"type": {"key": {"type": "uuid", "refTable": "Copp", + "refType": "weak"}, + "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, diff --git a/ovn-nb.xml b/ovn-nb.xml index d8f3237..6cac51a 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -147,6 +147,65 @@ + +

+ This table is used to define control plane protection policies, i.e., + associate entries from table to control protocol + names. +

+ + Rate limiting meter for ARP packets (request/reply) used for learning + neighbors. + + + Rate limiting meter for packets that require resolving the next-hop + (through ARP). + + + Rate limiting meter for packets that require adding DHCPv4 options. + + + Rate limiting meter for packets that require adding DHCPv6 options. + + + Rate limiting meter for DNS query packets that need to be replied to. + + + Rate limiting meter for empty load balancer events. + + + Rate limiting meter for packets that require replying with an ICMP + error. + + + Rate limiting meter for packets that require replying with an ICMPv6 + error. + + + Rate limiting meter for IGMP packets. + + + Rate limiting meter for ND neighbor advertisement packets used for + learning neighbors. + + + Rate limiting meter for ND neighbor solicitation packets used for + learning neighbors. + + + Rate limiting meter for packets that require resolving the next-hop + (through ND). + + + Rate limiting meter for packets that require adding ND router + advertisement options. + + + Rate limiting meter for packets that require replying with TCP RST + packet. + +
+

Each row represents one L2 logical switch. @@ -347,6 +406,14 @@ + +

+ The control plane protection policy from table + used for metering packets sent to ovn-controller from + ports of this logical switch. +

+ + See External IDs at the beginning of this document. @@ -1136,6 +1203,14 @@ + +

+ The control plane protection policy from table + used for metering packets sent to ovn-controller from + this logical port. +

+
+

@@ -1563,6 +1638,14 @@ + +

+ The control plane protection policy from table + used for metering packets sent to ovn-controller from + logical ports of this router. +

+
+ See External IDs at the beginning of this document. @@ -2087,6 +2170,14 @@ + +

+ The control plane protection policy from table + used for metering packets sent to ovn-controller from + this logical port. +

+
+ See External IDs at the beginning of this document. diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml index 88ebd13..b60a3f8 100644 --- a/utilities/ovn-nbctl.8.xml +++ b/utilities/ovn-nbctl.8.xml @@ -959,6 +959,100 @@ +

Control Plane Protection Policy commands

+ +
+
ls-copp-add switch proto + meter
+
+ Adds the control proto to meter mapping + to the switch control plane protection policy. If no + policy exists yet, it creates one. If a mapping already existed for + proto, this will overwrite it. +
+ +
ls-copp-del switch [proto]
+
+ Removes the control proto mapping from the + switch control plane protection policy. If + proto is not specified, the whole control plane + protection policy is destroyed. +
+ +
ls-copp-list switch
+
+ Display the current control plane protection policy for + switch. +
+ +
lsp-copp-add proto proto + meter
+
+ Adds the control proto to meter mapping + to the port control plane protection policy. If no + policy exists yet, it creates one. If a mapping already existed for + proto, this will overwrite it. +
+ +
lsp-copp-del port [proto]
+
+ Removes the control proto mapping from the + port control plane protection policy. If + proto is not specified, the whole control plane + protection policy is destroyed. +
+
lsp-copp-list port
+
+ Display the current control plane protection policy for + port. +
+ +
lr-copp-add router proto + meter
+
+ Adds the control proto to meter mapping + to the router control plane protection policy. If no + policy exists yet, it creates one. If a mapping already existed for + proto, this will overwrite it. +
+ +
lr-copp-del router [proto]
+
+ Removes the control proto mapping from the + router control plane protection policy. If + proto is not specified, the whole control plane + protection policy is destroyed. +
+ +
lr-copp-list router
+
+ Display the current control plane protection policy for + router. +
+ +
lrp-copp-add proto proto + meter
+
+ Adds the control proto to meter mapping + to the port control plane protection policy. If no + policy exists yet, it creates one. If a mapping already existed for + proto, this will overwrite it. +
+ +
lrp-copp-del port [proto]
+
+ Removes the control proto mapping from the + port control plane protection policy. If + proto is not specified, the whole control plane + protection policy is destroyed. +
+
lrp-copp-list port
+
+ Display the current control plane protection policy for + port. +
+
+

Database Commands

These commands query and modify the contents of ovsdb tables. They are a slight abstraction of the ovsdb interface and diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index 8188948..a5801c2 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -27,6 +27,7 @@ #include "jsonrpc.h" #include "openvswitch/json.h" #include "lib/acl-log.h" +#include "lib/copp.h" #include "lib/ovn-nb-idl.h" #include "lib/ovn-util.h" #include "packets.h" @@ -753,6 +754,48 @@ chassis with optional PRIORITY to the HA chassis group GRP\n\ ha-chassis-group-del-chassis GRP CHASSIS Deletes the HA chassis\ CHASSIS from the HA chassis group GRP\n\ \n\ +Control Plane Protection Policy commands:\n\ + ls-copp-add SWITCH PROTO METER\n\ + Add a copp policy for PROTO packets on SWITCH\n\ + based on an existing METER.\n\ + ls-copp-del SWITCH [PROTO]\n\ + Delete the copp policy for PROTO packets on\n\ + SWITCH. If PROTO is not specified, delete all\n\ + copp policies on SWITCH.\n\ + ls-copp-list SWITCH\n\ + List all copp policies defined for control\n\ + protocols on SWITCH.\n\ + lsp-copp-add PORT PROTO METER\n\ + Add a copp policy for PROTO packets on switch\n\ + PORT based on an existing METER.\n\ + lsp-copp-del PORT [PROTO]\n\ + Delete the copp policy for PROTO packets on\n\ + switch PORT. If PROTO is not specified, delete\n\ + all copp policies on switch PORT.\n\ + lsp-copp-list PORT\n\ + List all copp policies defined for control\n\ + protocols on switch PORT.\n\ + lr-copp-add ROUTER PROTO METER\n\ + Add a copp policy for PROTO packets on ROUTER\n\ + based on an existing METER.\n\ + lr-copp-del ROUTER [PROTO]\n\ + Delete the copp policy for PROTO packets on\n\ + ROUTER. If PROTO is not specified, delete all\n\ + copp policies on ROUTER.\n\ + lr-copp-list ROUTER\n\ + List all copp policies defined for control\n\ + protocols on ROUTER.\n\ + lrp-copp-add PORT PROTO METER\n\ + Add a copp policy for PROTO packets on router\n\ + PORT based on an existing METER.\n\ + lrp-copp-del PORT [PROTO]\n\ + Delete the copp policy for PROTO packets on\n\ + router PORT. If PROTO is not specified, delete\n\ + all copp policies on router PORT.\n\ + lrp-copp-list PORT\n\ + List all copp policies defined for control\n\ + protocols on router PORT.\n\ +\n\ %s\ %s\ \n\ @@ -4853,6 +4896,353 @@ nbctl_lr_route_list(struct ctl_context *ctx) free(ipv6_routes); } +static char * +copp_proto_validate(const char *proto_name, bool per_port) +{ + for (size_t i = COPP_PROTO_FIRST; i < COPP_PROTO_MAX; i++) { + if (!strcmp(proto_name, copp_proto_get(i))) { + if (per_port && !copp_port_meter_supported(i)) { + break; + } + return NULL; + } + } + + struct ds usage = DS_EMPTY_INITIALIZER; + + ds_put_cstr(&usage, "Invalid control protocol. Allowed values: "); + for (size_t i = COPP_PROTO_FIRST; i < COPP_PROTO_MAX; i++) { + if (per_port && !copp_port_meter_supported(i)) { + continue; + } + ds_put_format(&usage, "%s, ", copp_proto_get(i)); + } + ds_chomp(&usage, ' '); + ds_chomp(&usage, ','); + ds_put_cstr(&usage, "."); + + char *usage_str = xstrdup(ds_cstr(&usage)); + ds_destroy(&usage); + return usage_str; +} + +static const struct nbrec_copp * +copp_add_meter(struct ctl_context *ctx, const struct nbrec_copp *copp, + const char *proto_name, const char *meter) +{ + if (!copp) { + copp = nbrec_copp_insert(ctx->txn); + } + + struct smap meters; + smap_init(&meters); + smap_clone(&meters, &copp->meters); + smap_replace(&meters, proto_name, meter); + nbrec_copp_set_meters(copp, &meters); + smap_destroy(&meters); + + return copp; +} + +static void +copp_del_meter(const struct nbrec_copp *copp, const char *proto_name) +{ + if (!copp) { + return; + } + + if (proto_name) { + if (smap_get(&copp->meters, proto_name)) { + struct smap meters; + smap_init(&meters); + smap_clone(&meters, &copp->meters); + smap_remove(&meters, proto_name); + nbrec_copp_set_meters(copp, &meters); + smap_destroy(&meters); + } + } else { + nbrec_copp_delete(copp); + } +} + +static void +copp_list(struct ctl_context *ctx, const struct nbrec_copp *copp) +{ + if (!copp) { + return; + } + + struct smap_node *node; + + SMAP_FOR_EACH (node, &copp->meters) { + ds_put_format(&ctx->output, "%s: %s\n", node->key, node->value); + } +} + +static void +nbctl_ls_copp_add(struct ctl_context *ctx) +{ + const char *ls_name = ctx->argv[1]; + const char *proto_name = ctx->argv[2]; + const char *meter = ctx->argv[3]; + + char *error = copp_proto_validate(proto_name, false); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_logical_switch *ls = NULL; + error = ls_by_name_or_uuid(ctx, ls_name, true, &ls); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_copp *copp = + copp_add_meter(ctx, ls->copp, proto_name, meter); + nbrec_logical_switch_set_copp(ls, copp); +} + +static void +nbctl_ls_copp_del(struct ctl_context *ctx) +{ + const char *ls_name = ctx->argv[1]; + const char *proto_name = NULL; + char *error; + + if (ctx->argc == 3) { + proto_name = ctx->argv[2]; + error = copp_proto_validate(proto_name, false); + if (error) { + ctx->error = error; + return; + } + } + + const struct nbrec_logical_switch *ls = NULL; + error = ls_by_name_or_uuid(ctx, ls_name, true, &ls); + if (error) { + ctx->error = error; + return; + } + + copp_del_meter(ls->copp, proto_name); +} + +static void +nbctl_ls_copp_list(struct ctl_context *ctx) +{ + const char *ls_name = ctx->argv[1]; + + 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; + } + + copp_list(ctx, ls->copp); +} + +static void +nbctl_lsp_copp_add(struct ctl_context *ctx) +{ + const char *lsp_name = ctx->argv[1]; + const char *proto_name = ctx->argv[2]; + const char *meter = ctx->argv[3]; + + char *error = copp_proto_validate(proto_name, true); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_logical_switch_port *lsp = NULL; + error = lsp_by_name_or_uuid(ctx, lsp_name, true, &lsp); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_copp *copp = + copp_add_meter(ctx, lsp->copp, proto_name, meter); + nbrec_logical_switch_port_set_copp(lsp, copp); +} + +static void +nbctl_lsp_copp_del(struct ctl_context *ctx) +{ + const char *lsp_name = ctx->argv[1]; + const char *proto_name = NULL; + char *error; + + if (ctx->argc == 3) { + proto_name = ctx->argv[2]; + error = copp_proto_validate(proto_name, true); + if (error) { + ctx->error = error; + return; + } + } + + const struct nbrec_logical_switch_port *lsp = NULL; + error = lsp_by_name_or_uuid(ctx, lsp_name, true, &lsp); + if (error) { + ctx->error = error; + return; + } + + copp_del_meter(lsp->copp, proto_name); +} + +static void +nbctl_lsp_copp_list(struct ctl_context *ctx) +{ + const char *lsp_name = ctx->argv[1]; + + const struct nbrec_logical_switch_port *lsp = NULL; + char *error = lsp_by_name_or_uuid(ctx, lsp_name, true, &lsp); + if (error) { + ctx->error = error; + return; + } + + copp_list(ctx, lsp->copp); +} + +static void +nbctl_lr_copp_add(struct ctl_context *ctx) +{ + const char *lr_name = ctx->argv[1]; + const char *proto_name = ctx->argv[2]; + const char *meter = ctx->argv[3]; + + char *error = copp_proto_validate(proto_name, false); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_logical_router *lr = NULL; + error = lr_by_name_or_uuid(ctx, lr_name, true, &lr); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_copp *copp = + copp_add_meter(ctx, lr->copp, proto_name, meter); + nbrec_logical_router_set_copp(lr, copp); +} + +static void +nbctl_lr_copp_del(struct ctl_context *ctx) +{ + const char *lr_name = ctx->argv[1]; + const char *proto_name = NULL; + char *error; + + if (ctx->argc == 3) { + proto_name = ctx->argv[2]; + error = copp_proto_validate(proto_name, false); + if (error) { + ctx->error = error; + return; + } + } + + const struct nbrec_logical_router *lr = NULL; + error = lr_by_name_or_uuid(ctx, lr_name, true, &lr); + if (error) { + ctx->error = error; + return; + } + + copp_del_meter(lr->copp, proto_name); +} + +static void +nbctl_lr_copp_list(struct ctl_context *ctx) +{ + const char *lr_name = ctx->argv[1]; + + const struct nbrec_logical_router *lr = NULL; + char *error = lr_by_name_or_uuid(ctx, lr_name, true, &lr); + if (error) { + ctx->error = error; + return; + } + + copp_list(ctx, lr->copp); +} + +static void +nbctl_lrp_copp_add(struct ctl_context *ctx) +{ + const char *lrp_name = ctx->argv[1]; + const char *proto_name = ctx->argv[2]; + const char *meter = ctx->argv[3]; + + char *error = copp_proto_validate(proto_name, true); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_logical_router_port *lrp = NULL; + error = lrp_by_name_or_uuid(ctx, lrp_name, true, &lrp); + if (error) { + ctx->error = error; + return; + } + + const struct nbrec_copp *copp = + copp_add_meter(ctx, lrp->copp, proto_name, meter); + nbrec_logical_router_port_set_copp(lrp, copp); +} + +static void +nbctl_lrp_copp_del(struct ctl_context *ctx) +{ + const char *lrp_name = ctx->argv[1]; + const char *proto_name = NULL; + char *error; + + if (ctx->argc == 3) { + proto_name = ctx->argv[2]; + error = copp_proto_validate(proto_name, true); + if (error) { + ctx->error = error; + return; + } + } + + const struct nbrec_logical_router_port *lrp = NULL; + error = lrp_by_name_or_uuid(ctx, lrp_name, true, &lrp); + if (error) { + ctx->error = error; + return; + } + + copp_del_meter(lrp->copp, proto_name); +} + +static void +nbctl_lrp_copp_list(struct ctl_context *ctx) +{ + const char *lrp_name = ctx->argv[1]; + + const struct nbrec_logical_router_port *lrp = NULL; + char *error = lrp_by_name_or_uuid(ctx, lrp_name, true, &lrp); + if (error) { + ctx->error = error; + return; + } + + copp_list(ctx, lrp->copp); +} + static void verify_connections(struct ctl_context *ctx) { @@ -5780,6 +6170,28 @@ static const struct ctl_command_syntax nbctl_commands[] = { {"dhcp-options-get-options", 1, 1, "DHCP_OPT_UUID", NULL, nbctl_dhcp_options_get_options, NULL, "", RO }, + /* Control plane protection commands */ + {"ls-copp-add", 3, 3, "SWITCH PROTO METER", NULL, nbctl_ls_copp_add, NULL, + "", RW}, + {"ls-copp-del", 1, 2, "SWITCH [PROTO]", NULL, nbctl_ls_copp_del, NULL, + "", RW}, + {"ls-copp-list", 1, 1, "SWITCH", NULL, nbctl_ls_copp_list, NULL, "", RO}, + {"lsp-copp-add", 3, 3, "PORT PROTO METER", NULL, nbctl_lsp_copp_add, NULL, + "", RW}, + {"lsp-copp-del", 1, 2, "PORT [PROTO]", NULL, nbctl_lsp_copp_del, NULL, + "", RW}, + {"lsp-copp-list", 1, 1, "PORT", NULL, nbctl_lsp_copp_list, NULL, "", RO}, + {"lr-copp-add", 3, 3, "ROUTER PROTO METER", NULL, nbctl_lr_copp_add, NULL, + "", RW}, + {"lr-copp-del", 1, 2, "ROUTER [PROTO]", NULL, nbctl_lr_copp_del, NULL, + "", RW}, + {"lr-copp-list", 1, 1, "ROUTER", NULL, nbctl_lr_copp_list, NULL, "", RO}, + {"lrp-copp-add", 3, 3, "PORT PROTO METER", NULL, nbctl_lrp_copp_add, NULL, + "", RW}, + {"lrp-copp-del", 1, 2, "PORT [PROTO]", NULL, nbctl_lrp_copp_del, NULL, + "", RW}, + {"lrp-copp-list", 1, 1, "PORT", NULL, nbctl_lrp_copp_list, NULL, "", RO}, + /* Connection commands. */ {"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "", RO}, {"del-connection", 0, 0, "", pre_connection, cmd_del_connection, NULL, "", RW},