From patchwork Thu Jul 8 16:40:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1502514 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=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=ZtpowoUx; dkim-atps=neutral Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GLMVG6pQDz9sWc for ; Fri, 9 Jul 2021 02:40:38 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 5B97760C15; Thu, 8 Jul 2021 16:40:36 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yOxU4AU8DNMm; Thu, 8 Jul 2021 16:40:34 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id B340660AB3; Thu, 8 Jul 2021 16:40:33 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7C8CFC001A; Thu, 8 Jul 2021 16:40:33 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) by lists.linuxfoundation.org (Postfix) with ESMTP id 22A47C000E for ; Thu, 8 Jul 2021 16:40:32 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 4D25A41D08 for ; Thu, 8 Jul 2021 16:40:28 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp2.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id u0PmMiPq1137 for ; Thu, 8 Jul 2021 16:40:26 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp2.osuosl.org (Postfix) with ESMTPS id BB7E241D1C for ; Thu, 8 Jul 2021 16:40:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1625762425; 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=RjG//TfaE9f/pyz3HK33wyF2QteKXuqf9whjXNp7o4U=; b=ZtpowoUxe6XE8b/snTQc3aujETisua4A6wCrdBQFt4TZAfCIuPQDKJPvVNFDTitb+DXM7j McL431X07QDobhmx+XZI/A7zZDQwCkv+4yqOzzU8LOQoSQFR3fm95U9jMjXgypq8MZhvzR W0xll0/oaTKW8hhKpQ7gGyBElUOtdvM= Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-452-LrAraJW4NN28rO_bhpuGqA-1; Thu, 08 Jul 2021 12:40:22 -0400 X-MC-Unique: LrAraJW4NN28rO_bhpuGqA-1 Received: by mail-ed1-f71.google.com with SMTP id cn11-20020a0564020cabb0290396d773d4c7so3630728edb.18 for ; Thu, 08 Jul 2021 09:40:22 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RjG//TfaE9f/pyz3HK33wyF2QteKXuqf9whjXNp7o4U=; b=NMVxOSy/CuT05mUhHn5A6wmHdLv6a60q5kDy9W+Yrjh33l/pHgBntW0kNhqE8Sqqnl dOO3+5cVwaxOxevArBpjyRSl0i2k12ZJh7aQ45zeGj7ND7dv/c5r2yy+NjQaoOr20JwH 0k3jHTARbH3XMGkYQ4ImNwLgFH4lsFA2/fKC2rBRI7AFYUEwbVwtBxfAwID1zPjGumBb vdkU5n6lx1AUSU2VpSqYXC/FIbajqaDSBoMC6iMnOAb2912zpt7sswCdjRgIiRiN6e7n rJ7CIMbGXsqrUZFp0UtH9YSCdxKgixYqpGciz0TkNYcWQ/xffmNRxWCnMdccY/7qQkwW NIVA== X-Gm-Message-State: AOAM533gthy/mGrWYZc5kYcqJb+u1NkKjDWaes9626LliGYkeTiOaTW5 v2aNHeYzmjlB4pvpeSV4DZ40mKVIChjI4dw/5Nss4fWKALR3dyc70+FNfb9Nhu+U3HN0q9ugolg LwpgLEs1IBEZkDgKSNIIeeokBfaZ24jmq/Xw8kvzSWsA9SlImEOg7oY6h6y7WBAIPPPBPlZZn7P Q= X-Received: by 2002:a05:6402:40c4:: with SMTP id z4mr40030773edb.364.1625762420576; Thu, 08 Jul 2021 09:40:20 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyWMZ6/6DiyMmlYwRfYKnvXz/qF3Bjx5gy0OKH+z9I7WD0RaCE+Howk7yjG2EZLSJr9HIYM3A== X-Received: by 2002:a05:6402:40c4:: with SMTP id z4mr40030744edb.364.1625762420292; Thu, 08 Jul 2021 09:40:20 -0700 (PDT) Received: from lore-desk.redhat.com (net-93-71-3-244.cust.vodafonedsl.it. [93.71.3.244]) by smtp.gmail.com with ESMTPSA id ee25sm1568103edb.6.2021.07.08.09.40.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jul 2021 09:40:19 -0700 (PDT) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Thu, 8 Jul 2021 18:40:02 +0200 Message-Id: <8de7a63a25e244422f62d30c1898bea655702917.1625762235.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=lorenzo.bianconi@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Cc: dceara@redhat.com Subject: [ovs-dev] [PATCH v6 ovn 1/4] ovn-controller: Add support for Logical_Flow control meters 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" From: Dumitru Ceara Add a new 'controller_meter' column to OVN Southbound Logical_Flow table. This stores an optional string which should correspond to the Meter that must be used for rate limiting controller actions generated by packets hitting the flow. Add a new 'ofctrl_add_flow_metered' function to create a new 'ovn_flow' with an attached controller meter. Change ofctrl_check_and_add_flow to allow specifying a meter ID for packets that are punted to controller. Change consider_logical_flow to parse controller_meter from the logical flow and use it when building openflow entries. Add a new 'ctrl_meter_id' field to 'struct ovnact_encode_params' to be used when encoding controller actions from logical flow actions. Acked-by: Mark D. Gray Co-authored-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Dumitru Ceara --- controller/lflow.c | 40 +++++++++++++++++++++++--- controller/ofctrl.c | 56 +++++++++++++++++++++++++----------- controller/ofctrl.h | 21 ++++++++++---- controller/physical.c | 9 +++--- include/ovn/actions.h | 2 ++ lib/actions.c | 66 +++++++++++++++++++++++-------------------- northd/ovn_northd.dl | 2 ++ ovn-sb.ovsschema | 4 ++- ovn-sb.xml | 6 ++++ 9 files changed, 143 insertions(+), 63 deletions(-) diff --git a/controller/lflow.c b/controller/lflow.c index 60aa011ff..f90c20136 100644 --- a/controller/lflow.c +++ b/controller/lflow.c @@ -575,6 +575,27 @@ update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs) return false; } +static void +lflow_parse_ctrl_meter(const struct sbrec_logical_flow *lflow, + struct ovn_extend_table *meter_table, + uint32_t *meter_id) +{ + ovs_assert(meter_id); + *meter_id = NX_CTLR_NO_METER; + + if (lflow->controller_meter) { + *meter_id = ovn_extend_table_assign_id(meter_table, + lflow->controller_meter, + lflow->header_.uuid); + if (*meter_id == EXT_TABLE_ID_INVALID) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "Unable to assign id for meter: %s", + lflow->controller_meter); + return; + } + } +} + static void add_matches_to_flow_table(const struct sbrec_logical_flow *lflow, const struct sbrec_datapath_binding *dp, @@ -592,6 +613,13 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow, .lfrr = l_ctx_out->lfrr, }; + /* Parse any meter to be used if this flow should punt packets to + * controller. + */ + uint32_t ctrl_meter_id = NX_CTLR_NO_METER; + lflow_parse_ctrl_meter(lflow, l_ctx_out->meter_table, + &ctrl_meter_id); + /* Encode OVN logical actions into OpenFlow. */ uint64_t ofpacts_stub[1024 / 8]; struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); @@ -615,6 +643,7 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow, .ct_snat_vip_ptable = OFTABLE_CT_SNAT_FOR_VIP, .fdb_ptable = OFTABLE_GET_FDB, .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB, + .ctrl_meter_id = ctrl_meter_id, }; ovnacts_encode(ovnacts->data, ovnacts->size, &ep, &ofpacts); @@ -639,9 +668,11 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow, } } if (!m->n) { - ofctrl_add_flow(l_ctx_out->flow_table, ptable, lflow->priority, - lflow->header_.uuid.parts[0], &m->match, &ofpacts, - &lflow->header_.uuid); + ofctrl_add_flow_metered(l_ctx_out->flow_table, ptable, + lflow->priority, + lflow->header_.uuid.parts[0], &m->match, + &ofpacts, &lflow->header_.uuid, + ctrl_meter_id); } else { uint64_t conj_stubs[64 / 8]; struct ofpbuf conj; @@ -659,7 +690,8 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow, ofctrl_add_or_append_flow(l_ctx_out->flow_table, ptable, lflow->priority, 0, - &m->match, &conj, &lflow->header_.uuid); + &m->match, &conj, &lflow->header_.uuid, + ctrl_meter_id); ofpbuf_uninit(&conj); } } diff --git a/controller/ofctrl.c b/controller/ofctrl.c index 053631590..eebb27567 100644 --- a/controller/ofctrl.c +++ b/controller/ofctrl.c @@ -66,6 +66,7 @@ struct ovn_flow { struct ofpact *ofpacts; size_t ofpacts_len; uint64_t cookie; + uint32_t ctrl_meter_id; /* Meter to be used for controller actions. */ }; /* A desired flow, in struct ovn_desired_flow_table, calculated by the @@ -220,7 +221,8 @@ static struct desired_flow *desired_flow_alloc( uint16_t priority, uint64_t cookie, const struct match *match, - const struct ofpbuf *actions); + const struct ofpbuf *actions, + uint32_t meter_id); static struct desired_flow *desired_flow_lookup( struct ovn_desired_flow_table *, const struct ovn_flow *target); @@ -1014,8 +1016,9 @@ link_flow_to_sb(struct ovn_desired_flow_table *flow_table, /* Flow table interfaces to the rest of ovn-controller. */ /* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to - * the OpenFlow table numbered 'table_id' with the given 'priority' and - * OpenFlow 'cookie'. The caller retains ownership of 'match' and 'actions'. + * the OpenFlow table numbered 'table_id' with the given 'priority', OpenFlow + * 'cookie' and 'meter_id'. The caller retains ownership of 'match' and + * 'actions'. * * The flow is also linked to the sb_uuid that generates it. * @@ -1024,15 +1027,15 @@ link_flow_to_sb(struct ovn_desired_flow_table *flow_table, * * The caller should initialize its own hmap to hold the flows. */ void -ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table, - uint8_t table_id, uint16_t priority, - uint64_t cookie, const struct match *match, - const struct ofpbuf *actions, - const struct uuid *sb_uuid, - bool log_duplicate_flow) +ofctrl_check_and_add_flow_metered(struct ovn_desired_flow_table *flow_table, + uint8_t table_id, uint16_t priority, + uint64_t cookie, const struct match *match, + const struct ofpbuf *actions, + const struct uuid *sb_uuid, + uint32_t meter_id, bool log_duplicate_flow) { struct desired_flow *f = desired_flow_alloc(table_id, priority, cookie, - match, actions); + match, actions, meter_id); if (desired_flow_lookup_check_uuid(flow_table, &f->flow, sb_uuid)) { if (log_duplicate_flow) { @@ -1060,8 +1063,20 @@ ofctrl_add_flow(struct ovn_desired_flow_table *desired_flows, const struct match *match, const struct ofpbuf *actions, const struct uuid *sb_uuid) { - ofctrl_check_and_add_flow(desired_flows, table_id, priority, cookie, - match, actions, sb_uuid, true); + ofctrl_add_flow_metered(desired_flows, table_id, priority, cookie, + match, actions, sb_uuid, NX_CTLR_NO_METER); +} + +void +ofctrl_add_flow_metered(struct ovn_desired_flow_table *desired_flows, + uint8_t table_id, uint16_t priority, uint64_t cookie, + const struct match *match, + const struct ofpbuf *actions, + const struct uuid *sb_uuid, uint32_t meter_id) +{ + ofctrl_check_and_add_flow_metered(desired_flows, table_id, priority, + cookie, match, actions, sb_uuid, + meter_id, true); } /* Either add a new flow, or append actions on an existing flow. If the @@ -1072,12 +1087,14 @@ ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, uint8_t table_id, uint16_t priority, uint64_t cookie, const struct match *match, const struct ofpbuf *actions, - const struct uuid *sb_uuid) + const struct uuid *sb_uuid, + uint32_t meter_id) { struct desired_flow *existing; struct desired_flow *f; - f = desired_flow_alloc(table_id, priority, cookie, match, actions); + f = desired_flow_alloc(table_id, priority, cookie, match, actions, + meter_id); existing = desired_flow_lookup_conjunctive(desired_flows, &f->flow); if (existing) { /* There's already a flow with this particular match and action @@ -1282,7 +1299,7 @@ ofctrl_flood_remove_flows(struct ovn_desired_flow_table *flow_table, static void ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t priority, uint64_t cookie, const struct match *match, - const struct ofpbuf *actions) + const struct ofpbuf *actions, uint32_t meter_id) { f->table_id = table_id; f->priority = priority; @@ -1291,11 +1308,13 @@ ovn_flow_init(struct ovn_flow *f, uint8_t table_id, uint16_t priority, f->ofpacts_len = actions->size; f->hash = ovn_flow_match_hash(f); f->cookie = cookie; + f->ctrl_meter_id = meter_id; } static struct desired_flow * desired_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie, - const struct match *match, const struct ofpbuf *actions) + const struct match *match, const struct ofpbuf *actions, + uint32_t meter_id) { struct desired_flow *f = xmalloc(sizeof *f); ovs_list_init(&f->references); @@ -1304,7 +1323,8 @@ desired_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie, ovs_list_init(&f->track_list_node); f->installed_flow = NULL; f->is_deleted = false; - ovn_flow_init(&f->flow, table_id, priority, cookie, match, actions); + ovn_flow_init(&f->flow, table_id, priority, cookie, match, actions, + meter_id); return f; } @@ -1330,6 +1350,7 @@ installed_flow_dup(struct desired_flow *src) dst->flow.ofpacts_len = src->flow.ofpacts_len; dst->flow.hash = src->flow.hash; dst->flow.cookie = src->flow.cookie; + dst->flow.ctrl_meter_id = src->flow.ctrl_meter_id; return dst; } @@ -1356,6 +1377,7 @@ desired_flow_lookup__(struct ovn_desired_flow_table *flow_table, struct ovn_flow *f = &d->flow; if (f->table_id == target->table_id && f->priority == target->priority + && f->ctrl_meter_id == target->ctrl_meter_id && minimatch_equal(&f->match, &target->match)) { if (!match_cb || match_cb(d, arg)) { diff --git a/controller/ofctrl.h b/controller/ofctrl.h index ead8088c5..526525ffd 100644 --- a/controller/ofctrl.h +++ b/controller/ofctrl.h @@ -80,7 +80,15 @@ void ofctrl_add_or_append_flow(struct ovn_desired_flow_table *desired_flows, uint8_t table_id, uint16_t priority, uint64_t cookie, const struct match *match, const struct ofpbuf *actions, - const struct uuid *sb_uuid); + const struct uuid *sb_uuid, + uint32_t meter_id); + +void ofctrl_add_flow_metered(struct ovn_desired_flow_table *desired_flows, + uint8_t table_id, uint16_t priority, + uint64_t cookie, const struct match *match, + const struct ofpbuf *actions, + const struct uuid *sb_uuid, + uint32_t meter_id); /* Removes a bundles of flows from the flow table for a specific sb_uuid. The * flows are removed only if they are not referenced by any other sb_uuid(s). @@ -110,11 +118,12 @@ void ovn_desired_flow_table_init(struct ovn_desired_flow_table *); void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *); void ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *); -void ofctrl_check_and_add_flow(struct ovn_desired_flow_table *, - uint8_t table_id, uint16_t priority, - uint64_t cookie, const struct match *, - const struct ofpbuf *ofpacts, - const struct uuid *, bool log_duplicate_flow); +void ofctrl_check_and_add_flow_metered(struct ovn_desired_flow_table *, + uint8_t table_id, uint16_t priority, + uint64_t cookie, const struct match *, + const struct ofpbuf *ofpacts, + const struct uuid *, uint32_t meter_id, + bool log_duplicate_flow); bool ofctrl_is_connected(void); diff --git a/controller/physical.c b/controller/physical.c index 17ca5afbb..5fccc32ed 100644 --- a/controller/physical.c +++ b/controller/physical.c @@ -847,8 +847,8 @@ put_local_common_flows(uint32_t dp_key, * If a parent port has multiple child ports, then this if condition * will be hit multiple times, but we want to add only one flow. * ofctrl_add_flow() logs a warning message for duplicate flows. - * So use the function 'ofctrl_check_and_add_flow' which doesn't - * log a warning. + * So use the function 'ofctrl_check_and_add_flow_metered' which + * doesn't log a warning. * * Other option is to add this flow for all the ports which are not * nested containers. In which case we will add this flow for all the @@ -867,8 +867,9 @@ put_local_common_flows(uint32_t dp_key, put_load(ofp_to_u16(OFPP_NONE), MFF_IN_PORT, 0, 16, ofpacts_p); put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p); put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p)); - ofctrl_check_and_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0, - &match, ofpacts_p, hc_uuid, false); + ofctrl_check_and_add_flow_metered(flow_table, OFTABLE_SAVE_INPORT, 100, + 0, &match, ofpacts_p, hc_uuid, + NX_CTLR_NO_METER, false); } } diff --git a/include/ovn/actions.h b/include/ovn/actions.h index f5eb01eb7..a33d02681 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -796,6 +796,8 @@ struct ovnact_encode_params { * 'get_fdb' to resubmit. */ uint8_t fdb_lookup_ptable; /* OpenFlow table for * 'lookup_fdb' to resubmit. */ + uint32_t ctrl_meter_id; /* Meter to be used if the resulting flow + sends packets to controller. */ }; void ovnacts_encode(const struct ovnact[], size_t ovnacts_len, diff --git a/lib/actions.c b/lib/actions.c index 7010fab2b..2355a9ace 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -105,10 +105,10 @@ encode_finish_controller_op(size_t ofs, struct ofpbuf *ofpacts) } static void -encode_controller_op(enum action_opcode opcode, struct ofpbuf *ofpacts) +encode_controller_op(enum action_opcode opcode, uint32_t meter_id, + struct ofpbuf *ofpacts) { - size_t ofs = encode_start_controller_op(opcode, false, NX_CTLR_NO_METER, - ofpacts); + size_t ofs = encode_start_controller_op(opcode, false, meter_id, ofpacts); encode_finish_controller_op(ofs, ofpacts); } @@ -1678,7 +1678,7 @@ encode_nested_actions(const struct ovnact_nest *on, * packet to ARP or NA and then send the packet and actions back to the * switch inside an OFPT_PACKET_OUT message. */ size_t oc_offset = encode_start_controller_op(opcode, false, - NX_CTLR_NO_METER, ofpacts); + ep->ctrl_meter_id, ofpacts); ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size, ofpacts, OFP15_VERSION); encode_finish_controller_op(oc_offset, ofpacts); @@ -1729,10 +1729,10 @@ encode_ICMP6_ERROR(const struct ovnact_nest *on, static void encode_IGMP(const struct ovnact_null *a OVS_UNUSED, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_controller_op(ACTION_OPCODE_IGMP, ofpacts); + encode_controller_op(ACTION_OPCODE_IGMP, ep->ctrl_meter_id, ofpacts); } static void @@ -1974,6 +1974,7 @@ format_PUT_ND(const struct ovnact_put_mac_bind *put_mac, struct ds *s) static void encode_put_mac(const struct ovnact_put_mac_bind *put_mac, + const struct ovnact_encode_params *ep, enum mf_field_id ip_field, enum action_opcode opcode, struct ofpbuf *ofpacts) { @@ -1983,24 +1984,24 @@ encode_put_mac(const struct ovnact_put_mac_bind *put_mac, { expr_resolve_field(&put_mac->mac), MFF_ETH_SRC } }; encode_setup_args(args, ARRAY_SIZE(args), ofpacts); - encode_controller_op(opcode, ofpacts); + encode_controller_op(opcode, ep->ctrl_meter_id, ofpacts); encode_restore_args(args, ARRAY_SIZE(args), ofpacts); } static void encode_PUT_ARP(const struct ovnact_put_mac_bind *put_mac, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_put_mac(put_mac, MFF_REG0, ACTION_OPCODE_PUT_ARP, ofpacts); + encode_put_mac(put_mac, ep, MFF_REG0, ACTION_OPCODE_PUT_ARP, ofpacts); } static void encode_PUT_ND(const struct ovnact_put_mac_bind *put_mac, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_put_mac(put_mac, MFF_XXREG0, ACTION_OPCODE_PUT_ND, ofpacts); + encode_put_mac(put_mac, ep, MFF_XXREG0, ACTION_OPCODE_PUT_ND, ofpacts); } static void @@ -2701,13 +2702,13 @@ encode_put_dhcpv6_option(const struct ovnact_gen_option *o, static void encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { struct mf_subfield dst = expr_resolve_field(&pdo->dst); size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_PUT_DHCP_OPTS, - true, NX_CTLR_NO_METER, + true, ep->ctrl_meter_id, ofpacts); nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false); ovs_be32 ofs = htonl(dst.ofs); @@ -2754,13 +2755,13 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo, static void encode_PUT_DHCPV6_OPTS(const struct ovnact_put_opts *pdo, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { struct mf_subfield dst = expr_resolve_field(&pdo->dst); size_t oc_offset = encode_start_controller_op( - ACTION_OPCODE_PUT_DHCPV6_OPTS, true, NX_CTLR_NO_METER, ofpacts); + ACTION_OPCODE_PUT_DHCPV6_OPTS, true, ep->ctrl_meter_id, ofpacts); nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false); ovs_be32 ofs = htonl(dst.ofs); ofpbuf_put(ofpacts, &ofs, sizeof ofs); @@ -2787,10 +2788,11 @@ format_DHCP6_REPLY(const struct ovnact_null *a OVS_UNUSED, struct ds *s) static void encode_DHCP6_REPLY(const struct ovnact_null *a OVS_UNUSED, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_controller_op(ACTION_OPCODE_DHCP6_SERVER, ofpacts); + encode_controller_op(ACTION_OPCODE_DHCP6_SERVER, ep->ctrl_meter_id, + ofpacts); } static void @@ -2801,10 +2803,11 @@ format_BFD_MSG(const struct ovnact_null *a OVS_UNUSED, struct ds *s) static void encode_BFD_MSG(const struct ovnact_null *a OVS_UNUSED, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - encode_controller_op(ACTION_OPCODE_BFD_MSG, ofpacts); + encode_controller_op(ACTION_OPCODE_BFD_MSG, ep->ctrl_meter_id, + ofpacts); } static void @@ -2899,13 +2902,13 @@ format_DNS_LOOKUP(const struct ovnact_result *dl, struct ds *s) static void encode_DNS_LOOKUP(const struct ovnact_result *dl, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { struct mf_subfield dst = expr_resolve_field(&dl->dst); size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_DNS_LOOKUP, - true, NX_CTLR_NO_METER, + true, ep->ctrl_meter_id, ofpacts); nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false); ovs_be32 ofs = htonl(dst.ofs); @@ -3083,13 +3086,13 @@ encode_put_nd_ra_option(const struct ovnact_gen_option *o, static void encode_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { struct mf_subfield dst = expr_resolve_field(&po->dst); size_t oc_offset = encode_start_controller_op( - ACTION_OPCODE_PUT_ND_RA_OPTS, true, NX_CTLR_NO_METER, ofpacts); + ACTION_OPCODE_PUT_ND_RA_OPTS, true, ep->ctrl_meter_id, ofpacts); nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false); ovs_be32 ofs = htonl(dst.ofs); ofpbuf_put(ofpacts, &ofs, sizeof ofs); @@ -3372,7 +3375,7 @@ format_OVNFIELD_LOAD(const struct ovnact_load *load , struct ds *s) static void encode_OVNFIELD_LOAD(const struct ovnact_load *load, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name); @@ -3380,7 +3383,7 @@ encode_OVNFIELD_LOAD(const struct ovnact_load *load, case OVN_ICMP4_FRAG_MTU: { size_t oc_offset = encode_start_controller_op( ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, - NX_CTLR_NO_METER, ofpacts); + ep->ctrl_meter_id, ofpacts); ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16)); encode_finish_controller_op(oc_offset, ofpacts); break; @@ -3388,7 +3391,7 @@ encode_OVNFIELD_LOAD(const struct ovnact_load *load, case OVN_ICMP6_FRAG_MTU: { size_t oc_offset = encode_start_controller_op( ACTION_OPCODE_PUT_ICMP6_FRAG_MTU, true, - NX_CTLR_NO_METER, ofpacts); + ep->ctrl_meter_id, ofpacts); ofpbuf_put(ofpacts, &load->imm.value.be32_int, sizeof(ovs_be32)); encode_finish_controller_op(oc_offset, ofpacts); break; @@ -3492,7 +3495,7 @@ encode_BIND_VPORT(const struct ovnact_bind_vport *vp, }; encode_setup_args(args, ARRAY_SIZE(args), ofpacts); size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_BIND_VPORT, - false, NX_CTLR_NO_METER, + false, ep->ctrl_meter_id, ofpacts); ovs_be32 vp_key = htonl(vport_key); ofpbuf_put(ofpacts, &vp_key, sizeof(ovs_be32)); @@ -3530,14 +3533,15 @@ format_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk, static void encode_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { const struct arg args[] = { { expr_resolve_field(&svc_chk->port), MFF_LOG_INPORT }, }; encode_setup_args(args, ARRAY_SIZE(args), ofpacts); - encode_controller_op(ACTION_OPCODE_HANDLE_SVC_CHECK, ofpacts); + encode_controller_op(ACTION_OPCODE_HANDLE_SVC_CHECK, ep->ctrl_meter_id, + ofpacts); encode_restore_args(args, ARRAY_SIZE(args), ofpacts); } @@ -3786,7 +3790,7 @@ format_PUT_FDB(const struct ovnact_put_fdb *put_fdb, struct ds *s) static void encode_PUT_FDB(const struct ovnact_put_fdb *put_fdb, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { const struct arg args[] = { @@ -3794,7 +3798,7 @@ encode_PUT_FDB(const struct ovnact_put_fdb *put_fdb, { expr_resolve_field(&put_fdb->mac), MFF_ETH_SRC } }; encode_setup_args(args, ARRAY_SIZE(args), ofpacts); - encode_controller_op(ACTION_OPCODE_PUT_FDB, ofpacts); + encode_controller_op(ACTION_OPCODE_PUT_FDB, ep->ctrl_meter_id, ofpacts); encode_restore_args(args, ARRAY_SIZE(args), ofpacts); } diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl index e27c944a0..4a138977a 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -1672,6 +1672,7 @@ for (f in AggregatedFlow()) { .pipeline = pipeline, .table_id = f.stage.table_id, .priority = f.priority, + .controller_meter = None, .__match = f.__match, .actions = f.actions, .external_ids = external_ids) @@ -1684,6 +1685,7 @@ for (f in AggregatedFlow()) { .pipeline = pipeline, .table_id = f.stage.table_id, .priority = f.priority, + .controller_meter = None, .__match = f.__match, .actions = f.actions, .external_ids = external_ids); diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema index bbf60781d..fe7f858f7 100644 --- a/ovn-sb.ovsschema +++ b/ovn-sb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Southbound", "version": "20.18.0", - "cksum": "1816525029 26536", + "cksum": "60276371 26668", "tables": { "SB_Global": { "columns": { @@ -109,6 +109,8 @@ "maxInteger": 65535}}}, "match": {"type": "string"}, "actions": {"type": "string"}, + "controller_meter": {"type": {"key": {"type": "string"}, + "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, diff --git a/ovn-sb.xml b/ovn-sb.xml index 69de4551b..2d6528c98 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2441,6 +2441,12 @@ tcp.flags = RST; + + The name of the meter in table to be used for + all packets that the logical flow might send to + ovn-controller. + + Human-readable name for this flow's stage in the pipeline. From patchwork Thu Jul 8 16:40:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1502516 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=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=L3/b03fQ; dkim-atps=neutral Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GLMVR2X0vz9sWc for ; Fri, 9 Jul 2021 02:40:47 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id B222842257; Thu, 8 Jul 2021 16:40:44 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id DE-Zwbh1kL6K; Thu, 8 Jul 2021 16:40:41 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp4.osuosl.org (Postfix) with ESMTPS id 750C442237; Thu, 8 Jul 2021 16:40:40 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 54E17C001A; Thu, 8 Jul 2021 16:40:40 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 21C57C001A for ; Thu, 8 Jul 2021 16:40:39 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 28E4783C5C for ; Thu, 8 Jul 2021 16:40:33 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp1.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id kuGNNwyOhvTp for ; Thu, 8 Jul 2021 16:40:31 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id E2F6583C5F for ; Thu, 8 Jul 2021 16:40:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1625762430; 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=7EcG30Tu5LBUIAF8cPTCPKPTf9UC/V4rERk4sKjhSqk=; b=L3/b03fQ1PSz/2je60noxSwGQ/fMG00VyNkXJD1hclML5XxJ1h325FweOpm2a53ZacYawm zbJWGsinzdYgvuMynOzI9xP+hS+zHkr0H//DZ9Hn0Mc8Z+Jl4PboHYYJXhaeO1pLBGZRo4 Gtad0h3juE8740K5z73M1HEbUCJI8Xk= Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-600-D3nSKUGdN_i4506agKYBtA-1; Thu, 08 Jul 2021 12:40:25 -0400 X-MC-Unique: D3nSKUGdN_i4506agKYBtA-1 Received: by mail-ej1-f70.google.com with SMTP id t8-20020a1709063e48b0290501cd965554so480365eji.8 for ; Thu, 08 Jul 2021 09:40:24 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7EcG30Tu5LBUIAF8cPTCPKPTf9UC/V4rERk4sKjhSqk=; b=iiXszQ3ZfTMfH4JyX7N2bJCmjj9oz299iHb3UNo10dpS7+fpMhqEQ9JPJUHw06Jl70 b/lchSkD/OqpMH1tbjJ6/nps+FUjtYZ1n/d8cPAQAy4fmNyS50eED/q6sdyoz9ign9QF dwEVAsFuZrGEkQeHRy3G8dhZnkfAXu5gdXtQ7csrprbjx/CSROT/zwoorX2LH9GTYWGk 6rsXuZCDCeY+9ggwwRDuKQ4ftf388kPPlWCUMYApyFzLZiCRioYoXUzRWW9obYCSWN63 QCJ0DQl+FLOjA1D59UZGH+Uh2NrW6jl1miD2t4pPvuDHk8LDaQOfzoir9ibRUNPAH+V9 C7zw== X-Gm-Message-State: AOAM531jGc4+mfzzSkEMg5xsG9MxZN4dPfWhoDOQoYYy42vhWHby/Xob FynDLxIQpvrQ1BWkhAbmTkjN1VkFhHgeWTecTUwqjOdf6pAVvRe43AHA6Nlb4E1i/juH64MAAYX NRY3qO6HdqOiEuymThqCz8kca1txm390du4wNS+Owz9eS99Vol3AbzZSzHC7p9Q+5s1KsFJ11uS I= X-Received: by 2002:a17:906:d1d1:: with SMTP id bs17mr31764953ejb.492.1625762423524; Thu, 08 Jul 2021 09:40:23 -0700 (PDT) X-Google-Smtp-Source: ABdhPJz0auJTPZsaLeCkaQUCtMjDnmZjwJSTQuOekb8cA6JEajTzrJ9oRrArGavMd8D2pCZmczmK6w== X-Received: by 2002:a17:906:d1d1:: with SMTP id bs17mr31764921ejb.492.1625762423101; Thu, 08 Jul 2021 09:40:23 -0700 (PDT) Received: from lore-desk.redhat.com (net-93-71-3-244.cust.vodafonedsl.it. [93.71.3.244]) by smtp.gmail.com with ESMTPSA id ee25sm1568103edb.6.2021.07.08.09.40.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jul 2021 09:40:22 -0700 (PDT) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Thu, 8 Jul 2021 18:40:03 +0200 Message-Id: <65f2c074f66a6fba0745940b555dc2b40787a36c.1625762235.git.lorenzo.bianconi@redhat.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=lorenzo.bianconi@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Cc: dceara@redhat.com Subject: [ovs-dev] [PATCH v6 ovn 2/4] ovn-northd: Add support for CoPP. 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" From: Dumitru Ceara 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_Router 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). Acked-by: Mark D. Gray Co-authored-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Dumitru Ceara --- lib/automake.mk | 2 + lib/copp.c | 142 ++++++++++++++++++++++++++++++ lib/copp.h | 58 +++++++++++++ northd/ovn-northd.c | 55 ++++++++---- ovn-nb.ovsschema | 16 +++- ovn-nb.xml | 78 +++++++++++++++++ tests/ovn-controller.at | 52 +++++++++++ tests/ovn-northd.at | 96 ++++++++++++++++++++ utilities/ovn-nbctl.8.xml | 72 +++++++++++++++ utilities/ovn-nbctl.c | 178 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 733 insertions(+), 16 deletions(-) create mode 100644 lib/copp.c create mode 100644 lib/copp.h diff --git a/lib/automake.mk b/lib/automake.mk index 917b28e1e..ac0fde8a3 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 000000000..e3d14938a --- /dev/null +++ b/lib/copp.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2021, 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 "db-ctl-base.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", + [COPP_BFD] = "bfd", +}; + +static const char * +copp_proto_get_name(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; +} + +void +copp_meter_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); + } +} + +const struct nbrec_copp * +copp_meter_add(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; +} + +void +copp_meter_del(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); + } +} + +char * +copp_proto_validate(const char *proto_name) +{ + for (size_t i = COPP_PROTO_FIRST; i < COPP_PROTO_MAX; i++) { + if (!strcmp(proto_name, copp_proto_get_name(i))) { + 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++) { + ds_put_format(&usage, "%s, ", copp_proto_get_name(i)); + } + ds_chomp(&usage, ' '); + ds_chomp(&usage, ','); + ds_put_cstr(&usage, "."); + + return ds_steal_cstr(&usage); +} diff --git a/lib/copp.h b/lib/copp.h new file mode 100644 index 000000000..c34e1e029 --- /dev/null +++ b/lib/copp.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2021, 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_BFD, + COPP_PROTO_MAX, + COPP_PROTO_INVALID = COPP_PROTO_MAX, +}; + +struct nbrec_copp; +struct ctl_context; + +const char *copp_meter_get(enum copp_proto proto, + const struct nbrec_copp *copp, + const struct shash *meter_groups); + +void copp_meter_list(struct ctl_context *ctx, const struct nbrec_copp *copp); +const struct nbrec_copp * +copp_meter_add(struct ctl_context *ctx, const struct nbrec_copp *copp, + const char *proto_name, const char *meter); +void +copp_meter_del(const struct nbrec_copp *copp, const char *proto_name); +char * copp_proto_validate(const char *proto_name); + +#endif /* lib/copp.h */ diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 570c6a3ef..9845d634c 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -32,6 +32,7 @@ #include "ovn/lex.h" #include "lib/chassis-index.h" #include "lib/ip-mcast-index.h" +#include "lib/copp.h" #include "lib/mcast-group-index.h" #include "lib/ovn-l7.h" #include "lib/ovn-nb-idl.h" @@ -4102,6 +4103,7 @@ struct ovn_lflow { char *match; char *actions; char *stage_hint; + char *ctrl_meter; const char *where; }; @@ -4110,7 +4112,8 @@ static struct ovn_lflow *ovn_lflow_find(const struct hmap *lflows, const struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, const char *match, - const char *actions, uint32_t hash); + const char *actions, + const char *ctrl_meter, uint32_t hash); static char * ovn_lflow_hint(const struct ovsdb_idl_row *row) @@ -4124,20 +4127,21 @@ ovn_lflow_hint(const struct ovsdb_idl_row *row) static bool ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, const char *match, - const char *actions) + const char *actions, const char *ctrl_meter) { return (a->od == od && a->stage == stage && a->priority == priority && !strcmp(a->match, match) - && !strcmp(a->actions, actions)); + && !strcmp(a->actions, actions) + && nullable_string_is_equal(a->ctrl_meter, 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) { hmapx_init(&lflow->od_group); lflow->od = od; @@ -4146,6 +4150,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; } @@ -4164,7 +4169,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od, uint32_t hash, enum ovn_stage stage, uint16_t priority, const char *match, const char *actions, const struct ovsdb_idl_row *stage_hint, - const char *where) + const char *where, const char *ctrl_meter) { struct ovn_lflow *old_lflow; @@ -4172,7 +4177,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od, if (use_logical_dp_groups) { old_lflow = ovn_lflow_find(lflow_map, NULL, stage, priority, match, - actions, hash); + actions, ctrl_meter, hash); if (old_lflow) { hmapx_add(&old_lflow->od_group, od); return; @@ -4185,6 +4190,7 @@ do_ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od, * one datapath in a group, so it could be hashed correctly. */ ovn_lflow_init(lflow, NULL, stage, priority, xstrdup(match), xstrdup(actions), + nullable_xstrdup(ctrl_meter), ovn_lflow_hint(stage_hint), where); hmapx_add(&lflow->od_group, od); hmap_insert_fast(lflow_map, &lflow->hmap_node, hash); @@ -4195,6 +4201,7 @@ 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 *ctrl_meter, const struct ovsdb_idl_row *stage_hint, const char *where) { ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od)); @@ -4209,32 +4216,45 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od, if (use_logical_dp_groups && use_parallel_build) { lock_hash_row(&lflow_locks, hash); do_ovn_lflow_add(lflow_map, od, hash, stage, priority, match, - actions, stage_hint, where); + actions, stage_hint, where, ctrl_meter); unlock_hash_row(&lflow_locks, hash); } else { do_ovn_lflow_add(lflow_map, od, hash, stage, priority, match, - actions, stage_hint, where); + actions, stage_hint, where, ctrl_meter); } } /* 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, CTRL_METER, STAGE_HINT) \ + ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ + CTRL_METER, STAGE_HINT, OVS_SOURCE_LOCATOR) + #define ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ ACTIONS, STAGE_HINT) \ - ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ - STAGE_HINT, OVS_SOURCE_LOCATOR) + ovn_lflow_add_with_hint__(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ + ACTIONS, NULL, STAGE_HINT) #define ovn_lflow_add(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \ ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ - NULL, OVS_SOURCE_LOCATOR) + NULL, NULL, OVS_SOURCE_LOCATOR) + +#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(const struct hmap *lflows, const 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 *lflow; HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) { - if (ovn_lflow_equal(lflow, od, stage, priority, match, actions)) { + if (ovn_lflow_equal(lflow, od, stage, priority, match, actions, + ctrl_meter)) { return lflow; } } @@ -4252,6 +4272,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); } } @@ -12557,7 +12578,8 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths, lflow = ovn_lflow_find( &lflows, logical_datapath_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) { /* This is a valid lflow. Checking if the datapath group needs * updates. */ @@ -12602,6 +12624,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 @@ -14351,6 +14374,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_logical_dp_group); diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index faf619a1c..a53d5e851 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", "version": "5.32.0", - "cksum": "204590300 28863", + "cksum": "2501921026 29540", "tables": { "NB_Global": { "columns": { @@ -30,6 +30,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"}, @@ -58,6 +66,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"}}, @@ -322,6 +333,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", diff --git a/ovn-nb.xml b/ovn-nb.xml index 36a77097c..1e285851d 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -354,6 +354,68 @@ + +

+ 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. + + + Rate limiting meter for BFD packets. + +
+

Each row represents one L2 logical switch. @@ -587,6 +649,14 @@ + +

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

+ + @@ -1982,6 +2052,14 @@ + +

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

+
+

Additional options for the logical router. diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at index 42b10097e..ca210ba61 100644 --- a/tests/ovn-controller.at +++ b/tests/ovn-controller.at @@ -601,3 +601,55 @@ cat hv1/ovn-controller.log OVN_CLEANUP([hv1]) AT_CLEANUP + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn-controller - ovn action metering]) +AT_KEYWORDS([action-metering]) + +dnl This test is not suported by ovn-northd-ddlog yet. +AT_SKIP_IF([test NORTHD_TYPE = ovn-northd-ddlog && test "$RUN_ANYWAY" != yes]) + +ovn_start + +net_add n1 +sim_add hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 + +check ovn-nbctl lr-add lr1 \ + -- lrp-add lr1 rp-ls1 00:00:01:01:02:03 192.168.1.254/24 +check ovn-nbctl ls-add ls1 \ + -- lsp-add ls1 lsp1 \ + -- lsp-set-addresses lsp1 "00:00:00:00:00:01 192.168.1.1" \ + -- lsp-add ls1 ls1-rp \ + -- set Logical_Switch_Port ls1-rp type=router options:router-port=rp-ls1 \ + -- lsp-set-addresses ls1-rp router + +as hv1 +check ovs-vsctl \ + -- add-port br-int vif1 \ + -- set Interface vif1 external_ids:iface-id=lsp1 + +check ovn-nbctl --event lb-add lb1 192.168.1.100:80 "" +check ovn-nbctl ls-lb-add ls1 lb1 + +# controller-event metering +check ovn-nbctl meter-add event-elb drop 100 pktps 10 +check ovn-nbctl ls-copp-add ls1 event-elb event-elb + +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep userdata=00.00.00.0f | grep -q meter_id=1]) + +# reject metering +check ovn-nbctl meter-add acl-meter drop 1 pktps 0 +check ovn-nbctl ls-copp-add ls1 reject acl-meter +check ovn-nbctl acl-add ls1 from-lport 1002 'inport == "lsp1" && ip && udp' reject +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep userdata=00.00.00.16 | grep -q meter_id=2]) + +# arp metering +check ovn-nbctl meter-add arp-meter drop 200 pktps 0 +check ovn-nbctl lr-copp-add lr1 arp-resolve arp-meter +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep controller | grep userdata=00.00.00.00 | grep -q meter_id=3]) + +OVN_CLEANUP([hv1]) +AT_CLEANUP +]) diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 02640d163..1c5402e19 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -3058,6 +3058,102 @@ wait_row_count bfd 3 AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn -- check CoPP config]) +AT_KEYWORDS([northd-CoPP]) + +dnl This test is not suported by ovn-northd-ddlog yet. +AT_SKIP_IF([test NORTHD_TYPE = ovn-northd-ddlog && test "$RUN_ANYWAY" != yes]) + +ovn_start + +check ovn-nbctl --wait=sb lr-add r0 +check ovn-nbctl --wait=sb lrp-add r0 r0-sw1 00:00:00:00:00:01 192.168.1.1/24 +check ovn-nbctl --wait=sb ls-add sw1 +check ovn-nbctl --wait=sb lsp-add sw1 sw1-r0 +check ovn-nbctl --wait=sb lsp-set-type sw1-r0 router +check ovn-nbctl --wait=sb lsp-set-options sw1-r0 router-port=r0-sw1 +check ovn-nbctl --wait=sb lsp-set-addresses sw1-r0 00:00:00:00:00:01 + +check ovn-nbctl --event lb-add lb0 192.168.1.100:80 "" +check ovn-nbctl ls-lb-add sw1 lb0 +check ovn-nbctl --wait=hv meter-add meter0 drop 100 pktps 10 +check ovn-nbctl --wait=hv ls-copp-add sw1 event-elb meter0 +AT_CHECK([ovn-nbctl ls-copp-list sw1], [0], [dnl +event-elb: meter0 +]) + +AT_CHECK([ovn-sbctl list logical_flow | grep trigger_event -A 2 | grep -q meter0]) + +check ovn-nbctl --wait=hv meter-add meter1 drop 200 pktps 10 +check ovn-nbctl --wait=hv lr-copp-add r0 arp meter1 +AT_CHECK([ovn-nbctl lr-copp-list r0], [0], [dnl +arp: meter1 +]) + +AT_CHECK([ovn-sbctl list logical_flow | grep arp -A 2 | grep -q meter1]) + +check ovn-nbctl --wait=hv lr-copp-del r0 arp +AT_CHECK([ovn-nbctl lr-copp-list r0], [0], [dnl +]) + +AT_CHECK([ovn-sbctl list logical_flow | grep arp -A 2 | grep -q meter1],[1]) + +check ovn-nbctl --wait=hv ls-copp-del sw1 event-elb +AT_CHECK([ovn-nbctl ls-copp-list sw1], [0], [dnl +]) + +AT_CHECK([ovn-sbctl list logical_flow | grep trigger_event -A 2 | grep -q meter0],[1]) + +# let's try to add an usupported protocol "dhcp" +AT_CHECK([ovn-nbctl --wait=hv ls-copp-add sw1 dhcp meter1],[1],[],[dnl +ovn-nbctl: Invalid control protocol. Allowed values: arp, arp-resolve, dhcpv4-opts, dhcpv6-opts, dns, event-elb, icmp4-error, icmp6-error, igmp, nd-na, nd-ns, nd-ns-resolve, nd-ra-opts, tcp-reset, bfd, reject. +]) +AT_CHECK([ovn-nbctl ls-copp-list sw1], [0], [dnl +]) + +#Let's try to add a valid protocol to an unknown datapath +AT_CHECK([ovn-nbctl --wait=hv ls-copp-add sw10 arp meter1],[1],[],[dnl +ovn-nbctl: sw10: switch name not found +]) +AT_CHECK([ovn-nbctl ls-copp-list sw1], [0], [dnl +]) + +check ovn-nbctl --bfd lr-route-add r0 240.0.0.0/8 192.168.50.2 r0-sw1 +check ovn-nbctl --wait=hv lr-copp-add r0 bfd meter0 +AT_CHECK([ovn-nbctl lr-copp-list r0], [0], [dnl +bfd: meter0 +]) +AT_CHECK([ovn-sbctl list logical_flow | grep bfd -A 2 | grep -q meter0]) + +check ovn-nbctl --wait=hv set Logical_Switch sw1 \ + other_config:mcast_querier="false" \ + other_config:mcast_snoop="true" +check ovn-nbctl --wait=hv ls-copp-add sw1 igmp meter1 +AT_CHECK([ovn-nbctl ls-copp-list sw1], [0], [dnl +igmp: meter1 +]) +AT_CHECK([ovn-sbctl list logical_flow | grep igmp -A 2 | grep -q meter1]) + +# let's add igmp meter1 twice +AT_CHECK([ovn-nbctl --wait=hv ls-copp-add sw1 igmp meter1]) +AT_CHECK([ovn-nbctl ls-copp-list sw1], [0], [dnl +igmp: meter1 +]) + +# let's delete a wrong meter +AT_CHECK([ovn-nbctl --wait=hv lr-copp-del r0 event-elb]) +AT_CHECK([ovn-nbctl lr-copp-list r0], [0], [dnl +bfd: meter0 +]) + +check ovn-nbctl lr-copp-del r0 +AT_CHECK([ovn-nbctl lr-copp-list r0], [0], [dnl +]) + +AT_CLEANUP +]) + AT_SETUP([check LSP attached to multiple LS]) ovn_start diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml index c3ff87e74..101849911 100644 --- a/utilities/ovn-nbctl.8.xml +++ b/utilities/ovn-nbctl.8.xml @@ -1446,6 +1446,78 @@ +

Control Plane Protection Policy commands

+

+ These commands manage meters configured in table + linking them to logical datapaths through copp column in + or tables. + Protocol packets for which CoPP is enforced when sending packets to + ovn-controller (if configured): +

    +
  • ARP
  • +
  • ND_NS
  • +
  • ND_NA
  • +
  • ND_RA
  • +
  • ND
  • +
  • DNS
  • +
  • IGMP
  • +
  • packets that require ARP resolution before forwarding
  • +
  • packets that require ND_NS before forwarding
  • +
  • packets that need to be replied to with ICMP Errors
  • +
  • packets that need to be replied to with TCP RST
  • +
  • packets that need to be replied to with DHCP_OPTS
  • +
  • BFD
  • +
+

+ +
+
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. +
+ +
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. +
+
+

Synchronization Commands

diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index dc13fa9ca..7d2ee0bea 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 "memory.h" @@ -425,6 +426,28 @@ chassis with optional PRIORITY to the HA chassis group GRP\n\ ha-chassis-group-remove-chassis GRP CHASSIS Removes 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\ + 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\ +\n\ %s\ %s\ \n\ @@ -6000,6 +6023,147 @@ nbctl_lr_route_list(struct ctl_context *ctx) free(ipv6_routes); } +static void +nbctl_pre_copp(struct ctl_context *ctx) +{ + nbctl_pre_context(ctx); + ovsdb_idl_add_column(ctx->idl, &nbrec_copp_col_meters); + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_switch_col_copp); + ovsdb_idl_add_column(ctx->idl, &nbrec_logical_router_col_copp); +} + +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); + 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_meter_add(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); + 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_meter_del(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_meter_list(ctx, ls->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); + 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_meter_add(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); + 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_meter_del(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_meter_list(ctx, lr->copp); +} + static void verify_connections(struct ctl_context *ctx) { @@ -6756,6 +6920,20 @@ static const struct ctl_command_syntax nbctl_commands[] = { nbctl_pre_dhcp_options_options, nbctl_dhcp_options_get_options, NULL, "", RO }, + /* Control plane protection commands */ + {"ls-copp-add", 3, 3, "SWITCH PROTO METER", nbctl_pre_copp, + nbctl_ls_copp_add, NULL, "", RW}, + {"ls-copp-del", 1, 2, "SWITCH [PROTO]", nbctl_pre_copp, + nbctl_ls_copp_del, NULL, "", RW}, + {"ls-copp-list", 1, 1, "SWITCH", nbctl_pre_copp, nbctl_ls_copp_list, + NULL, "", RO}, + {"lr-copp-add", 3, 3, "ROUTER PROTO METER", nbctl_pre_copp, + nbctl_lr_copp_add, NULL, "", RW}, + {"lr-copp-del", 1, 2, "ROUTER [PROTO]", nbctl_pre_copp, + nbctl_lr_copp_del, NULL, "", RW}, + {"lr-copp-list", 1, 1, "ROUTER", nbctl_pre_copp, nbctl_lr_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}, From patchwork Thu Jul 8 16:40:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1502517 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=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=BMq2eP3N; dkim-atps=neutral Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GLMVd6Btpz9sWc for ; Fri, 9 Jul 2021 02:40:57 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 08FF942276; Thu, 8 Jul 2021 16:40:55 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 281cMpmH9eQ8; Thu, 8 Jul 2021 16:40:48 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp4.osuosl.org (Postfix) with ESMTPS id C04E34224E; Thu, 8 Jul 2021 16:40:43 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 93C42C0025; Thu, 8 Jul 2021 16:40:42 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2153DC0025 for ; Thu, 8 Jul 2021 16:40:41 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 3CDB083C6D for ; Thu, 8 Jul 2021 16:40:34 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp1.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 71M6FhxMADus for ; Thu, 8 Jul 2021 16:40:32 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id F151C83C51 for ; Thu, 8 Jul 2021 16:40:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1625762430; 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=mTpRBeLmESjkKnBoXqMoUTQ9G8ZYuQduxD6EcQqDITs=; b=BMq2eP3NdpByvKPO/W2t/KAn7wpFNCC9vACJMlpw75zjApmRonwk9Bqdka2JYOcqLe5Bbv zezrz2Kv1GvLM/n6uFakfr8Sk9I3TYSKKUdkDpNprMWH8vD5gEtS3yW9IZ7mvQ/Mtq//4+ b91atawmX1lodK9tQvJ/8Jdv1MtWUZg= Received: from mail-ed1-f70.google.com (mail-ed1-f70.google.com [209.85.208.70]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-541-b9notVd0NvWcJmQZamWxVg-1; Thu, 08 Jul 2021 12:40:27 -0400 X-MC-Unique: b9notVd0NvWcJmQZamWxVg-1 Received: by mail-ed1-f70.google.com with SMTP id e3-20020a0564020883b029039ef9536577so2113233edy.5 for ; Thu, 08 Jul 2021 09:40:26 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mTpRBeLmESjkKnBoXqMoUTQ9G8ZYuQduxD6EcQqDITs=; b=r7pGclHLn4l1DaGXu7Ug8cltMAIIg9UptyWHGCVbrFkivA9m8bEiQE/qOimeAUhS5R X6BVQHM/4tWINjT8ionzK6YIlsR7vjnwcbnCKD4s9gchLsHUDympZhtApZyAGyijvT4j 2fyDgjZ3HWV07j/hbPsgwE3+RxmmV02RD0Hj/YedZ8A5KwCzpHPTionYv812xkIhOW6B FFejZKwTR1JUxto1N169TRkltzlbyrC6xuN4t8j1lTC+0yGOIgijftb+NPgnYLa46iF8 6ldvtzVvFhsCKCnZ6q6ynCiwS7l9fq1hylQd5hwycUiCq+ytogX2wtsktOw7RHZbbBFc 5u6A== X-Gm-Message-State: AOAM532q72+5qgGi+oO93ckAR8994KgEEVkJ4zu4apU+sDHC7Qk0QYL8 Ni1K+C+M3JJtHrWsYtLDpprSSakhK3UiGCibQz9ma2SkQ0BBmYDVd5LopQHjghKCkt1zllHDwVI U+PikUJIwg7g47ohqF9W7/VuukUMSJlL6pESpQKH8rUYWZ0gVYEhlHa0OyaIyFZ0FcTsht1Csqj E= X-Received: by 2002:a05:6402:d59:: with SMTP id ec25mr39501554edb.373.1625762425463; Thu, 08 Jul 2021 09:40:25 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyRstTn18vKfwTIaPqa/VuvqV69ToT2DbFY+v0FF4oUyxWiQxM3KltF553kA4jndlYoQIgTPA== X-Received: by 2002:a05:6402:d59:: with SMTP id ec25mr39501497edb.373.1625762424895; Thu, 08 Jul 2021 09:40:24 -0700 (PDT) Received: from lore-desk.redhat.com (net-93-71-3-244.cust.vodafonedsl.it. [93.71.3.244]) by smtp.gmail.com with ESMTPSA id ee25sm1568103edb.6.2021.07.08.09.40.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jul 2021 09:40:24 -0700 (PDT) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Thu, 8 Jul 2021 18:40:04 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=lorenzo.bianconi@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Cc: dceara@redhat.com Subject: [ovs-dev] [PATCH v6 ovn 3/4] ovn-northd: Add CoPP policies for flows that punt packets to ovn-controller. 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" From: Dumitru Ceara Change the ovn-northd implementation to set the new 'controller_meter' field for flows that need to punt packets to ovn-controller. Protocol packets for which CoPP is enforced when sending packets to ovn-controller (if configured): - ARP - ND_NS - ND_NA - ND_RA - DNS - IGMP - packets that require ARP resolution before forwarding - packets that require ND_NS before forwarding - packets that need to be replied to with ICMP Errors - packets that need to be replied to with TCP RST - packets that need to be replied to with DHCP_OPTS - packets that trigger a SCTP abort action - contoller_events - BFD Acked-by: Mark D. Gray Co-authored-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Dumitru Ceara --- include/ovn/actions.h | 1 - lib/actions.c | 50 +--- lib/copp.c | 1 + lib/copp.h | 1 + northd/ovn-northd.c | 516 ++++++++++++++++++++++++-------------- ovn-nb.xml | 3 + tests/atlocal.in | 3 + tests/ovn.at | 9 +- tests/system-ovn.at | 138 ++++++++++ utilities/ovn-nbctl.8.xml | 3 + 10 files changed, 489 insertions(+), 236 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index a33d02681..f023a37b9 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -394,7 +394,6 @@ struct ovnact_controller_event { int event_type; /* controller event type */ struct ovnact_gen_option *options; size_t n_options; - char *meter; }; /* OVNACT_BIND_VPORT. */ diff --git a/lib/actions.c b/lib/actions.c index 2355a9ace..c572e88ae 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -1644,9 +1644,6 @@ format_TRIGGER_EVENT(const struct ovnact_controller_event *event, { ds_put_format(s, "trigger_event(event = \"%s\"", event_to_string(event->event_type)); - if (event->meter) { - ds_put_format(s, ", meter = \"%s\"", event->meter); - } for (const struct ovnact_gen_option *o = event->options; o < &event->options[event->n_options]; o++) { ds_put_cstr(s, ", "); @@ -1821,24 +1818,11 @@ encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts, static void encode_TRIGGER_EVENT(const struct ovnact_controller_event *event, - const struct ovnact_encode_params *ep OVS_UNUSED, + const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) { - uint32_t meter_id = NX_CTLR_NO_METER; - size_t oc_offset; - - if (event->meter) { - meter_id = ovn_extend_table_assign_id(ep->meter_table, event->meter, - ep->lflow_uuid); - if (meter_id == EXT_TABLE_ID_INVALID) { - VLOG_WARN("Unable to assign id for trigger meter: %s", - event->meter); - return; - } - } - - oc_offset = encode_start_controller_op(ACTION_OPCODE_EVENT, false, - meter_id, ofpacts); + size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_EVENT, false, + ep->ctrl_meter_id, ofpacts); ovs_be32 ofs = htonl(event->event_type); ofpbuf_put(ofpacts, &ofs, sizeof ofs); @@ -2372,27 +2356,12 @@ parse_trigger_event(struct action_context *ctx, sizeof *event->options); } - if (lexer_match_id(ctx->lexer, "meter")) { - if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { - return; - } - /* If multiple meters are given, use the most recent. */ - if (ctx->lexer->token.type == LEX_T_STRING && - strlen(ctx->lexer->token.s)) { - free(event->meter); - event->meter = xstrdup(ctx->lexer->token.s); - } else if (ctx->lexer->token.type != LEX_T_STRING) { - lexer_syntax_error(ctx->lexer, "expecting string"); - return; - } - lexer_get(ctx->lexer); - } else { - struct ovnact_gen_option *o = &event->options[event->n_options++]; - memset(o, 0, sizeof *o); - parse_gen_opt(ctx, o, - &ctx->pp->controller_event_opts->event_opts[event_type], - event_to_string(event_type)); - } + struct ovnact_gen_option *o = &event->options[event->n_options++]; + memset(o, 0, sizeof *o); + parse_gen_opt(ctx, o, + &ctx->pp->controller_event_opts->event_opts[event_type], + event_to_string(event_type)); + if (ctx->lexer->error) { return; } @@ -2413,7 +2382,6 @@ static void ovnact_controller_event_free(struct ovnact_controller_event *event) { free_gen_options(event->options, event->n_options); - free(event->meter); } static void diff --git a/lib/copp.c b/lib/copp.c index e3d14938a..bbe66924b 100644 --- a/lib/copp.c +++ b/lib/copp.c @@ -37,6 +37,7 @@ static char *copp_proto_names[COPP_PROTO_MAX] = { [COPP_ND_NS_RESOLVE] = "nd-ns-resolve", [COPP_ND_RA_OPTS] = "nd-ra-opts", [COPP_TCP_RESET] = "tcp-reset", + [COPP_REJECT] = "reject", [COPP_BFD] = "bfd", }; diff --git a/lib/copp.h b/lib/copp.h index c34e1e029..e238d963a 100644 --- a/lib/copp.h +++ b/lib/copp.h @@ -36,6 +36,7 @@ enum copp_proto { COPP_ND_RA_OPTS, COPP_TCP_RESET, COPP_BFD, + COPP_REJECT, COPP_PROTO_MAX, COPP_PROTO_INVALID = COPP_PROTO_MAX, }; diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 9845d634c..83a73850f 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -3354,11 +3354,11 @@ ovn_lb_svc_create(struct northd_context *ctx, struct ovn_northd_lb *lb, } } -static -void build_lb_vip_actions(struct ovn_lb_vip *lb_vip, - struct ovn_northd_lb_vip *lb_vip_nb, - struct ds *action, char *selection_fields, - bool ls_dp) +static bool +build_lb_vip_actions(struct ovn_lb_vip *lb_vip, + struct ovn_northd_lb_vip *lb_vip_nb, + struct ds *action, char *selection_fields, + bool ls_dp) { bool skip_hash_fields = false, reject = false; @@ -3410,6 +3410,7 @@ void build_lb_vip_actions(struct ovn_lb_vip *lb_vip, ds_chomp(action, ')'); ds_put_format(action, "; hash_fields=\"%s\");", selection_fields); } + return reject; } static void @@ -4239,11 +4240,15 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od, ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ NULL, NULL, OVS_SOURCE_LOCATOR) -#define ovn_lflow_add_ctrl(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ - CTRL_METER) \ +#define ovn_lflow_metered(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ + CTRL_METER) \ ovn_lflow_add_with_hint__(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ ACTIONS, CTRL_METER, NULL) +#define ovn_lflow_add_unique(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ + ACTIONS, CTRL_METER) \ + ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ + CTRL_METER, NULL, OVS_SOURCE_LOCATOR) static struct ovn_lflow * ovn_lflow_find(const struct hmap *lflows, const struct ovn_datapath *od, @@ -5188,7 +5193,6 @@ ls_has_dns_records(const struct nbrec_logical_switch *nbs) static bool build_empty_lb_event_flow(struct ovn_lb_vip *lb_vip, const struct nbrec_load_balancer *lb, - struct shash *meter_groups, struct ds *match, struct ds *action) { bool controller_event = smap_get_bool(&lb->options, "event", false) || @@ -5202,11 +5206,6 @@ build_empty_lb_event_flow(struct ovn_lb_vip *lb_vip, ds_clear(match); bool ipv4 = IN6_IS_ADDR_V4MAPPED(&lb_vip->vip); - char *meter = ""; - - if (meter_groups && shash_find(meter_groups, "event-elb")) { - meter = "event-elb"; - } ds_put_format(match, "ip%s.dst == %s && %s", ipv4 ? "4": "6", lb_vip->vip_str, lb->protocol); @@ -5221,11 +5220,11 @@ build_empty_lb_event_flow(struct ovn_lb_vip *lb_vip, ds_put_format(action, "trigger_event(event = \"%s\", " - "meter = \"%s\", vip = \"%s\", " + "vip = \"%s\", " "protocol = \"%s\", " "load_balancer = \"" UUID_FMT "\");", event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS), - meter, vip, lb->protocol, + vip, lb->protocol, UUID_ARGS(&lb->header_.uuid)); if (lb_vip->vip_port) { free(vip); @@ -5581,9 +5580,12 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows, "reject { " "/* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ " "outport <-> inport; %s };", next_action); - ovn_lflow_add_with_hint(lflows, od, stage, - acl->priority + OVN_ACL_PRI_OFFSET, - ds_cstr(&match), ds_cstr(&actions), stage_hint); + ovn_lflow_add_with_hint__(lflows, od, stage, + acl->priority + OVN_ACL_PRI_OFFSET, + ds_cstr(&match), ds_cstr(&actions), + copp_meter_get(COPP_REJECT, od->nbs->copp, + meter_groups), + stage_hint); free(next_action); ds_destroy(&match); @@ -6079,7 +6081,8 @@ build_qos(struct ovn_datapath *od, struct hmap *lflows) { static void build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, - struct ds *match, struct ds *action) + struct ds *match, struct ds *action, + struct shash *meter_groups) { for (size_t i = 0; i < lb->n_vips; i++) { struct ovn_lb_vip *lb_vip = &lb->vips[i]; @@ -6121,8 +6124,9 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, } /* New connections in Ingress table. */ - build_lb_vip_actions(lb_vip, lb_vip_nb, action, - lb->selection_fields, true); + const char *meter = NULL; + bool reject = build_lb_vip_actions(lb_vip, lb_vip_nb, action, + lb->selection_fields, true); ds_put_format(match, "ct.new && %s.dst == %s", ip_match, lb_vip->vip_str); @@ -6132,9 +6136,16 @@ build_lb_rules(struct hmap *lflows, struct ovn_northd_lb *lb, priority = 120; } for (size_t j = 0; j < lb->n_nb_ls; j++) { - ovn_lflow_add_with_hint(lflows, lb->nb_ls[j], S_SWITCH_IN_STATEFUL, - priority, ds_cstr(match), - ds_cstr(action), &lb->nlb->header_); + struct ovn_datapath *od = lb->nb_ls[j]; + + if (reject) { + meter = copp_meter_get(COPP_REJECT, od->nbs->copp, + meter_groups); + } + ovn_lflow_add_with_hint__(lflows, od, S_SWITCH_IN_STATEFUL, + priority, ds_cstr(match), + ds_cstr(action), meter, + &lb->nlb->header_); } } } @@ -6679,6 +6690,7 @@ static void build_dhcpv4_options_flows(struct ovn_port *op, struct lport_addresses *lsp_addrs, const char *json_key, bool is_external, + struct shash *meter_groups, struct hmap *lflows) { struct ds match = DS_EMPTY_INITIALIZER; @@ -6702,11 +6714,14 @@ build_dhcpv4_options_flows(struct ovn_port *op, op->json_key); } - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_DHCP_OPTIONS, 100, - ds_cstr(&match), - ds_cstr(&options_action), - &op->nbsp->dhcpv4_options->header_); + ovn_lflow_add_with_hint__(lflows, op->od, + S_SWITCH_IN_DHCP_OPTIONS, 100, + ds_cstr(&match), + ds_cstr(&options_action), + copp_meter_get(COPP_DHCPV4_OPTS, + op->od->nbs->copp, + meter_groups), + &op->nbsp->dhcpv4_options->header_); ds_clear(&match); /* Allow ip4.src = OFFER_IP and * ip4.dst = {SERVER_IP, 255.255.255.255} for the below @@ -6726,11 +6741,14 @@ build_dhcpv4_options_flows(struct ovn_port *op, op->json_key); } - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_DHCP_OPTIONS, 100, - ds_cstr(&match), - ds_cstr(&options_action), - &op->nbsp->dhcpv4_options->header_); + ovn_lflow_add_with_hint__(lflows, op->od, + S_SWITCH_IN_DHCP_OPTIONS, 100, + ds_cstr(&match), + ds_cstr(&options_action), + copp_meter_get(COPP_DHCPV4_OPTS, + op->od->nbs->copp, + meter_groups), + &op->nbsp->dhcpv4_options->header_); ds_clear(&match); /* If REGBIT_DHCP_OPTS_RESULT is set, it means the @@ -6764,6 +6782,7 @@ static void build_dhcpv6_options_flows(struct ovn_port *op, struct lport_addresses *lsp_addrs, const char *json_key, bool is_external, + struct shash *meter_groups, struct hmap *lflows) { struct ds match = DS_EMPTY_INITIALIZER; @@ -6786,11 +6805,14 @@ build_dhcpv6_options_flows(struct ovn_port *op, op->json_key); } - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_DHCP_OPTIONS, 100, - ds_cstr(&match), - ds_cstr(&options_action), - &op->nbsp->dhcpv6_options->header_); + ovn_lflow_add_with_hint__(lflows, op->od, + S_SWITCH_IN_DHCP_OPTIONS, 100, + ds_cstr(&match), + ds_cstr(&options_action), + copp_meter_get(COPP_DHCPV6_OPTS, + op->od->nbs->copp, + meter_groups), + &op->nbsp->dhcpv6_options->header_); /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the * put_dhcpv6_opts action is successful */ @@ -6980,6 +7002,7 @@ static void build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op, struct hmap *lflows, struct hmap *ports, + struct shash *meter_groups, struct ds *actions, struct ds *match) { @@ -7125,11 +7148,14 @@ build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op, op->lsp_addrs[i].ipv6_addrs[j].addr_s, op->lsp_addrs[i].ipv6_addrs[j].addr_s, op->lsp_addrs[i].ea_s); - ovn_lflow_add_with_hint(lflows, op->od, - S_SWITCH_IN_ARP_ND_RSP, 50, - ds_cstr(match), - ds_cstr(actions), - &op->nbsp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, + S_SWITCH_IN_ARP_ND_RSP, 50, + ds_cstr(match), + ds_cstr(actions), + copp_meter_get(COPP_ND_NA, + op->od->nbs->copp, + meter_groups), + &op->nbsp->header_); /* Do not reply to a solicitation from the port that owns * the address (otherwise DAD detection will fail). */ @@ -7249,7 +7275,8 @@ build_lswitch_arp_nd_service_monitor(struct ovn_northd_lb *lb, * priority 100 flows. */ static void build_lswitch_dhcp_options_and_response(struct ovn_port *op, - struct hmap *lflows) + struct hmap *lflows, + struct shash *meter_groups) { if (op->nbsp) { if (!lsp_is_enabled(op->nbsp) || lsp_is_router(op->nbsp)) { @@ -7278,17 +7305,17 @@ build_lswitch_dhcp_options_and_response(struct ovn_port *op, build_dhcpv4_options_flows( op, &op->lsp_addrs[i], op->od->localnet_ports[j]->json_key, is_external, - lflows); + meter_groups, lflows); build_dhcpv6_options_flows( op, &op->lsp_addrs[i], op->od->localnet_ports[j]->json_key, is_external, - lflows); + meter_groups, lflows); } } else { build_dhcpv4_options_flows(op, &op->lsp_addrs[i], op->json_key, - is_external, lflows); + is_external, meter_groups, lflows); build_dhcpv6_options_flows(op, &op->lsp_addrs[i], op->json_key, - is_external, lflows); + is_external, meter_groups, lflows); } } } @@ -7318,13 +7345,15 @@ build_lswitch_dhcp_and_dns_defaults(struct ovn_datapath *od, */ static void build_lswitch_dns_lookup_and_response(struct ovn_datapath *od, - struct hmap *lflows) + struct hmap *lflows, + struct shash *meter_groups) { if (od->nbs && ls_has_dns_records(od->nbs)) { - - ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 100, - "udp.dst == 53", - REGBIT_DNS_LOOKUP_RESULT" = dns_lookup(); next;"); + ovn_lflow_metered(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 100, + "udp.dst == 53", + REGBIT_DNS_LOOKUP_RESULT" = dns_lookup(); next;", + copp_meter_get(COPP_DNS, od->nbs->copp, + meter_groups)); const char *dns_action = "eth.dst <-> eth.src; ip4.src <-> ip4.dst; " "udp.dst = udp.src; udp.src = 53; outport = inport; " "flags.loopback = 1; output;"; @@ -7361,7 +7390,8 @@ build_lswitch_external_port(struct ovn_port *op, static void build_lswitch_destination_lookup_bmcast(struct ovn_datapath *od, struct hmap *lflows, - struct ds *actions) + struct ds *actions, + struct shash *meter_groups) { if (od->nbs) { @@ -7382,12 +7412,16 @@ build_lswitch_destination_lookup_bmcast(struct ovn_datapath *od, } ds_put_cstr(actions, "igmp;"); /* Punt IGMP traffic to controller. */ - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100, - "ip4 && ip.proto == 2", ds_cstr(actions)); + ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 100, + "ip4 && ip.proto == 2", ds_cstr(actions), + copp_meter_get(COPP_IGMP, od->nbs->copp, + meter_groups)); /* Punt MLD traffic to controller. */ - ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100, - "mldv1 || mldv2", ds_cstr(actions)); + ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 100, + "mldv1 || mldv2", ds_cstr(actions), + copp_meter_get(COPP_IGMP, od->nbs->copp, + meter_groups)); /* Flood all IP multicast traffic destined to 224.0.0.X to all * ports - RFC 4541, section 2.1.2, item 2. @@ -8781,7 +8815,8 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip, struct ovn_northd_lb *lb, struct ovn_northd_lb_vip *vips_nb, struct hmap *lflows, - struct ds *match, struct ds *action) + struct ds *match, struct ds *action, + struct shash *meter_groups) { char *skip_snat_new_action = NULL; char *skip_snat_est_action = NULL; @@ -8791,8 +8826,8 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip, ds_clear(match); ds_clear(action); - build_lb_vip_actions(lb_vip, vips_nb, action, - lb->selection_fields, false); + bool reject = build_lb_vip_actions(lb_vip, vips_nb, action, + lb->selection_fields, false); /* Higher priority rules are added for load-balancing in DNAT * table. For every match (on a VIP[:port]), we add two flows. @@ -8871,6 +8906,11 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip, char *new_match_p = new_match; char *est_match_p = est_match; char *est_actions = NULL; + const char *meter = NULL; + + if (reject) { + meter = copp_meter_get(COPP_REJECT, od->nbr->copp, meter_groups); + } if (sset_contains(&od->external_ips, lb_vip->vip_str)) { /* The load balancer vip is also present in the NAT entries. @@ -8906,18 +8946,18 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip, } if (snat_type == SKIP_SNAT) { - ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio, - new_match_p, skip_snat_new_action, - &lb->nlb->header_); + ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, prio, + new_match_p, skip_snat_new_action, + meter, &lb->nlb->header_); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio, est_match_p, skip_snat_est_action, &lb->nlb->header_); } else if (snat_type == FORCE_SNAT) { char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s", ds_cstr(action)); - ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio, - new_match_p, new_actions, - &lb->nlb->header_); + ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, prio, + new_match_p, new_actions, + meter, &lb->nlb->header_); free(new_actions); est_actions = xasprintf("flags.force_snat_for_lb = 1; " @@ -8926,9 +8966,9 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip, est_match_p, est_actions, &lb->nlb->header_); } else { - ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio, - new_match_p, ds_cstr(action), - &lb->nlb->header_); + ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, prio, + new_match_p, ds_cstr(action), + meter, &lb->nlb->header_); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio, est_match_p, "ct_dnat;", &lb->nlb->header_); @@ -8990,14 +9030,18 @@ build_lswitch_flows_for_lb(struct ovn_northd_lb *lb, struct hmap *lflows, struct ovn_lb_vip *lb_vip = &lb->vips[i]; /* pre-stateful lb */ - if (!build_empty_lb_event_flow(lb_vip, lb->nlb, meter_groups, - match, action)) { + if (!build_empty_lb_event_flow(lb_vip, lb->nlb, match, action)) { continue; } for (size_t j = 0; j < lb->n_nb_ls; j++) { - ovn_lflow_add_with_hint(lflows, lb->nb_ls[j], - S_SWITCH_IN_PRE_LB, 130, ds_cstr(match), - ds_cstr(action), &lb->nlb->header_); + struct ovn_datapath *od = lb->nb_ls[j]; + ovn_lflow_add_with_hint__(lflows, od, + S_SWITCH_IN_PRE_LB, 130, ds_cstr(match), + ds_cstr(action), + copp_meter_get(COPP_EVENT_ELB, + od->nbs->copp, + meter_groups), + &lb->nlb->header_); } /* Ignore L4 port information in the key because fragmented packets * may not have L4 information. The pre-stateful table will send @@ -9011,7 +9055,7 @@ build_lswitch_flows_for_lb(struct ovn_northd_lb *lb, struct hmap *lflows, * a higher priority rule for load balancing below also commits the * connection, so it is okay if we do not hit the above match on * REGBIT_CONNTRACK_COMMIT. */ - build_lb_rules(lflows, lb, match, action); + build_lb_rules(lflows, lb, match, action, meter_groups); } static void @@ -9027,16 +9071,20 @@ build_lrouter_flows_for_lb(struct ovn_northd_lb *lb, struct hmap *lflows, struct ovn_lb_vip *lb_vip = &lb->vips[i]; build_lrouter_nat_flows_for_lb(lb_vip, lb, &lb->vips_nb[i], - lflows, match, action); + lflows, match, action, + meter_groups); - if (!build_empty_lb_event_flow(lb_vip, lb->nlb, meter_groups, - match, action)) { + if (!build_empty_lb_event_flow(lb_vip, lb->nlb, match, action)) { continue; } for (size_t j = 0; j < lb->n_nb_lr; j++) { - ovn_lflow_add_with_hint(lflows, lb->nb_lr[j], S_ROUTER_IN_DNAT, - 130, ds_cstr(match), ds_cstr(action), - &lb->nlb->header_); + struct ovn_datapath *od = lb->nb_lr[j]; + ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, + 130, ds_cstr(match), ds_cstr(action), + copp_meter_get(COPP_EVENT_ELB, + od->nbr->copp, + meter_groups), + &lb->nlb->header_); } } @@ -9322,7 +9370,7 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, const char *sn_ip_address, const char *eth_addr, struct ds *extra_match, bool drop, uint16_t priority, const struct ovsdb_idl_row *hint, - struct hmap *lflows) + struct hmap *lflows, struct shash *meter_groups) { struct ds match = DS_EMPTY_INITIALIZER; struct ds actions = DS_EMPTY_INITIALIZER; @@ -9344,6 +9392,8 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, if (drop) { ds_put_format(&actions, "drop;"); + ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority, + ds_cstr(&match), ds_cstr(&actions), hint); } else { ds_put_format(&actions, "%s { " @@ -9360,11 +9410,13 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, ip_address, ip_address, eth_addr); + ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_IP_INPUT, priority, + ds_cstr(&match), ds_cstr(&actions), + copp_meter_get(COPP_ND_NA, od->nbr->copp, + meter_groups), + hint); } - ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority, - ds_cstr(&match), ds_cstr(&actions), hint); - ds_destroy(&match); ds_destroy(&actions); } @@ -9372,7 +9424,8 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op, static void build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od, struct ovn_nat *nat_entry, - struct hmap *lflows) + struct hmap *lflows, + struct shash *meter_groups) { struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; const struct nbrec_nat *nat = nat_entry->nb; @@ -9382,7 +9435,7 @@ build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od, ext_addrs->ipv6_addrs[0].addr_s, ext_addrs->ipv6_addrs[0].sn_addr_s, REG_INPORT_ETH_ADDR, NULL, false, 90, - &nat->header_, lflows); + &nat->header_, lflows, meter_groups); } else { build_lrouter_arp_flow(od, NULL, ext_addrs->ipv4_addrs[0].addr_s, @@ -9394,7 +9447,8 @@ build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od, static void build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, struct ovn_nat *nat_entry, - struct hmap *lflows) + struct hmap *lflows, + struct shash *meter_groups) { struct lport_addresses *ext_addrs = &nat_entry->ext_addrs; const struct nbrec_nat *nat = nat_entry->nb; @@ -9437,12 +9491,12 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port *op, ext_addrs->ipv6_addrs[0].addr_s, ext_addrs->ipv6_addrs[0].sn_addr_s, mac_s, &match, false, 92, - &nat->header_, lflows); + &nat->header_, lflows, meter_groups); build_lrouter_nd_flow(op->od, op, "nd_na", ext_addrs->ipv6_addrs[0].addr_s, ext_addrs->ipv6_addrs[0].sn_addr_s, mac_s, NULL, true, 91, - &nat->header_, lflows); + &nat->header_, lflows, meter_groups); } else { build_lrouter_arp_flow(op->od, op, ext_addrs->ipv4_addrs[0].addr_s, @@ -9615,7 +9669,8 @@ build_lrouter_force_snat_flows_op(struct ovn_port *op, } static void -build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op) +build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op, + struct shash *meter_groups) { if (!op->has_bfd) { return; @@ -9634,9 +9689,11 @@ build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op) ds_clear(&match); ds_put_format(&match, "ip4.dst == %s && udp.dst == 3784", ds_cstr(&ip_list)); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, - ds_cstr(&match), "handle_bfd_msg(); ", - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, + ds_cstr(&match), "handle_bfd_msg(); ", + copp_meter_get(COPP_BFD, op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } if (op->lrp_networks.n_ipv6_addrs) { ds_clear(&ip_list); @@ -9651,9 +9708,11 @@ build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op) ds_clear(&match); ds_put_format(&match, "ip6.dst == %s && udp.dst == 3784", ds_cstr(&ip_list)); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, - ds_cstr(&match), "handle_bfd_msg(); ", - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, + ds_cstr(&match), "handle_bfd_msg(); ", + copp_meter_get(COPP_BFD, op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } ds_destroy(&ip_list); @@ -9733,7 +9792,8 @@ build_adm_ctrl_flows_for_lrouter_port( static void build_neigh_learning_flows_for_lrouter( struct ovn_datapath *od, struct hmap *lflows, - struct ds *match, struct ds *actions) + struct ds *match, struct ds *actions, + struct shash *meter_groups) { if (od->nbr) { @@ -9813,14 +9873,20 @@ build_neigh_learning_flows_for_lrouter( ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100, ds_cstr(match), "next;"); - ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, - "arp", "put_arp(inport, arp.spa, arp.sha); next;"); + ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, + "arp", "put_arp(inport, arp.spa, arp.sha); next;", + copp_meter_get(COPP_ARP, od->nbr->copp, + meter_groups)); - ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, - "nd_na", "put_nd(inport, nd.target, nd.tll); next;"); + ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, + "nd_na", "put_nd(inport, nd.target, nd.tll); next;", + copp_meter_get(COPP_ND_NA, od->nbr->copp, + meter_groups)); - ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, - "nd_ns", "put_nd(inport, ip6.src, nd.sll); next;"); + ovn_lflow_metered(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90, + "nd_ns", "put_nd(inport, ip6.src, nd.sll); next;", + copp_meter_get(COPP_ND_NS, od->nbr->copp, + meter_groups)); } } @@ -9895,7 +9961,8 @@ build_neigh_learning_flows_for_lrouter_port( static void build_ND_RA_flows_for_lrouter_port( struct ovn_port *op, struct hmap *lflows, - struct ds *match, struct ds *actions) + struct ds *match, struct ds *actions, + struct shash *meter_groups) { if (!op->nbrp || op->nbrp->peer || !op->peer) { return; @@ -9988,9 +10055,12 @@ build_ND_RA_flows_for_lrouter_port( if (add_rs_response_flow) { ds_put_cstr(actions, "); next;"); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS, - 50, ds_cstr(match), ds_cstr(actions), - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS, + 50, ds_cstr(match), ds_cstr(actions), + copp_meter_get(COPP_ND_RA_OPTS, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(actions); ds_clear(match); ds_put_format(match, "inport == %s && ip6.dst == ff02::2 && " @@ -10613,7 +10683,8 @@ static void build_check_pkt_len_flows_for_lrouter( struct ovn_datapath *od, struct hmap *lflows, struct hmap *ports, - struct ds *match, struct ds *actions) + struct ds *match, struct ds *actions, + struct shash *meter_groups) { if (od->nbr) { @@ -10675,10 +10746,14 @@ build_check_pkt_len_flows_for_lrouter( rp->lrp_networks.ipv4_addrs[0].addr_s, gw_mtu, ovn_stage_get_table(S_ROUTER_IN_ADMISSION)); - ovn_lflow_add_with_hint(lflows, od, - S_ROUTER_IN_LARGER_PKTS, 50, - ds_cstr(match), ds_cstr(actions), - &rp->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, od, + S_ROUTER_IN_LARGER_PKTS, 50, + ds_cstr(match), ds_cstr(actions), + copp_meter_get( + COPP_ICMP4_ERR, + rp->od->nbr->copp, + meter_groups), + &rp->nbrp->header_); } if (rp->lrp_networks.ipv6_addrs) { @@ -10704,10 +10779,14 @@ build_check_pkt_len_flows_for_lrouter( rp->lrp_networks.ipv6_addrs[0].addr_s, gw_mtu, ovn_stage_get_table(S_ROUTER_IN_ADMISSION)); - ovn_lflow_add_with_hint(lflows, od, - S_ROUTER_IN_LARGER_PKTS, 50, - ds_cstr(match), ds_cstr(actions), - &rp->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, od, + S_ROUTER_IN_LARGER_PKTS, 50, + ds_cstr(match), ds_cstr(actions), + copp_meter_get( + COPP_ICMP6_ERR, + rp->od->nbr->copp, + meter_groups), + &rp->nbrp->header_); } } } @@ -10762,7 +10841,8 @@ build_gateway_redirect_flows_for_lrouter( static void build_arp_request_flows_for_lrouter( struct ovn_datapath *od, struct hmap *lflows, - struct ds *match, struct ds *actions) + struct ds *match, struct ds *actions, + struct shash *meter_groups) { if (od->nbr) { for (int i = 0; i < od->nbr->n_static_routes; i++) { @@ -10799,26 +10879,33 @@ build_arp_request_flows_for_lrouter( "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s, route->nexthop); - ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200, - ds_cstr(match), ds_cstr(actions), - &route->header_); - } - - ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, - "eth.dst == 00:00:00:00:00:00 && ip4", - "arp { " - "eth.dst = ff:ff:ff:ff:ff:ff; " - "arp.spa = " REG_SRC_IPV4 "; " - "arp.tpa = " REG_NEXT_HOP_IPV4 "; " - "arp.op = 1; " /* ARP request */ - "output; " - "};"); - ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, - "eth.dst == 00:00:00:00:00:00 && ip6", - "nd_ns { " - "nd.target = " REG_NEXT_HOP_IPV6 "; " - "output; " - "};"); + ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200, + ds_cstr(match), ds_cstr(actions), + copp_meter_get(COPP_ND_NS_RESOLVE, + od->nbr->copp, + meter_groups), + &route->header_); + } + + ovn_lflow_metered(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, + "eth.dst == 00:00:00:00:00:00 && ip4", + "arp { " + "eth.dst = ff:ff:ff:ff:ff:ff; " + "arp.spa = " REG_SRC_IPV4 "; " + "arp.tpa = " REG_NEXT_HOP_IPV4 "; " + "arp.op = 1; " /* ARP request */ + "output; " + "};", + copp_meter_get(COPP_ARP_RESOLVE, od->nbr->copp, + meter_groups)); + ovn_lflow_metered(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, + "eth.dst == 00:00:00:00:00:00 && ip6", + "nd_ns { " + "nd.target = " REG_NEXT_HOP_IPV6 "; " + "output; " + "};", + copp_meter_get(COPP_ND_NS_RESOLVE, od->nbr->copp, + meter_groups)); ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;"); } } @@ -10949,7 +11036,8 @@ build_dhcpv6_reply_flows_for_lrouter_port( static void build_ipv6_input_flows_for_lrouter_port( struct ovn_port *op, struct hmap *lflows, - struct ds *match, struct ds *actions) + struct ds *match, struct ds *actions, + struct shash *meter_groups) { if (op->nbrp && (!op->derived)) { /* No ingress packets are accepted on a chassisredirect @@ -10992,7 +11080,7 @@ build_ipv6_input_flows_for_lrouter_port( op->lrp_networks.ipv6_addrs[i].addr_s, op->lrp_networks.ipv6_addrs[i].sn_addr_s, REG_INPORT_ETH_ADDR, match, false, 90, - &op->nbrp->header_, lflows); + &op->nbrp->header_, lflows, meter_groups); } /* UDP/TCP/SCTP port unreachable */ @@ -11007,9 +11095,13 @@ build_ipv6_input_flows_for_lrouter_port( "eth.dst <-> eth.src; " "ip6.dst <-> ip6.src; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 80, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 80, ds_cstr(match), action, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11019,9 +11111,13 @@ build_ipv6_input_flows_for_lrouter_port( "eth.dst <-> eth.src; " "ip6.dst <-> ip6.src; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 80, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 80, ds_cstr(match), action, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11034,9 +11130,13 @@ build_ipv6_input_flows_for_lrouter_port( "icmp6.type = 1; " "icmp6.code = 4; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 80, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 80, ds_cstr(match), action, + copp_meter_get( + COPP_ICMP6_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11049,9 +11149,13 @@ build_ipv6_input_flows_for_lrouter_port( "icmp6.type = 1; " "icmp6.code = 3; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 70, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 70, ds_cstr(match), action, + copp_meter_get( + COPP_ICMP6_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } } @@ -11082,9 +11186,12 @@ build_ipv6_input_flows_for_lrouter_port( "icmp6.code = 0; /* TTL exceeded in transit */ " "next; };", op->lrp_networks.ipv6_addrs[i].addr_s); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40, - ds_cstr(match), ds_cstr(actions), - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40, + ds_cstr(match), ds_cstr(actions), + copp_meter_get(COPP_ICMP6_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } } @@ -11092,7 +11199,8 @@ build_ipv6_input_flows_for_lrouter_port( static void build_lrouter_arp_nd_for_datapath(struct ovn_datapath *od, - struct hmap *lflows) + struct hmap *lflows, + struct shash *meter_groups) { if (od->nbr) { @@ -11118,7 +11226,7 @@ build_lrouter_arp_nd_for_datapath(struct ovn_datapath *od, if (!strcmp(nat_entry->nb->type, "snat")) { continue; } - build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows); + build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows, meter_groups); } /* Now handle SNAT entries too, one per unique SNAT IP. */ @@ -11133,7 +11241,7 @@ build_lrouter_arp_nd_for_datapath(struct ovn_datapath *od, struct ovn_nat *nat_entry = CONTAINER_OF(ovs_list_front(&snat_ip->snat_entries), struct ovn_nat, ext_addr_list_node); - build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows); + build_lrouter_nat_arp_nd_flow(od, nat_entry, lflows, meter_groups); } } } @@ -11142,7 +11250,8 @@ build_lrouter_arp_nd_for_datapath(struct ovn_datapath *od, static void build_lrouter_ipv4_ip_input(struct ovn_port *op, struct hmap *lflows, - struct ds *match, struct ds *actions) + struct ds *match, struct ds *actions, + struct shash *meter_groups) { /* No ingress packets are accepted on a chassisredirect * port, so no need to program flows for that port. */ @@ -11180,7 +11289,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, } /* BFD msg handling */ - build_lrouter_bfd_flows(lflows, op); + build_lrouter_bfd_flows(lflows, op, meter_groups); /* ICMP time exceeded */ for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) { @@ -11200,9 +11309,12 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, "ip.ttl = 255; " "next; };", op->lrp_networks.ipv4_addrs[i].addr_s); - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40, - ds_cstr(match), ds_cstr(actions), - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40, + ds_cstr(match), ds_cstr(actions), + copp_meter_get(COPP_ICMP4_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } /* ARP reply. These flows reply to ARP requests for the router's own @@ -11279,7 +11391,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, build_lrouter_nd_flow(op->od, op, "nd_na", ip_address, NULL, REG_INPORT_ETH_ADDR, - match, false, 90, NULL, lflows); + match, false, 90, NULL, + lflows, meter_groups); } if (!smap_get(&op->od->nbr->options, "chassis") @@ -11297,9 +11410,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, "icmp4.type = 3; " "icmp4.code = 3; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 80, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 80, ds_cstr(match), action, + copp_meter_get( + COPP_ICMP4_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11309,9 +11426,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, "eth.dst <-> eth.src; " "ip4.dst <-> ip4.src; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 80, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 80, ds_cstr(match), action, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11321,9 +11442,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, "eth.dst <-> eth.src; " "ip4.dst <-> ip4.src; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 80, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 80, ds_cstr(match), action, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11336,9 +11461,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, "icmp4.type = 3; " "icmp4.code = 2; " "next; };"; - ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, - 70, ds_cstr(match), action, - &op->nbrp->header_); + ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, + 70, ds_cstr(match), action, + copp_meter_get( + COPP_ICMP4_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } } @@ -11382,7 +11511,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, if (!strcmp(nat_entry->nb->type, "snat")) { continue; } - build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows); + build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows, + meter_groups); } /* Now handle SNAT entries too, one per unique SNAT IP. */ @@ -11397,7 +11527,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op, struct ovn_nat *nat_entry = CONTAINER_OF(ovs_list_front(&snat_ip->snat_entries), struct ovn_nat, ext_addr_list_node); - build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows); + build_lrouter_port_nat_arp_nd_flow(op, nat_entry, lflows, + meter_groups); } } } @@ -12070,15 +12201,16 @@ build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od, build_lswitch_input_port_sec_od(od, lsi->lflows); build_lswitch_learn_fdb_od(od, lsi->lflows); build_lswitch_arp_nd_responder_default(od, lsi->lflows); - build_lswitch_dns_lookup_and_response(od, lsi->lflows); + build_lswitch_dns_lookup_and_response(od, lsi->lflows, lsi->meter_groups); build_lswitch_dhcp_and_dns_defaults(od, lsi->lflows); - build_lswitch_destination_lookup_bmcast(od, lsi->lflows, &lsi->actions); + build_lswitch_destination_lookup_bmcast(od, lsi->lflows, &lsi->actions, + lsi->meter_groups); build_lswitch_output_port_sec_od(od, lsi->lflows); /* Build Logical Router Flows. */ build_adm_ctrl_flows_for_lrouter(od, lsi->lflows); build_neigh_learning_flows_for_lrouter(od, lsi->lflows, &lsi->match, - &lsi->actions); + &lsi->actions, lsi->meter_groups); build_ND_RA_flows_for_lrouter(od, lsi->lflows); build_static_route_flows_for_lrouter(od, lsi->lflows, lsi->ports, lsi->bfd_connections); @@ -12087,13 +12219,14 @@ build_lswitch_and_lrouter_iterate_by_od(struct ovn_datapath *od, build_ingress_policy_flows_for_lrouter(od, lsi->lflows, lsi->ports); build_arp_resolve_flows_for_lrouter(od, lsi->lflows); build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->ports, - &lsi->match, &lsi->actions); + &lsi->match, &lsi->actions, + lsi->meter_groups); build_gateway_redirect_flows_for_lrouter(od, lsi->lflows, &lsi->match, &lsi->actions); build_arp_request_flows_for_lrouter(od, lsi->lflows, &lsi->match, - &lsi->actions); + &lsi->actions, lsi->meter_groups); build_misc_local_traffic_drop_flows_for_lrouter(od, lsi->lflows); - build_lrouter_arp_nd_for_datapath(od, lsi->lflows); + build_lrouter_arp_nd_for_datapath(od, lsi->lflows, lsi->meter_groups); build_lrouter_nat_defrag_and_lb(od, lsi->lflows, lsi->lbs, &lsi->match, &lsi->actions); } @@ -12113,9 +12246,11 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op, &lsi->match); build_lswitch_arp_nd_responder_known_ips(op, lsi->lflows, lsi->ports, + lsi->meter_groups, &lsi->actions, &lsi->match); - build_lswitch_dhcp_options_and_response(op, lsi->lflows); + build_lswitch_dhcp_options_and_response(op, lsi->lflows, + lsi->meter_groups); build_lswitch_external_port(op, lsi->lflows); build_lswitch_ip_unicast_lookup(op, lsi->lflows, lsi->mcgroups, &lsi->actions, &lsi->match); @@ -12129,16 +12264,17 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op, &lsi->actions); build_ip_routing_flows_for_lrouter_port(op, lsi->lflows); build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, - &lsi->actions); + &lsi->actions, lsi->meter_groups); build_arp_resolve_flows_for_lrouter_port(op, lsi->lflows, lsi->ports, &lsi->match, &lsi->actions); build_egress_delivery_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, &lsi->actions); build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, &lsi->match); build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows, - &lsi->match, &lsi->actions); + &lsi->match, &lsi->actions, + lsi->meter_groups); build_lrouter_ipv4_ip_input(op, lsi->lflows, - &lsi->match, &lsi->actions); + &lsi->match, &lsi->actions, lsi->meter_groups); build_lrouter_force_snat_flows_op(op, lsi->lflows, &lsi->match, &lsi->actions); } diff --git a/ovn-nb.xml b/ovn-nb.xml index 1e285851d..590d907cd 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -414,6 +414,9 @@ Rate limiting meter for BFD packets. + + Rate limiting meter for packets that trigger a reject action +
diff --git a/tests/atlocal.in b/tests/atlocal.in index b5bc0818b..310fd46a5 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -169,6 +169,9 @@ find_command tcpdump # Set HAVE_LFTP find_command lftp +# Set HAVE_SCAPY +find_command scapy + CURL_OPT="-g -v --max-time 1 --retry 2 --retry-delay 1 --connect-timeout 1" # Determine whether "diff" supports "normal" diffs. (busybox diff does not.) diff --git a/tests/ovn.at b/tests/ovn.at index 41d78da6c..661665d17 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -1668,10 +1668,6 @@ reject { }; trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63) -trigger_event(event = "empty_lb_backends", meter="event-elb" vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); - formats as trigger_event(event = "empty_lb_backends", meter = "event-elb", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); - encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63,meter_id=5) - # Testing invalid vip results in extra error messages from socket-util.c trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "aarp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c"); Load balancer protocol 'aarp' is not 'tcp', 'udp', or 'sctp' @@ -17837,6 +17833,10 @@ AT_CLEANUP OVN_FOR_EACH_NORTHD([ AT_SETUP([controller event]) AT_KEYWORDS([ovn_controller_event]) + +dnl This test is not suported by ovn-northd-ddlog yet. +AT_SKIP_IF([test NORTHD_TYPE = ovn-northd-ddlog && test "$RUN_ANYWAY" != yes]) + ovn_start # Create hypervisors hv[12]. @@ -17900,6 +17900,7 @@ ovn-nbctl ls-lb-add sw0 lb2 uuid_lb2=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb2) ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10 +ovn-nbctl --wait=hv ls-copp-add sw0 event-elb event-elb OVN_POPULATE_ARP wait_for_ports_up diff --git a/tests/system-ovn.at b/tests/system-ovn.at index f42cfc0db..6feeae0c8 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -6481,3 +6481,141 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([ovn -- CoPP]) +AT_SKIP_IF([test $HAVE_TCPDUMP = no]) +AT_SKIP_IF([test $HAVE_SCAPY = no]) +AT_KEYWORDS([ovn-copp]) + +dnl This test is not suported by ovn-northd-ddlog yet. +AT_SKIP_IF([test NORTHD_TYPE = ovn-northd-ddlog && test "$RUN_ANYWAY" != yes]) + +ovn_start +OVS_TRAFFIC_VSWITCHD_START() + +ADD_BR([br-int]) +ADD_BR([br-ext]) + +check ovs-ofctl add-flow br-ext action=normal +# Set external-ids in br-int needed for ovn-controller +check ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +# Start ovn-controller +start_daemon ovn-controller + +check ovn-nbctl lr-add R1 +check ovn-nbctl ls-add sw0 +check ovn-nbctl ls-add public + +check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24 +check ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.1/24 1000::a/64 \ + -- lrp-set-gateway-chassis rp-public hv1 + +check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \ + type=router options:router-port=rp-sw0 \ + -- lsp-set-addresses sw0-rp router + +check ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \ + type=router options:router-port=rp-public \ + -- lsp-set-addresses public-rp router + +ADD_NAMESPACES(sw01) +ADD_VETH(sw01, sw01, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \ + "192.168.1.1") +check ovn-nbctl lsp-add sw0 sw01 \ + -- lsp-set-addresses sw01 "f0:00:00:01:02:03 192.168.1.2" + +ADD_NAMESPACES(server) +NS_CHECK_EXEC([server], [ip link set dev lo up]) +ADD_VETH(s1, server, br-ext, "172.16.1.50/24", "f0:00:00:01:02:05", \ + "172.16.1.1") + +AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext]) +check ovn-nbctl lsp-add public public1 \ + -- lsp-set-addresses public1 unknown \ + -- lsp-set-type public1 localnet \ + -- lsp-set-options public1 network_name=phynet + +NS_EXEC([sw01], [tcpdump -n -i sw01 icmp -Q in > reject.pcap &]) +check ovn-nbctl meter-add acl-meter drop 1 pktps 0 +check ovn-nbctl --wait=hv ls-copp-add sw0 reject acl-meter +check ovn-nbctl acl-add sw0 from-lport 1002 'inport == "sw01" && ip && udp' reject + +AT_CHECK([ovn-nbctl ls-copp-list sw0], [0], [dnl +reject: acl-meter +]) + +ip netns exec sw01 scapy -H <<-EOF +p = IP(src="192.168.1.2", dst="192.168.1.1")/ UDP(dport = 12345) / Raw(b"X"*64) +send (p, iface='sw01', loop = 0, verbose = 0, count = 20) +EOF + +sleep 2 +kill $(pidof tcpdump) + +# 1pps + 1 burst size +OVS_WAIT_UNTIL([ + n_reject=$(grep unreachable reject.pcap | wc -l) + test "${n_reject}" = "2" +]) + +rm -f reject.pcap +NS_EXEC([sw01], [tcpdump -n -i sw01 icmp -Q in > reject.pcap &]) +check ovn-nbctl --wait=hv ls-copp-del sw0 reject + +ip netns exec sw01 scapy -H <<-EOF +p = IP(src="192.168.1.2", dst="192.168.1.1")/ UDP(dport = 12345) / Raw(b"X"*64) +send (p, iface='sw01', loop = 0, verbose = 0, count = 20) +EOF + +sleep 2 +kill $(pidof tcpdump) + +OVS_WAIT_UNTIL([ + n_reject=$(grep unreachable reject.pcap | wc -l) + test "${n_reject}" = "20" +]) + +NS_EXEC([server], [tcpdump -n -i s1 arp[[24:4]]=0xac100164 > arp.pcap &]) +check ovn-nbctl meter-add arp-meter drop 1 pktps 0 +check ovn-nbctl --wait=hv lr-copp-add R1 arp-resolve arp-meter +AT_CHECK([ovn-nbctl lr-copp-list R1], [0], [dnl +arp-resolve: arp-meter +]) + +ip netns exec sw01 scapy -H <<-EOF +p = IP(src="192.168.1.2", dst="172.16.1.100")/ TCP(dport = 80, flags="S") / Raw(b"X"*64) +send (p, iface='sw01', loop = 0, verbose = 0, count = 100) +EOF + +sleep 2 +kill $(pidof tcpdump) + +# 1pps + 1 burst size +OVS_WAIT_UNTIL([ + n_arp=$(grep ARP arp.pcap | wc -l) + test "${n_arp}" = "2" +]) + +kill $(pidof ovn-controller) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d +/.*terminating with signal 15.*/d"]) +AT_CLEANUP +]) diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml index 101849911..987797860 100644 --- a/utilities/ovn-nbctl.8.xml +++ b/utilities/ovn-nbctl.8.xml @@ -1466,6 +1466,9 @@
  • packets that need to be replied to with ICMP Errors
  • packets that need to be replied to with TCP RST
  • packets that need to be replied to with DHCP_OPTS
  • +
  • packets that trigger a reject action
  • +
  • packets that trigger a SCTP abort action
  • +
  • controller_events
  • BFD
  • From patchwork Thu Jul 8 16:40:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1502515 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=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=OA7ONr4R; dkim-atps=neutral Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GLMVL73rkz9sWl for ; Fri, 9 Jul 2021 02:40:42 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 531EF60C06; Thu, 8 Jul 2021 16:40:38 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 39BpQMozxO8e; Thu, 8 Jul 2021 16:40:37 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp3.osuosl.org (Postfix) with ESMTPS id 55A5D60C14; Thu, 8 Jul 2021 16:40:36 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 8CD34C001F; Thu, 8 Jul 2021 16:40:35 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2E8DAC001F for ; Thu, 8 Jul 2021 16:40:34 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id 5E35483C60 for ; Thu, 8 Jul 2021 16:40:31 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp1.osuosl.org (amavisd-new); dkim=pass (1024-bit key) header.d=redhat.com Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 5jNff9kCkLfH for ; Thu, 8 Jul 2021 16:40:30 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id A0FD483C58 for ; Thu, 8 Jul 2021 16:40:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1625762429; 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=MGrgV/odzJolGHCm847/SGqZx7n+GyF7Kdko220iO+k=; b=OA7ONr4ReOcV/4DwhfGU7vRjPXUMGuWOkgrfxQ0Lm/a+liqZHv9ycPy44TVnoh0hXYzYA5 2XE26LhhVZTR9u6tDBLKkd93IyVdOG4klUSynNn5azKYZ5oOKdvpBzp2+bQrRq2NLYAp1n 7/T7ssz0gwmpGwnLEVpFTZQ0viriYqY= Received: from mail-ed1-f69.google.com (mail-ed1-f69.google.com [209.85.208.69]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-587-B46M2dQ9PgOI6rivbqe7Jw-1; Thu, 08 Jul 2021 12:40:28 -0400 X-MC-Unique: B46M2dQ9PgOI6rivbqe7Jw-1 Received: by mail-ed1-f69.google.com with SMTP id z5-20020a05640235c5b0290393974bcf7eso3620334edc.2 for ; Thu, 08 Jul 2021 09:40:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=MGrgV/odzJolGHCm847/SGqZx7n+GyF7Kdko220iO+k=; b=NXvJ3tC2II39qqs0ozBp3HErJqsvn7VIC2zH8+jkLr9eBQeQpcM/PqUWMHH6i8ab/l 9HPRXa5v8OUQUyIJDREQRhOyqVo7WazxK/yh/lfMycEfkkFvUstii1KZcu7O9YRlmMLi oJE5mFJnL1Qbe65wD1I6CRODej6rxwmbx9/9AfxP4ykkaJU4ociIMvzkQhcrEFNE6lnt L+y6jI7EDSmu+dujfly8Gcf4+RdNcjAwAEdkNk/EYhtexh9O59Vtre4sjGGXpT7q4dX0 tnsGOHjAl83bBms41Q14ZFTQjm391c7J3wQcDOTCwDvvi1X2PYpAhw/ObHdQmWNY07hw Gv/g== X-Gm-Message-State: AOAM533PBrOM+VciqN/yOuBW7TcNOEdcqt+qEnhRok7pfyNOpPUkW/Qk GuOd6v5kgS9zVcICJWBdtZYXAT3102wI+Ku8df7FPkFDv86Xc7Ule0ykuYE5gG7K7FNMVZMb0Lh VRZep72H2MUKAGgXoAazHMQwDPSCuusqiJcLDQLbZmsHEr8rcjnN8wt1S6oDjxkKl81Bym76owD o= X-Received: by 2002:aa7:c04e:: with SMTP id k14mr38493241edo.157.1625762427013; Thu, 08 Jul 2021 09:40:27 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxmFG8z+NohqF71EyjTbs/OQUOH3qriYTQN3VhXRynC1pJdvtvHl4kTKJNWAml7dzMMXFTZgA== X-Received: by 2002:aa7:c04e:: with SMTP id k14mr38493227edo.157.1625762426887; Thu, 08 Jul 2021 09:40:26 -0700 (PDT) Received: from lore-desk.redhat.com (net-93-71-3-244.cust.vodafonedsl.it. [93.71.3.244]) by smtp.gmail.com with ESMTPSA id ee25sm1568103edb.6.2021.07.08.09.40.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jul 2021 09:40:26 -0700 (PDT) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Thu, 8 Jul 2021 18:40:05 +0200 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=lorenzo.bianconi@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Cc: dceara@redhat.com Subject: [ovs-dev] [PATCH v6 ovn 4/4] NEWS: Add CoPP support. 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" From: Dumitru Ceara Acked-by: Mark D. Gray Co-authored-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Dumitru Ceara --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index e77989208..7b801fcd5 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,7 @@ OVN v21.06.0 - 18 Jun 2021 * ovn-sbctl now also supports daemon mode. - Added support in native DNS to respond to PTR request types. - New --dry-run option for ovn-northd and ovn-northd-ddlog. + - Added Control Plane Protection support (control plane traffic metering). OVN v21.03.0 - 12 Mar 2021 -------------------------