From patchwork Fri Jul 23 15:10:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lorenzo Bianconi X-Patchwork-Id: 1509197 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.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=doqrlDJv; dkim-atps=neutral Received: from smtp4.osuosl.org (smtp4.osuosl.org [140.211.166.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 4GWXpV0SzTz9sS8 for ; Sat, 24 Jul 2021 01:11:30 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 4E0754147F; Fri, 23 Jul 2021 15:11:28 +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 NVAzCumH5V_R; Fri, 23 Jul 2021 15:11:25 +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 4E706406A6; Fri, 23 Jul 2021 15:11:24 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 1A23DC001C; Fri, 23 Jul 2021 15:11:24 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 169FFC000E for ; Fri, 23 Jul 2021 15:11:22 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 58EA160607 for ; Fri, 23 Jul 2021 15:11:01 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Authentication-Results: smtp3.osuosl.org (amavisd-new); dkim=fail (1024-bit key) reason="fail (body has been altered)" header.d=redhat.com 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 UJ0scsZkaelD for ; Fri, 23 Jul 2021 15:10:58 +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 smtp3.osuosl.org (Postfix) with ESMTPS id 8F82660A59 for ; Fri, 23 Jul 2021 15:10:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1627053057; 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=AQOvK2fuwjHUCU8prFOEKaK8tjGhilGfau4NRH/V5ZQ=; b=doqrlDJvv1sVZ9UNu7x6Hcy+K6/mwcWCOaG3Sr1YSFOdkHeeHq2UpypxisFHX4Wua/Gt2i sIcrQ719n2dj1moWQbO0+kkE38hDERf6z3j4PdaMvle8A83zbGNUDNUn2tDrC/s4dkpbI0 +LIuAp3Uca+hr21/CY5IKRHlSP05o34= 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-582-Ebw5C6suNg68G7CkLRXtpA-1; Fri, 23 Jul 2021 11:10:55 -0400 X-MC-Unique: Ebw5C6suNg68G7CkLRXtpA-1 Received: by mail-ed1-f71.google.com with SMTP id f24-20020a0564021618b02903954c05c938so905641edv.3 for ; Fri, 23 Jul 2021 08:10:54 -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=KxFGI/ZP7IWss55VK3ojNihXo2+pJrfc6dFMXo0pn8U=; b=Ipzg71Tip1Ca1Wb4QfiVJIufrET8Yp0ITPO3M0SocC0pA8VtBuBg1bvmfXb8dU+G8p vx9YVPcQiHS2wmUbJ1qHZwdo1TZ1+ZomWFAR8cYlRUxS04bfXksjVbN7IElO1zEiMmpN PtctGx6RNLJwTiSqOvbCtQ+cNDUv5QqR0fw9656V+Qij9eVlbk31dYRPpahjfaDhf9Vw 3RfDYEofkvM4RUiOAt0kNtE93oXFU5wczkZZFCl8mmBX2ViAYnNvmn++P7t54z0/yjoK FAg/z5JyQe/NLdhxphC0mKXh2+7j3Y1bkz/2p6IQNPwbfKYbLULgUQIm5UdeZTQeTEV9 +GAw== X-Gm-Message-State: AOAM531TezEeHIZ3bJFel96DqLL/PqgGFEtsIxl1Bqb2LgA9+KCt6h+2 FB6tidU2KzS7h6r0XrZ+K5K74Rv6AdfvzKNb5mq6JPOPBdaubHM/uS/2JN7q9YqVJAS9tLQdI6D yjiwMHQtS+JD3pQvmp4hEd+oI8aG0mcpMDPGmtNcJtcozTfgeXFqwYVfP1Js5ahUxq1Un8gMBR9 E= X-Received: by 2002:a17:907:d09:: with SMTP id gn9mr4962899ejc.447.1627053047760; Fri, 23 Jul 2021 08:10:47 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxFLdLM52Th7oG710WKWlsEdRVFnWB0PnJbHVljHcpcxy4OEMaX1zNPinLvYvYCExr4DOenlQ== X-Received: by 2002:a17:907:d09:: with SMTP id gn9mr4962480ejc.447.1627053040790; Fri, 23 Jul 2021 08:10:40 -0700 (PDT) Received: from lore-desk.redhat.com (net-130-25-106-225.cust.vodafonedsl.it. [130.25.106.225]) by smtp.gmail.com with ESMTPSA id m12sm10852596ejd.21.2021.07.23.08.10.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 23 Jul 2021 08:10:40 -0700 (PDT) From: Lorenzo Bianconi To: dev@openvswitch.org Date: Fri, 23 Jul 2021 17:10:28 +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 ovn v8 4/5] 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 Co-authored-by: Ben Pfaff Signed-off-by: Ben Pfaff Signed-off-by: Dumitru Ceara --- include/ovn/actions.h | 1 - lib/actions.c | 50 +-- lib/copp.c | 1 + lib/copp.h | 1 + northd/automake.mk | 3 +- northd/copp.dl | 30 ++ northd/lrouter.dl | 22 +- northd/lswitch.dl | 20 + northd/ovn-northd.c | 528 +++++++++++++++++---------- northd/ovn_northd.dl | 745 +++++++++++++++++++++----------------- ovn-nb.xml | 3 + tests/atlocal.in | 3 + tests/ovn.at | 6 +- tests/system-ovn.at | 135 +++++++ utilities/ovn-nbctl.8.xml | 3 + 15 files changed, 981 insertions(+), 570 deletions(-) create mode 100644 northd/copp.dl 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/automake.mk b/northd/automake.mk index 4fc81c17b..6da54deb8 100644 --- a/northd/automake.mk +++ b/northd/automake.mk @@ -30,7 +30,8 @@ ddlog_sources = \ northd/ovn.dl \ northd/ovn.rs \ northd/helpers.dl \ - northd/bitwise.dl + northd/bitwise.dl \ + northd/copp.dl ddlog_nodist_sources = \ northd/OVN_Northbound.dl \ northd/OVN_Southbound.dl diff --git a/northd/copp.dl b/northd/copp.dl new file mode 100644 index 000000000..ffb9fb32e --- /dev/null +++ b/northd/copp.dl @@ -0,0 +1,30 @@ +/* + * 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. + */ + +function cOPP_ARP() : string { "arp" } +function cOPP_ARP_RESOLVE() : string { "arp-resolve" } +function cOPP_DHCPV4_OPTS() : string { "dhcpv4-opts" } +function cOPP_DHCPV6_OPTS() : string { "dhcpv6-opts" } +function cOPP_DNS() : string { "dns" } +function cOPP_EVENT_ELB() : string { "event-elb" } +function cOPP_ICMP4_ERR() : string { "icmp4-error" } +function cOPP_ICMP6_ERR() : string { "icmp6-error" } +function cOPP_IGMP() : string { "igmp" } +function cOPP_ND_NA() : string { "nd-na" } +function cOPP_ND_NS() : string { "nd-ns" } +function cOPP_ND_NS_RESOLVE() : string { "nd-ns-resolve" } +function cOPP_ND_RA_OPTS() : string { "nd-ra-opts" } +function cOPP_TCP_RESET() : string { "tcp-reset" } +function cOPP_REJECT() : string { "reject" } +function cOPP_BFD() : string { "bfd" } diff --git a/northd/lrouter.dl b/northd/lrouter.dl index 85c07716b..4a24f3f61 100644 --- a/northd/lrouter.dl +++ b/northd/lrouter.dl @@ -431,6 +431,23 @@ LogicalRouterLBs(lr, vec_empty()) :- nb::Logical_Router(._uuid = lr), not LogicalRouterLB(lr, _). +// LogicalRouterCopp maps from each LR to its collection of Copp meters, +// dropping any Copp meter whose meter name doesn't exist. +relation LogicalRouterCopp(lr: uuid, meters: Map) +LogicalRouterCopp(lr, meters) :- LogicalRouterCopp0(lr, meters). +LogicalRouterCopp(lr, map_empty()) :- + nb::Logical_Router(._uuid = lr), + not LogicalRouterCopp0(lr, _). + +relation LogicalRouterCopp0(lr: uuid, meters: Map) +LogicalRouterCopp0(lr, meters) :- + nb::Logical_Router(._uuid = lr, .copp = Some{copp_uuid}), + nb::Copp(._uuid = copp_uuid, .meters = meters), + var entry = FlatMap(meters), + (var copp_id, var meter_name) = entry, + &nb::Meter(.name = meter_name), + var meters = (copp_id, meter_name).group_by(lr).to_map(). + /* Router relation collects all attributes of a logical router. * * `l3dgw_port` - optional redirect port (see `DistributedGatewayPort`) @@ -466,6 +483,7 @@ typedef Router = Router { mcast_cfg: Intern, learn_from_arp_request: bool, force_lb_snat: bool, + copp: Map, } relation Router[Intern] @@ -492,13 +510,15 @@ Router[Router{ .lbs = lbs, .mcast_cfg = mcast_cfg, .learn_from_arp_request = learn_from_arp_request, - .force_lb_snat = force_lb_snat}.intern()] :- + .force_lb_snat = force_lb_snat, + .copp = copp}.intern()] :- lr in nb::Logical_Router(), lr.is_enabled(), LogicalRouterRedirectPort(lr._uuid, l3dgw_port), LogicalRouterNATs(lr._uuid, nats), LogicalRouterLBs(lr._uuid, lbs), LogicalRouterSnatIPs(lr._uuid, snat_ips), + LogicalRouterCopp(lr._uuid, copp), mcast_cfg in &McastRouterCfg(.datapath = lr._uuid), var learn_from_arp_request = lr.options.get_bool_def("always_learn_from_arp_request", true), var force_lb_snat = lb_force_snat_router_ip(lr.options). diff --git a/northd/lswitch.dl b/northd/lswitch.dl index 402df48ef..7e7b62a4d 100644 --- a/northd/lswitch.dl +++ b/northd/lswitch.dl @@ -202,6 +202,23 @@ LogicalSwitchHasNonRouterPort(ls, false) :- nb::Logical_Switch(._uuid = ls), not LogicalSwitchHasNonRouterPort0(ls). +// LogicalSwitchCopp maps from each LS to its collection of Copp meters, +// dropping any Copp meter whose meter name doesn't exist. +relation LogicalSwitchCopp(ls: uuid, meters: Map) +LogicalSwitchCopp(ls, meters) :- LogicalSwitchCopp0(ls, meters). +LogicalSwitchCopp(ls, map_empty()) :- + nb::Logical_Switch(._uuid = ls), + not LogicalSwitchCopp0(ls, _). + +relation LogicalSwitchCopp0(ls: uuid, meters: Map) +LogicalSwitchCopp0(ls, meters) :- + nb::Logical_Switch(._uuid = ls, .copp = Some{copp_uuid}), + nb::Copp(._uuid = copp_uuid, .meters = meters), + var entry = FlatMap(meters), + (var copp_id, var meter_name) = entry, + &nb::Meter(.name = meter_name), + var meters = (copp_id, meter_name).group_by(ls).to_map(). + /* Switch relation collects all attributes of a logical switch */ typedef Switch = Switch { @@ -223,6 +240,7 @@ typedef Switch = Switch { ipv6_prefix: Option, mcast_cfg: Intern, is_vlan_transparent: bool, + copp: Map, /* Does this switch have at least one port with type != "router"? */ has_non_router_port: bool @@ -259,6 +277,7 @@ Switch[Switch{ .ipv6_prefix = ipv6_prefix, .mcast_cfg = mcast_cfg, .has_non_router_port = has_non_router_port, + .copp = copp, .is_vlan_transparent = is_vlan_transparent }.intern()] :- nb::Logical_Switch[ls], @@ -269,6 +288,7 @@ Switch[Switch{ LogicalSwitchHasUnknownPorts(ls._uuid, has_unknown_ports), LogicalSwitchLocalnetPorts(ls._uuid, localnet_ports), LogicalSwitchHasNonRouterPort(ls._uuid, has_non_router_port), + LogicalSwitchCopp(ls._uuid, copp), mcast_cfg in &McastSwitchCfg(.datapath = ls._uuid), var subnet = match (ls.other_config.get("subnet")) { diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 214596610..150f01e02 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -3460,11 +3460,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; @@ -3516,6 +3516,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 @@ -4341,9 +4342,10 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od, /* 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) \ + ACTIONS, IN_OUT_PORT, CTRL_METER, \ + STAGE_HINT) \ ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \ - CTRL_METER, STAGE_HINT, OVS_SOURCE_LOCATOR) + IN_OUT_PORT, CTRL_METER, STAGE_HINT, OVS_SOURCE_LOCATOR) #define ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \ ACTIONS, STAGE_HINT) \ @@ -4370,11 +4372,10 @@ 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, 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) - + ACTIONS, NULL, CTRL_METER, NULL) static struct ovn_lflow * ovn_lflow_find(const struct hmap *lflows, const struct ovn_datapath *od, @@ -5330,7 +5331,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) || @@ -5344,11 +5344,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); @@ -5363,11 +5358,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); @@ -5723,10 +5718,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), NULL, + copp_meter_get(COPP_REJECT, od->nbs->copp, + meter_groups), + stage_hint); free(next_action); ds_destroy(&match); @@ -6223,7 +6220,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]; @@ -6265,8 +6263,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); @@ -6276,9 +6275,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), NULL, meter, + &lb->nlb->header_); } } } @@ -6883,6 +6889,7 @@ static void build_dhcpv4_options_flows(struct ovn_port *op, struct lport_addresses *lsp_addrs, struct ovn_port *inport, bool is_external, + struct shash *meter_groups, struct hmap *lflows) { struct ds match = DS_EMPTY_INITIALIZER; @@ -6906,10 +6913,15 @@ build_dhcpv4_options_flows(struct ovn_port *op, op->json_key); } - ovn_lflow_add_with_lport_and_hint( - lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100, ds_cstr(&match), - ds_cstr(&options_action), inport->key, - &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), + inport->key, + 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 @@ -6929,10 +6941,15 @@ build_dhcpv4_options_flows(struct ovn_port *op, op->json_key); } - ovn_lflow_add_with_lport_and_hint( - lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100, ds_cstr(&match), - ds_cstr(&options_action), inport->key, - &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), + inport->key, + 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 @@ -6965,6 +6982,7 @@ static void build_dhcpv6_options_flows(struct ovn_port *op, struct lport_addresses *lsp_addrs, struct ovn_port *inport, bool is_external, + struct shash *meter_groups, struct hmap *lflows) { struct ds match = DS_EMPTY_INITIALIZER; @@ -6987,10 +7005,15 @@ build_dhcpv6_options_flows(struct ovn_port *op, op->json_key); } - ovn_lflow_add_with_lport_and_hint( - lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100, ds_cstr(&match), - ds_cstr(&options_action), inport->key, - &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), + inport->key, + 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 */ @@ -7181,6 +7204,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) { @@ -7328,11 +7352,15 @@ 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), + NULL, + 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). */ @@ -7453,7 +7481,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)) { @@ -7482,17 +7511,19 @@ build_lswitch_dhcp_options_and_response(struct ovn_port *op, build_dhcpv4_options_flows( op, &op->lsp_addrs[i], op->od->localnet_ports[j], is_external, - lflows); + meter_groups, lflows); build_dhcpv6_options_flows( op, &op->lsp_addrs[i], op->od->localnet_ports[j], is_external, - lflows); + meter_groups, lflows); } } else { build_dhcpv4_options_flows(op, &op->lsp_addrs[i], op, - is_external, lflows); + is_external, meter_groups, + lflows); build_dhcpv6_options_flows(op, &op->lsp_addrs[i], op, - is_external, lflows); + is_external, meter_groups, + lflows); } } } @@ -7522,13 +7553,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;"; @@ -7565,7 +7598,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) { @@ -7586,12 +7620,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_metered(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_metered(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. @@ -8983,7 +9021,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; @@ -8993,8 +9032,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. @@ -9075,6 +9114,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. @@ -9110,18 +9154,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, NULL, + 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, NULL, + meter, &lb->nlb->header_); free(new_actions); est_actions = xasprintf("flags.force_snat_for_lb = 1; " @@ -9130,9 +9174,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), NULL, + meter, &lb->nlb->header_); ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, prio, est_match_p, "next;", &lb->nlb->header_); @@ -9194,14 +9238,19 @@ 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), + NULL, + 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 @@ -9215,7 +9264,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); } /* If there are any load balancing rules, we should send the packet to @@ -9281,16 +9330,21 @@ 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), + NULL, + copp_meter_get(COPP_EVENT_ELB, + od->nbr->copp, + meter_groups), + &lb->nlb->header_); } } @@ -9532,7 +9586,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; @@ -9554,6 +9608,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 { " @@ -9570,11 +9626,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), NULL, + 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); } @@ -9582,7 +9640,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; @@ -9592,7 +9651,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, @@ -9604,7 +9663,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; @@ -9647,12 +9707,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, @@ -9825,7 +9885,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; @@ -9844,9 +9905,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(); ", NULL, + 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); @@ -9861,9 +9924,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(); ", NULL, + copp_meter_get(COPP_BFD, op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } ds_destroy(&ip_list); @@ -9943,7 +10008,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) { @@ -10023,14 +10089,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)); } } @@ -10105,7 +10177,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; @@ -10198,9 +10271,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), NULL, + 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 && " @@ -10853,7 +10929,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) { @@ -10915,10 +10992,15 @@ 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), + NULL, + copp_meter_get( + COPP_ICMP4_ERR, + rp->od->nbr->copp, + meter_groups), + &rp->nbrp->header_); } if (rp->lrp_networks.ipv6_addrs) { @@ -10944,10 +11026,15 @@ 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), + NULL, + copp_meter_get( + COPP_ICMP6_ERR, + rp->od->nbr->copp, + meter_groups), + &rp->nbrp->header_); } } } @@ -11002,7 +11089,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++) { @@ -11039,26 +11127,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), NULL, + 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;"); } } @@ -11189,7 +11284,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 @@ -11232,7 +11328,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 */ @@ -11246,9 +11342,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, NULL, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11258,9 +11358,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, NULL, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11273,9 +11377,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, NULL, + copp_meter_get( + COPP_ICMP6_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11288,9 +11396,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, NULL, + copp_meter_get( + COPP_ICMP6_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } } @@ -11321,9 +11433,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), NULL, + copp_meter_get(COPP_ICMP6_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } } @@ -11331,7 +11446,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) { @@ -11357,7 +11473,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. */ @@ -11372,7 +11488,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); } } } @@ -11381,7 +11497,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. */ @@ -11419,7 +11536,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++) { @@ -11439,9 +11556,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), NULL, + 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 @@ -11518,7 +11638,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 (!op->od->is_gw_router && !op->od->l3dgw_port) { @@ -11535,9 +11656,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, NULL, + copp_meter_get( + COPP_ICMP4_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11547,9 +11672,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, NULL, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11559,9 +11688,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, NULL, + copp_meter_get( + COPP_TCP_RESET, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); ds_clear(match); ds_put_format(match, @@ -11574,9 +11707,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, NULL, + copp_meter_get( + COPP_ICMP4_ERR, + op->od->nbr->copp, + meter_groups), + &op->nbrp->header_); } } @@ -11620,7 +11757,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. */ @@ -11635,7 +11773,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); } } } @@ -12318,15 +12457,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); @@ -12335,13 +12475,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->ports, &lsi->match, &lsi->actions); } @@ -12361,9 +12502,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); @@ -12377,16 +12520,17 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op, &lsi->actions); build_ip_routing_flows_for_lrouter_port(op, lsi->ports, 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/northd/ovn_northd.dl b/northd/ovn_northd.dl index 25a1e1fdb..7bfaae992 100644 --- a/northd/ovn_northd.dl +++ b/northd/ovn_northd.dl @@ -14,6 +14,7 @@ import OVN_Northbound as nb import OVN_Southbound as sb +import copp import ovsdb import allocate import ovn @@ -1626,6 +1627,23 @@ function mFF_N_LOG_REGS() : bit<32> = 10 * enables or disables this feature. */ +// A Flow including a 'controller_meter' column for metering. +// +// Most flows have an empty 'controller_meter' so we provide a Flow relation +// that provides it as empty without specifying it explicitly. +relation MeteredFlow( + logical_datapath: uuid, + stage: Stage, + priority: integer, + __match: string, + actions: string, + tags: Map, + controller_meter: Option, + external_ids: Map +) +MeteredFlow(logical_datapath, stage, priority, __match, actions, tags, None, external_ids) :- + TaggedFlow(logical_datapath, stage, priority, __match, actions, tags, external_ids). + relation Flow( logical_datapath: uuid, stage: Stage, @@ -1680,6 +1698,7 @@ relation AggregatedFlow ( __match: string, actions: string, tags: Map, + controller_meter: Option, external_ids: Map ) AggregatedFlow(.logical_datapaths = g.to_set(), @@ -1688,19 +1707,21 @@ AggregatedFlow(.logical_datapaths = g.to_set(), .__match = __match, .actions = actions, .tags = tags, + .controller_meter = controller_meter, .external_ids = external_ids) :- UseLogicalDatapathGroups[true], - TaggedFlow(logical_datapath, stage, priority, __match, actions, tags, external_ids), - var g = logical_datapath.group_by((stage, priority, __match, actions, tags, external_ids)). + MeteredFlow(logical_datapath, stage, priority, __match, actions, tags, controller_meter, external_ids), + var g = logical_datapath.group_by((stage, priority, __match, actions, tags, controller_meter, external_ids)). AggregatedFlow(.logical_datapaths = set_singleton(logical_datapath), .stage = stage, .priority = priority, .__match = __match, .actions = actions, .tags = tags, + .controller_meter = controller_meter, .external_ids = external_ids) :- UseLogicalDatapathGroups[false], - TaggedFlow(logical_datapath, stage, priority, __match, actions, tags, external_ids). + MeteredFlow(logical_datapath, stage, priority, __match, actions, tags, controller_meter, external_ids). for (f in AggregatedFlow()) { var pipeline = if (f.stage.pipeline == Ingress) "ingress" else "egress" in @@ -1708,13 +1729,13 @@ for (f in AggregatedFlow()) { if (f.logical_datapaths.size() == 1) { Some{var dp} = f.logical_datapaths.nth(0) in sb::Out_Logical_Flow( - ._uuid = hash128((dp, f.stage, f.priority, f.__match, f.actions, f.external_ids)), + ._uuid = hash128((dp, f.stage, f.priority, f.__match, f.actions, f.controller_meter, f.external_ids)), .logical_datapath = Some{dp}, .logical_dp_group = None, .pipeline = pipeline, .table_id = f.stage.table_id, .priority = f.priority, - .controller_meter = None, + .controller_meter = f.controller_meter, .__match = f.__match, .actions = f.actions, .tags = f.tags, @@ -1722,13 +1743,13 @@ for (f in AggregatedFlow()) { } else { var group_uuid = hash128(f.logical_datapaths) in { sb::Out_Logical_Flow( - ._uuid = hash128((group_uuid, f.stage, f.priority, f.__match, f.actions, f.external_ids)), + ._uuid = hash128((group_uuid, f.stage, f.priority, f.__match, f.actions, f.controller_meter, f.external_ids)), .logical_datapath = None, .logical_dp_group = Some{group_uuid}, .pipeline = pipeline, .table_id = f.stage.table_id, .priority = f.priority, - .controller_meter = None, + .controller_meter = f.controller_meter, .__match = f.__match, .actions = f.actions, .tags = f.tags, @@ -2076,18 +2097,8 @@ if (lsp.__type == "router" or lsp.__type == "localnet") { lsp.name) } -relation HasEventElbMeter(has_meter: bool) - -HasEventElbMeter(true) :- - &nb::Meter(.name = "event-elb"). - -HasEventElbMeter(false) :- - Unit(), - not &nb::Meter(.name = "event-elb"). - /* Empty LoadBalancer Controller event */ -function build_empty_lb_event_flow(key: string, lb: Intern, - meter: bool): Option<(string, string)> { +function build_empty_lb_event_flow(key: string, lb: Intern): Option<(string, string)> { (var ip, var port) = match (ip_address_and_port_from_lb_key(key)) { Some{(ip, port)} -> (ip, port), _ -> return None @@ -2097,10 +2108,6 @@ function build_empty_lb_event_flow(key: string, lb: Intern, Some{"tcp"} -> "tcp", _ -> "udp" }; - var meter = match (meter) { - true -> "event-elb", - _ -> "" - }; var vip = match (port) { 0 -> "${ip}", _ -> "${ip.to_bracketed_string()}:${port}" @@ -2114,7 +2121,6 @@ function build_empty_lb_event_flow(key: string, lb: Intern, var action = "trigger_event(" "event = \"empty_lb_backends\", " - "meter = \"${meter}\", " "vip = \"${vip}\", " "protocol = \"${protocol}\", " "load_balancer = \"${uuid2str(lb._uuid)}\");"; @@ -2139,20 +2145,20 @@ LoadBalancerEmptyEvents(lb) :- var local_events = local_options.get_bool_def("event", false), global_events or local_events. -Flow(.logical_datapath = sw._uuid, - .stage = s_SWITCH_IN_PRE_LB(), - .priority = 130, - .__match = __match, - .actions = __action, - .external_ids = stage_hint(lb._uuid)) :- +MeteredFlow(.logical_datapath = sw._uuid, + .stage = s_SWITCH_IN_PRE_LB(), + .priority = 130, + .__match = __match, + .actions = __action, + .tags = map_empty(), + .controller_meter = sw.copp.get(cOPP_EVENT_ELB()), + .external_ids = stage_hint(lb._uuid)) :- SwitchLBVIP(.sw_uuid = sw_uuid, .lb = lb, .vip = vip, .backends = backends), LoadBalancerEmptyEvents(lb), not lb.options.get_bool_def("reject", false), sw in &Switch(._uuid = sw_uuid), backends == "", - HasEventElbMeter(has_elb_meter), - Some {(var __match, var __action)} = build_empty_lb_event_flow( - vip, lb, has_elb_meter). + Some {(var __match, var __action)} = build_empty_lb_event_flow(vip, lb). /* 'REGBIT_CONNTRACK_NAT' is set to let the pre-stateful table send * packet to conntrack for defragmentation. @@ -2343,6 +2349,7 @@ relation Reject( stage: Stage, acl: Intern, fair_meter: bool, + controller_meter: Option, extra_match: string, extra_actions: string) @@ -2354,7 +2361,8 @@ function next_to_stage(stage: Stage): string { }; "next(pipeline=${pipeline},table=${stage.table_id})" } -for (Reject(lsuuid, pipeline, stage, acl, fair_meter, extra_match_, extra_actions_)) { +for (Reject(lsuuid, pipeline, stage, acl, fair_meter, controller_meter, + extra_match_, extra_actions_)) { var extra_match = match (extra_match_) { "" -> "", s -> "(${s}) && " @@ -2373,12 +2381,14 @@ for (Reject(lsuuid, pipeline, stage, acl, fair_meter, extra_match_, extra_action "reject { " "/* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ " "outport <-> inport; ${next_to_stage(next_stage)}; };" in - Flow(.logical_datapath = lsuuid, - .stage = stage, - .priority = acl.priority + oVN_ACL_PRI_OFFSET(), - .__match = __match, - .actions = actions, - .external_ids = stage_hint(acl._uuid)) + MeteredFlow(.logical_datapath = lsuuid, + .stage = stage, + .priority = acl.priority + oVN_ACL_PRI_OFFSET(), + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = controller_meter, + .external_ids = stage_hint(acl._uuid)) } /* build_acls */ @@ -2719,12 +2729,13 @@ for (&SwitchACL(.sw = sw, .acl = acl, .has_fair_meter = fair_meter)) { * use for this datapath. In that case, the actions differ * depending on whether the connection was previously committed * to the connection tracker with ct_commit. */ + var controller_meter = sw.copp.get(cOPP_REJECT()) in if (has_stateful) { /* If the packet is not tracked or not part of an established * connection, then we can simply reject/drop it. */ var __match = "${rEGBIT_ACL_HINT_DROP()} == 1" in if (acl.action == "reject") { - Reject(sw._uuid, pipeline, stage, acl, fair_meter, __match, "") + Reject(sw._uuid, pipeline, stage, acl, fair_meter, controller_meter, __match, "") } else { Flow(.logical_datapath = sw._uuid, .stage = stage, @@ -2747,7 +2758,7 @@ for (&SwitchACL(.sw = sw, .acl = acl, .has_fair_meter = fair_meter)) { var __match = "${rEGBIT_ACL_HINT_BLOCK()} == 1" in var actions = "ct_commit { ct_label.blocked = 1; }; " in if (acl.action == "reject") { - Reject(sw._uuid, pipeline, stage, acl, fair_meter, __match, actions) + Reject(sw._uuid, pipeline, stage, acl, fair_meter, controller_meter, __match, actions) } else { Flow(.logical_datapath = sw._uuid, .stage = stage, @@ -2761,7 +2772,7 @@ for (&SwitchACL(.sw = sw, .acl = acl, .has_fair_meter = fair_meter)) { * so a "reject/drop" ACL is simply the "reject/drop" * logical flow action in all cases. */ if (acl.action == "reject") { - Reject(sw._uuid, pipeline, stage, acl, fair_meter, "", "") + Reject(sw._uuid, pipeline, stage, acl, fair_meter, controller_meter, "", "") } else { Flow(.logical_datapath = sw._uuid, .stage = stage, @@ -3007,7 +3018,7 @@ function ct_lb(backends: string, } function build_lb_vip_actions(lbvip: Intern, stage: Stage, - actions0: string): string { + actions0: string): (string, bool) { var up_backends = set_empty(); for (pair in lbvip.backends) { (var backend, var up) = pair; @@ -3022,27 +3033,29 @@ function build_lb_vip_actions(lbvip: Intern, if (up_backends.is_empty()) { if (lbvip.lb.options.get_bool_def("reject", false)) { - return "reg0 = 0; reject { outport <-> inport; ${next_to_stage(stage)};};" + return ("reg0 = 0; reject { outport <-> inport; ${next_to_stage(stage)};};", true) } else if (lbvip.health_check.is_some()) { - return "drop;" + return ("drop;", false) } // else fall through }; var actions = ct_lb(up_backends.to_vec().join(","), lbvip.lb.selection_fields, lbvip.lb.protocol); - actions0 ++ actions + (actions0 ++ actions, false) } -Flow(.logical_datapath = sw._uuid, - .stage = s_SWITCH_IN_STATEFUL(), - .priority = priority, - .__match = __match, - .actions = actions, - .external_ids = stage_hint(lb._uuid)) :- +MeteredFlow(.logical_datapath = sw._uuid, + .stage = s_SWITCH_IN_STATEFUL(), + .priority = priority, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = meter, + .external_ids = stage_hint(lb._uuid)) :- sw in &Switch(), LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}], sw.load_balancer.contains(lb._uuid), var priority = if (lbvip.vip_port != 0) { 120 } else { 110 }, - var actions = { + (var actions, var reject) = { /* Store the original destination IP to be used when generating * hairpin flows. */ @@ -3062,6 +3075,11 @@ Flow(.logical_datapath = sw._uuid, build_lb_vip_actions(lbvip, s_SWITCH_OUT_QOS_MARK(), actions0 ++ actions1) }, + var meter = if (reject) { + sw.copp.get(cOPP_REJECT()) + } else { + None + }, var __match = "ct.new && " ++ get_match_for_lb_key(lbvip.vip_addr, lbvip.vip_port, lb.protocol, false, false). /* Ingress Pre-Hairpin/Nat-Hairpin/Hairpin tabled (Priority 0). @@ -3515,12 +3533,14 @@ for (SwitchPortIPv6Address(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam "output; " "};" in { - Flow(.logical_datapath = sw._uuid, - .stage = s_SWITCH_IN_ARP_ND_RSP(), - .priority = 50, - .__match = __match, - .actions = actions, - .external_ids = stage_hint(lsp._uuid)); + MeteredFlow(.logical_datapath = sw._uuid, + .stage = s_SWITCH_IN_ARP_ND_RSP(), + .priority = 50, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = sw.copp.get(cOPP_ND_NA()), + .external_ids = stage_hint(lsp._uuid)); /* Do not reply to a solicitation from the port that owns the * address (otherwise DAD detection will fail). */ @@ -3774,12 +3794,14 @@ for (lsp in &SwitchPort "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " "udp.src == 68 && udp.dst == 67" ++ sfx in - Flow(.logical_datapath = lsuuid, - .stage = s_SWITCH_IN_DHCP_OPTIONS(), - .priority = 100, - .__match = __match, - .actions = options_action, - .external_ids = stage_hint(lsp.lsp._uuid)); + MeteredFlow(.logical_datapath = lsuuid, + .stage = s_SWITCH_IN_DHCP_OPTIONS(), + .priority = 100, + .__match = __match, + .actions = options_action, + .tags = map_empty(), + .controller_meter = lsp.sw.copp.get(cOPP_DHCPV4_OPTS()), + .external_ids = stage_hint(lsp.lsp._uuid)); /* Allow ip4.src = OFFER_IP and * ip4.dst = {SERVER_IP, 255.255.255.255} for the below @@ -3791,12 +3813,14 @@ for (lsp in &SwitchPort */ var __match = pfx ++ "eth.src == ${ea} && " "${ipv4_addr_match} && udp.src == 68 && udp.dst == 67" ++ sfx in - Flow(.logical_datapath = lsuuid, - .stage = s_SWITCH_IN_DHCP_OPTIONS(), - .priority = 100, - .__match = __match, - .actions = options_action, - .external_ids = stage_hint(lsp.lsp._uuid)); + MeteredFlow(.logical_datapath = lsuuid, + .stage = s_SWITCH_IN_DHCP_OPTIONS(), + .priority = 100, + .__match = __match, + .actions = options_action, + .tags = map_empty(), + .controller_meter = lsp.sw.copp.get(cOPP_DHCPV4_OPTS()), + .external_ids = stage_hint(lsp.lsp._uuid)); /* If REGBIT_DHCP_OPTS_RESULT is set, it means the * put_dhcp_opts action is successful. */ @@ -3830,12 +3854,14 @@ for (lsp in &SwitchPort " && ip6.dst == ff02::1:2 && udp.src == 546 &&" " udp.dst == 547" ++ sfx in { - Flow(.logical_datapath = lsuuid, - .stage = s_SWITCH_IN_DHCP_OPTIONS(), - .priority = 100, - .__match = __match, - .actions = options_action, - .external_ids = stage_hint(lsp.lsp._uuid)); + MeteredFlow(.logical_datapath = lsuuid, + .stage = s_SWITCH_IN_DHCP_OPTIONS(), + .priority = 100, + .__match = __match, + .actions = options_action, + .tags = map_empty(), + .controller_meter = lsp.sw.copp.get(cOPP_DHCPV6_OPTS()), + .external_ids = stage_hint(lsp.lsp._uuid)); /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the * put_dhcpv6_opts action is successful */ @@ -3946,6 +3972,7 @@ Flow(.logical_datapath = sw._uuid, for (sw in &Switch(._uuid = ls_uuid, .mcast_cfg = mcast_cfg) if (mcast_cfg.enabled)) { + var controller_meter = sw.copp.get(cOPP_IGMP()) in for (SwitchMcastFloodRelayPorts(sw, relay_ports)) { for (SwitchMcastFloodReportPorts(sw, flood_report_ports)) { for (SwitchMcastFloodPorts(sw, flood_ports)) { @@ -3964,20 +3991,24 @@ for (sw in &Switch(._uuid = ls_uuid, .mcast_cfg = mcast_cfg) } } in { /* Punt IGMP traffic to controller. */ - Flow(.logical_datapath = ls_uuid, - .stage = s_SWITCH_IN_L2_LKUP(), - .priority = 100, - .__match = "ip4 && ip.proto == 2", - .actions = "${igmp_act}", - .external_ids = map_empty()); + MeteredFlow(.logical_datapath = ls_uuid, + .stage = s_SWITCH_IN_L2_LKUP(), + .priority = 100, + .__match = "ip4 && ip.proto == 2", + .actions = "${igmp_act}", + .tags = map_empty(), + .controller_meter = controller_meter, + .external_ids = map_empty()); /* Punt MLD traffic to controller. */ - Flow(.logical_datapath = ls_uuid, - .stage = s_SWITCH_IN_L2_LKUP(), - .priority = 100, - .__match = "mldv1 || mldv2", - .actions = "${igmp_act}", - .external_ids = map_empty()); + MeteredFlow(.logical_datapath = ls_uuid, + .stage = s_SWITCH_IN_L2_LKUP(), + .priority = 100, + .__match = "mldv1 || mldv2", + .actions = "${igmp_act}", + .tags = map_empty(), + .controller_meter = controller_meter, + .external_ids = map_empty()); /* Flood all IP multicast traffic destined to 224.0.0.X to * all ports - RFC 4541, section 2.1.2, item 2. @@ -4759,7 +4790,9 @@ for (&RouterPort(.lrp = lrp, * */ /* Flows for LOOKUP_NEIGHBOR. */ -for (&Router(._uuid = lr_uuid, .learn_from_arp_request = learn_from_arp_request)) +for (&Router(._uuid = lr_uuid, + .learn_from_arp_request = learn_from_arp_request, + .copp = copp)) var rLNR = rEGBIT_LOOKUP_NEIGHBOR_RESULT() in var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in { @@ -4811,30 +4844,30 @@ var rLNIR = rEGBIT_LOOKUP_NEIGHBOR_IP_RESULT() in { if (learn_from_arp_request) "" else " || ${rLNIR} == 0" }, .actions = "next;", .external_ids = map_empty()); - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), - .priority = 90, - .__match = "arp", - .actions = "put_arp(inport, arp.spa, arp.sha); next;", - .external_ids = map_empty()); - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), - .priority = 90, - .__match = "arp", - .actions = "put_arp(inport, arp.spa, arp.sha); next;", - .external_ids = map_empty()); - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), - .priority = 90, - .__match = "nd_na", - .actions = "put_nd(inport, nd.target, nd.tll); next;", - .external_ids = map_empty()); - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), - .priority = 90, - .__match = "nd_ns", - .actions = "put_nd(inport, ip6.src, nd.sll); next;", - .external_ids = map_empty()) + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), + .priority = 90, + .__match = "arp", + .actions = "put_arp(inport, arp.spa, arp.sha); next;", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ARP()), + .external_ids = map_empty()); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), + .priority = 90, + .__match = "nd_na", + .actions = "put_nd(inport, nd.target, nd.tll); next;", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ND_NA()), + .external_ids = map_empty()); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_LEARN_NEIGHBOR(), + .priority = 90, + .__match = "nd_ns", + .actions = "put_nd(inport, ip6.src, nd.sll); next;", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ND_NS()), + .external_ids = map_empty()) } /* Check if we need to learn mac-binding from ARP requests. */ @@ -5226,12 +5259,14 @@ relation LogicalRouterNdFlow( drop: bool, priority: integer, external_ids: Map) -Flow(.logical_datapath = lr._uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = priority, - .__match = __match, - .actions = actions, - .external_ids = external_ids) :- +MeteredFlow(.logical_datapath = lr._uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = priority, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = controller_meter, + .external_ids = external_ids) :- LogicalRouterNdFlow(.lr = lr, .lrp = lrp, .action = action, .ip = ip, .sn_ip = sn_ip, .mac = mac, .extra_match = extra_match, .drop = drop, .priority = priority, @@ -5249,18 +5284,19 @@ Flow(.logical_datapath = lr._uuid, clauses.append(extra_match.to_vec()); clauses.join(" && ") }, - var actions = if (drop) { - "drop;" + (var actions, var controller_meter) = if (drop) { + ("drop;", None) } else { - "${action} { " - "eth.src = ${mac}; " - "ip6.src = ${ip}; " - "nd.target = ${ip}; " - "nd.tll = ${mac}; " - "outport = inport; " - "flags.loopback = 1; " - "output; " - "};" + ("${action} { " + "eth.src = ${mac}; " + "ip6.src = ${ip}; " + "nd.target = ${ip}; " + "nd.tll = ${mac}; " + "outport = inport; " + "flags.loopback = 1; " + "output; " + "};", + lr.copp.get(cOPP_ND_NA())) }. /* ICMP time exceeded */ @@ -5382,60 +5418,69 @@ for (RouterPortNetworksIPv4Addr( .port = &RouterPort{ .router = &Router{._uuid = lr_uuid, .l3dgw_port = None, - .is_gateway = false}, + .is_gateway = false, + .copp = copp}, .lrp = lrp}, .addr = addr)) { /* UDP/TCP/SCTP port unreachable. */ var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag && udp" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 80, - .__match = __match, - .actions = "icmp4 {" - "eth.dst <-> eth.src; " - "ip4.dst <-> ip4.src; " - "ip.ttl = 255; " - "icmp4.type = 3; " - "icmp4.code = 3; " - "next; };", - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 80, + .__match = __match, + .actions = "icmp4 {" + "eth.dst <-> eth.src; " + "ip4.dst <-> ip4.src; " + "ip.ttl = 255; " + "icmp4.type = 3; " + "icmp4.code = 3; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ICMP4_ERR()), + .external_ids = stage_hint(lrp._uuid)); var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag && tcp" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 80, - .__match = __match, - .actions = "tcp_reset {" - "eth.dst <-> eth.src; " - "ip4.dst <-> ip4.src; " - "next; };", - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 80, + .__match = __match, + .actions = "tcp_reset {" + "eth.dst <-> eth.src; " + "ip4.dst <-> ip4.src; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_TCP_RESET()), + .external_ids = stage_hint(lrp._uuid)); var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag && sctp" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 80, - .__match = __match, - .actions = "sctp_abort {" - "eth.dst <-> eth.src; " - "ip4.dst <-> ip4.src; " - "next; };", - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 80, + .__match = __match, + .actions = "sctp_abort {" + "eth.dst <-> eth.src; " + "ip4.dst <-> ip4.src; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_TCP_RESET()), + .external_ids = stage_hint(lrp._uuid)); var __match = "ip4 && ip4.dst == ${addr.addr} && !ip.later_frag" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 70, - .__match = __match, - .actions = "icmp4 {" - "eth.dst <-> eth.src; " - "ip4.dst <-> ip4.src; " - "ip.ttl = 255; " - "icmp4.type = 3; " - "icmp4.code = 2; " - "next; };", - .external_ids = stage_hint(lrp._uuid)) + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 70, + .__match = __match, + .actions = "icmp4 {" + "eth.dst <-> eth.src; " + "ip4.dst <-> ip4.src; " + "ip.ttl = 255; " + "icmp4.type = 3; " + "icmp4.code = 2; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ICMP4_ERR()), + .external_ids = stage_hint(lrp._uuid)) } /* DHCPv6 reply handling */ @@ -5509,60 +5554,69 @@ for (RouterPortNetworksIPv6Addr(.port = &RouterPort{.lrp = lrp, for (RouterPortNetworksIPv6Addr( .port = &RouterPort{.router = &Router{._uuid = lr_uuid, .l3dgw_port = None, - .is_gateway = false}, + .is_gateway = false, + .copp = copp}, .lrp = lrp, .json_name = json_name}, .addr = addr)) { var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag && tcp" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 80, - .__match = __match, - .actions = "tcp_reset {" - "eth.dst <-> eth.src; " - "ip6.dst <-> ip6.src; " - "next; };", - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 80, + .__match = __match, + .actions = "tcp_reset {" + "eth.dst <-> eth.src; " + "ip6.dst <-> ip6.src; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_TCP_RESET()), + .external_ids = stage_hint(lrp._uuid)); var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag && sctp" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 80, - .__match = __match, - .actions = "sctp_abort {" - "eth.dst <-> eth.src; " - "ip6.dst <-> ip6.src; " - "next; };", - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 80, + .__match = __match, + .actions = "sctp_abort {" + "eth.dst <-> eth.src; " + "ip6.dst <-> ip6.src; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_TCP_RESET()), + .external_ids = stage_hint(lrp._uuid)); var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag && udp" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 80, - .__match = __match, - .actions = "icmp6 {" - "eth.dst <-> eth.src; " - "ip6.dst <-> ip6.src; " - "ip.ttl = 255; " - "icmp6.type = 1; " - "icmp6.code = 4; " - "next; };", - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 80, + .__match = __match, + .actions = "icmp6 {" + "eth.dst <-> eth.src; " + "ip6.dst <-> ip6.src; " + "ip.ttl = 255; " + "icmp6.type = 1; " + "icmp6.code = 4; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ICMP6_ERR()), + .external_ids = stage_hint(lrp._uuid)); var __match = "ip6 && ip6.dst == ${addr.addr} && !ip.later_frag" in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 70, - .__match = __match, - .actions = "icmp6 {" - "eth.dst <-> eth.src; " - "ip6.dst <-> ip6.src; " - "ip.ttl = 255; " - "icmp6.type = 1; " - "icmp6.code = 3; " - "next; };", - .external_ids = stage_hint(lrp._uuid)) + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 70, + .__match = __match, + .actions = "icmp6 {" + "eth.dst <-> eth.src; " + "ip6.dst <-> ip6.src; " + "ip.ttl = 255; " + "icmp6.type = 1; " + "icmp6.code = 3; " + "next; };", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ICMP6_ERR()), + .external_ids = stage_hint(lrp._uuid)) } /* ICMPv6 time exceeded */ @@ -5584,12 +5638,14 @@ for (RouterPortNetworksIPv6Addr(.port = &RouterPort{.router = router, "icmp6.type = 3; /* Time exceeded */ " "icmp6.code = 0; /* TTL exceeded in transit */ " "next; };" in - Flow(.logical_datapath = router._uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 40, - .__match = __match, - .actions = actions, - .external_ids = stage_hint(lrp._uuid)) + MeteredFlow(.logical_datapath = router._uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 40, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = router.copp.get(cOPP_ICMP6_ERR()), + .external_ids = stage_hint(lrp._uuid)) } /* NAT, Defrag and load balancing. */ @@ -6247,16 +6303,16 @@ for (RouterLBVIP( { if (backends == "" and not lb.options.get_bool_def("reject", false)) { for (LoadBalancerEmptyEvents(lb)) { - for (HasEventElbMeter(has_elb_meter)) { - Some {(var __match, var __action)} = - build_empty_lb_event_flow(vip, lb, has_elb_meter) in - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_DNAT(), - .priority = 130, - .__match = __match, - .actions = __action, - .external_ids = stage_hint(lb._uuid)) - } + Some {(var __match, var __action)} = + build_empty_lb_event_flow(vip, lb) in + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_DNAT(), + .priority = 130, + .__match = __match, + .actions = __action, + .tags = map_empty(), + .controller_meter = r.copp.get(cOPP_EVENT_ELB()), + .external_ids = stage_hint(lb._uuid)) } }; @@ -6412,12 +6468,14 @@ for (RouterLBVIP( * via add_router_lb_flow(). One flow is for specific matching * on ct.new with an action of "ct_lb($targets);". The other * flow is for ct.est with an action of "ct_dnat;". */ -Flow(.logical_datapath = r._uuid, - .stage = s_ROUTER_IN_DNAT(), - .priority = priority, - .__match = __match, - .actions = actions, - .external_ids = stage_hint(lb._uuid)) :- +MeteredFlow(.logical_datapath = r._uuid, + .stage = s_ROUTER_IN_DNAT(), + .priority = priority, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = meter, + .external_ids = stage_hint(lb._uuid)) :- r in &Router(), r.l3dgw_port.is_some() or r.is_gateway, LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}], @@ -6435,7 +6493,12 @@ Flow(.logical_datapath = r._uuid, ForceSNAT -> "flags.force_snat_for_lb = 1; ", _ -> "" }, - var actions = build_lb_vip_actions(lbvip, s_ROUTER_OUT_SNAT(), force_snat). + (var actions, var reject) = build_lb_vip_actions(lbvip, s_ROUTER_OUT_SNAT(), force_snat), + var meter = if (reject) { + r.copp.get(cOPP_REJECT()) + } else { + None + }. /* Defaults based on MaxRtrInterval and MinRtrInterval from RFC 4861 section @@ -6576,12 +6639,14 @@ for (&RouterPort[port@RouterPort{.lrp = lrp@&nb::Logical_Router_Port{.peer = Non Some{prf} -> ", router_preference = \"${prf}\"" } in var actions = actions0 ++ router_preference ++ prefix ++ "); next;" in - Flow(.logical_datapath = router._uuid, - .stage = s_ROUTER_IN_ND_RA_OPTIONS(), - .priority = 50, - .__match = __match, - .actions = actions, - .external_ids = stage_hint(lrp._uuid)); + MeteredFlow(.logical_datapath = router._uuid, + .stage = s_ROUTER_IN_ND_RA_OPTIONS(), + .priority = 50, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = router.copp.get(cOPP_ND_RA_OPTS()), + .external_ids = stage_hint(lrp._uuid)); var __match = "inport == ${json_name} && ip6.dst == ff02::2 && " "nd_ra && ${rEGBIT_ND_RA_OPTS_RESULT()}" in @@ -7478,24 +7543,26 @@ Flow(.logical_datapath = lr_uuid, var gw_mtu = l3dgw_port.options.get_int_def("gateway_mtu", 0), gw_mtu > 0, var mtu = gw_mtu + vLAN_ETH_HEADER_LEN(). -Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_LARGER_PKTS(), - .priority = 50, - .__match = "inport == ${rp.json_name} && outport == ${l3dgw_port_json_name} && " - "ip4 && ${rEGBIT_PKT_LARGER()}", - .actions = "icmp4_error {" - "${rEGBIT_EGRESS_LOOPBACK()} = 1; " - "eth.dst = ${rp.networks.ea}; " - "ip4.dst = ip4.src; " - "ip4.src = ${first_ipv4.addr}; " - "ip.ttl = 255; " - "icmp4.type = 3; /* Destination Unreachable. */ " - "icmp4.code = 4; /* Frag Needed and DF was Set. */ " - /* Set icmp4.frag_mtu to gw_mtu */ - "icmp4.frag_mtu = ${gw_mtu}; " - "next(pipeline=ingress, table=0); " - "};", - .external_ids = stage_hint(rp.lrp._uuid)) :- +MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_LARGER_PKTS(), + .priority = 50, + .__match = "inport == ${rp.json_name} && outport == ${l3dgw_port_json_name} && " + "ip4 && ${rEGBIT_PKT_LARGER()}", + .actions = "icmp4_error {" + "${rEGBIT_EGRESS_LOOPBACK()} = 1; " + "eth.dst = ${rp.networks.ea}; " + "ip4.dst = ip4.src; " + "ip4.src = ${first_ipv4.addr}; " + "ip.ttl = 255; " + "icmp4.type = 3; /* Destination Unreachable. */ " + "icmp4.code = 4; /* Frag Needed and DF was Set. */ " + /* Set icmp4.frag_mtu to gw_mtu */ + "icmp4.frag_mtu = ${gw_mtu}; " + "next(pipeline=ingress, table=0); " + "};", + .tags = map_empty(), + .controller_meter = r.copp.get(cOPP_ICMP4_ERR()), + .external_ids = stage_hint(rp.lrp._uuid)) :- r in &Router(._uuid = lr_uuid), Some{var l3dgw_port} = r.l3dgw_port, var l3dgw_port_json_name = json_string_escape(l3dgw_port.name), @@ -7505,24 +7572,26 @@ Flow(.logical_datapath = lr_uuid, rp in &RouterPort(.router = r), rp.lrp != l3dgw_port, Some{var first_ipv4} = rp.networks.ipv4_addrs.nth(0). -Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_LARGER_PKTS(), - .priority = 50, - .__match = "inport == ${rp.json_name} && outport == ${l3dgw_port_json_name} && " - "ip6 && ${rEGBIT_PKT_LARGER()}", - .actions = "icmp6_error {" - "${rEGBIT_EGRESS_LOOPBACK()} = 1; " - "eth.dst = ${rp.networks.ea}; " - "ip6.dst = ip6.src; " - "ip6.src = ${first_ipv6.addr}; " - "ip.ttl = 255; " - "icmp6.type = 2; /* Packet Too Big. */ " - "icmp6.code = 0; " - /* Set icmp6.frag_mtu to gw_mtu */ - "icmp6.frag_mtu = ${gw_mtu}; " - "next(pipeline=ingress, table=0); " - "};", - .external_ids = stage_hint(rp.lrp._uuid)) :- +MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_LARGER_PKTS(), + .priority = 50, + .__match = "inport == ${rp.json_name} && outport == ${l3dgw_port_json_name} && " + "ip6 && ${rEGBIT_PKT_LARGER()}", + .actions = "icmp6_error {" + "${rEGBIT_EGRESS_LOOPBACK()} = 1; " + "eth.dst = ${rp.networks.ea}; " + "ip6.dst = ip6.src; " + "ip6.src = ${first_ipv6.addr}; " + "ip.ttl = 255; " + "icmp6.type = 2; /* Packet Too Big. */ " + "icmp6.code = 0; " + /* Set icmp6.frag_mtu to gw_mtu */ + "icmp6.frag_mtu = ${gw_mtu}; " + "next(pipeline=ingress, table=0); " + "};", + .tags = map_empty(), + .controller_meter = r.copp.get(cOPP_ICMP6_ERR()), + .external_ids = stage_hint(rp.lrp._uuid)) :- r in &Router(._uuid = lr_uuid), Some{var l3dgw_port} = r.l3dgw_port, var l3dgw_port_json_name = json_string_escape(l3dgw_port.name), @@ -7570,12 +7639,14 @@ for (&Router(._uuid = lr_uuid, * In the common case where the Ethernet destination has been resolved, * this table outputs the packet (priority 0). Otherwise, it composes * and sends an ARP/IPv6 NA request (priority 100). */ -Flow(.logical_datapath = router._uuid, - .stage = s_ROUTER_IN_ARP_REQUEST(), - .priority = 200, - .__match = __match, - .actions = actions, - .external_ids = map_empty()) :- +MeteredFlow(.logical_datapath = router._uuid, + .stage = s_ROUTER_IN_ARP_REQUEST(), + .priority = 200, + .__match = __match, + .actions = actions, + .tags = map_empty(), + .controller_meter = router.copp.get(cOPP_ND_NS_RESOLVE()), + .external_ids = map_empty()) :- rsr in RouterStaticRoute(.router = router), var dst = FlatMap(rsr.dsts), IPv6{var gw_ip6} = dst.nexthop, @@ -7591,30 +7662,34 @@ Flow(.logical_datapath = router._uuid, "output; " "};". -for (&Router(._uuid = lr_uuid)) +for (&Router(._uuid = lr_uuid, .copp = copp)) { - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_ARP_REQUEST(), - .priority = 100, - .__match = "eth.dst == 00:00:00:00:00:00 && ip4", - .actions = "arp { " - "eth.dst = ff:ff:ff:ff:ff:ff; " - "arp.spa = ${rEG_SRC()}; " - "arp.tpa = ${rEG_NEXT_HOP()}; " - "arp.op = 1; " /* ARP request */ - "output; " - "};", - .external_ids = map_empty()); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_ARP_REQUEST(), + .priority = 100, + .__match = "eth.dst == 00:00:00:00:00:00 && ip4", + .actions = "arp { " + "eth.dst = ff:ff:ff:ff:ff:ff; " + "arp.spa = ${rEG_SRC()}; " + "arp.tpa = ${rEG_NEXT_HOP()}; " + "arp.op = 1; " /* ARP request */ + "output; " + "};", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ARP_RESOLVE()), + .external_ids = map_empty()); - Flow(.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_ARP_REQUEST(), - .priority = 100, - .__match = "eth.dst == 00:00:00:00:00:00 && ip6", - .actions = "nd_ns { " - "nd.target = xx${rEG_NEXT_HOP()}; " - "output; " - "};", - .external_ids = map_empty()); + MeteredFlow(.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_ARP_REQUEST(), + .priority = 100, + .__match = "eth.dst == 00:00:00:00:00:00 && ip6", + .actions = "nd_ns { " + "nd.target = xx${rEG_NEXT_HOP()}; " + "output; " + "};", + .tags = map_empty(), + .controller_meter = copp.get(cOPP_ND_NS_RESOLVE()), + .external_ids = map_empty()); Flow(.logical_datapath = lr_uuid, .stage = s_ROUTER_IN_ARP_REQUEST(), @@ -8285,8 +8360,12 @@ nb::Out_BFD(bfd_uuid, Some{status}) :- * Logical router BFD flows */ -function lrouter_bfd_flows(lr_uuid: uuid, lrp_uuid: uuid, ipX: string, networks: string) - : (Flow, Flow) +function lrouter_bfd_flows(lr_uuid: uuid, + lrp_uuid: uuid, + ipX: string, + networks: string, + controller_meter: Option) + : (Flow, MeteredFlow) { (Flow{.logical_datapath = lr_uuid, .stage = s_ROUTER_IN_IP_INPUT(), @@ -8294,27 +8373,33 @@ function lrouter_bfd_flows(lr_uuid: uuid, lrp_uuid: uuid, ipX: string, networks: .__match = "${ipX}.src == ${networks} && udp.dst == 3784", .actions = "next; ", .external_ids = stage_hint(lrp_uuid)}, - Flow{.logical_datapath = lr_uuid, - .stage = s_ROUTER_IN_IP_INPUT(), - .priority = 110, - .__match = "${ipX}.dst == ${networks} && udp.dst == 3784", - .actions = "handle_bfd_msg(); ", - .external_ids = stage_hint(lrp_uuid)}) + MeteredFlow{.logical_datapath = lr_uuid, + .stage = s_ROUTER_IN_IP_INPUT(), + .priority = 110, + .__match = "${ipX}.dst == ${networks} && udp.dst == 3784", + .actions = "handle_bfd_msg(); ", + .tags = map_empty(), + .controller_meter = controller_meter, + .external_ids = stage_hint(lrp_uuid)}) } for (&RouterPort(.router = router, .networks = networks, .lrp = lrp, .has_bfd = true)) { - if (not networks.ipv4_addrs.is_empty()) { - (var a, var b) = lrouter_bfd_flows(router._uuid, lrp._uuid, "ip4", - format_v4_networks(networks, false)) in { - Flow[a]; - Flow[b] - } - }; + var controller_meter = router.copp.get(cOPP_BFD()) in { + if (not networks.ipv4_addrs.is_empty()) { + (var a, var b) = lrouter_bfd_flows(router._uuid, lrp._uuid, "ip4", + format_v4_networks(networks, false), + controller_meter) in { + Flow[a]; + MeteredFlow[b] + } + }; - if (not networks.ipv6_addrs.is_empty()) { - (var a, var b) = lrouter_bfd_flows(router._uuid, lrp._uuid, "ip6", - format_v6_networks(networks)) in { - Flow[a]; - Flow[b] + if (not networks.ipv6_addrs.is_empty()) { + (var a, var b) = lrouter_bfd_flows(router._uuid, lrp._uuid, "ip6", + format_v6_networks(networks), + controller_meter) in { + Flow[a]; + MeteredFlow[b] + } } } } diff --git a/ovn-nb.xml b/ovn-nb.xml index 941e7bc7d..c1176e81f 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 eaf344be9..c8407bc6e 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' @@ -17969,6 +17965,7 @@ AT_CLEANUP OVN_FOR_EACH_NORTHD([ AT_SETUP([controller event]) AT_KEYWORDS([ovn_controller_event]) + ovn_start # Create hypervisors hv[12]. @@ -18032,6 +18029,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 fc377bbd1..4288d80e5 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -6612,3 +6612,138 @@ NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.12 | FORMAT_PING], \ 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]) + +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([NORTHD_TYPE]) + +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