[ovs-dev] [PATCH ovn 3/5] ovn-northd: Add CoPP policies for flows that punt packets to ovn-controller.

Mark Gray mark.d.gray at redhat.com
Fri May 21 14:17:31 UTC 2021


On 29/04/2021 18:04, Lorenzo Bianconi wrote:
> From: Dumitru Ceara <dceara at redhat.com>
> 
> 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
> - BFD
> 
Add reject?

Do you need to add event-elb?

> Co-authored-by: Lorenzo Bianconi <lorenzo.bianconi at redhat.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi at redhat.com>
> Signed-off-by: Dumitru Ceara <dceara at redhat.com>
> ---
>  lib/copp.c                |   1 +
>  lib/copp.h                |   1 +
>  northd/ovn-northd.c       | 451 ++++++++++++++++++++++++--------------
>  ovn-nb.xml                |   3 +
>  tests/atlocal.in          |   3 +
>  tests/system-ovn.at       | 119 ++++++++++
>  utilities/ovn-nbctl.8.xml |   1 +
>  7 files changed, 417 insertions(+), 162 deletions(-)
> 
> diff --git a/lib/copp.c b/lib/copp.c
> index ac53a1094..7713046e5 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 82581e7e4..826fe987e 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,

If it is easy to do, you should merge the reject meter addition into the
previous patch as I think that is where it actually belongs.

>      COPP_PROTO_MAX,
>      COPP_PROTO_INVALID = COPP_PROTO_MAX,
>  };
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index 2e9c7de22..93b431f4c 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -3328,11 +3328,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;
>  
> @@ -3384,6 +3384,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
> @@ -4174,9 +4175,14 @@ ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
>      ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, false, \
>                       NULL, STAGE_HINT, OVS_SOURCE_LOCATOR)
>  
> -#define ovn_lflow_add_unique(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \
> +#define ovn_lflow_add_unique__(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \
> +                               ACTIONS, CTRL_METER) \
>      ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, false, \
> -                     NULL, NULL, OVS_SOURCE_LOCATOR)
> +                     CTRL_METER, NULL, OVS_SOURCE_LOCATOR)
> +
> +#define ovn_lflow_add_unique(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \
> +    ovn_lflow_add_unique__(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \
> +                           NULL)
>  
>  static struct ovn_lflow *
>  ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
> @@ -5429,9 +5435,12 @@ build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
>                    "reject { "
>                    "/* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ "
>                    "outport <-> inport; %s };", next_action);
> -    ovn_lflow_add_with_hint(lflows, od, stage,
> -                            acl->priority + OVN_ACL_PRI_OFFSET,
> -                            ds_cstr(&match), ds_cstr(&actions), stage_hint);
> +    ovn_lflow_add_with_hint__(lflows, od, stage,
> +                              acl->priority + OVN_ACL_PRI_OFFSET,
> +                              ds_cstr(&match), ds_cstr(&actions),
> +                              copp_meter_get(COPP_REJECT, od->nbs->copp,
> +                                             meter_groups),
> +                              stage_hint);
>  
>      free(next_action);
>      ds_destroy(&match);
> @@ -5938,7 +5947,7 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows)
>  
>  static void
>  build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
> -               struct ovn_northd_lb *lb)
> +               struct ovn_northd_lb *lb, struct shash *meter_groups)
>  {
>      for (size_t i = 0; i < lb->n_vips; i++) {
>          struct ovn_lb_vip *lb_vip = &lb->vips[i];
> @@ -5979,21 +5988,25 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
>          }
>  
>          /* 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);
> +        if (reject) {
> +            meter = copp_meter_get(COPP_REJECT, od->nbs->copp, meter_groups);
> +        }
>  
>          struct ds match = DS_EMPTY_INITIALIZER;
>          ds_put_format(&match, "ct.new && %s.dst == %s", ip_match,
>                        lb_vip->vip_str);
>          if (lb_vip->vip_port) {
>              ds_put_format(&match, " && %s.dst == %d", proto, lb_vip->vip_port);
> -            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 120,
> -                                    ds_cstr(&match), ds_cstr(&action),
> -                                    &lb->nlb->header_);
> +            ovn_lflow_add_with_hint__(lflows, od, S_SWITCH_IN_STATEFUL, 120,
> +                                      ds_cstr(&match), ds_cstr(&action),
> +                                      meter, &lb->nlb->header_);
>          } else {
> -            ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_STATEFUL, 110,
> -                                    ds_cstr(&match), ds_cstr(&action),
> -                                    &lb->nlb->header_);
> +            ovn_lflow_add_with_hint__(lflows, od, S_SWITCH_IN_STATEFUL, 110,
> +                                      ds_cstr(&match), ds_cstr(&action),
> +                                      meter, &lb->nlb->header_);
>          }
>  
>          ds_destroy(&match);
> @@ -6002,7 +6015,8 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows,
>  }
>  
>  static void
> -build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
> +build_stateful(struct ovn_datapath *od, struct hmap *lflows,
> +               struct hmap *lbs, struct shash *meter_groups)
>  {
>      /* Ingress and Egress stateful Table (Priority 0): Packets are
>       * allowed by default. */
> @@ -6072,7 +6086,7 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
>              ovn_northd_lb_find(lbs, &od->nbs->load_balancer[i]->header_.uuid);
>  
>          ovs_assert(lb);
> -        build_lb_rules(od, lflows, lb);
> +        build_lb_rules(od, lflows, lb, meter_groups);
>      }
>  }
>  
> @@ -6603,6 +6617,7 @@ static void
>  build_dhcpv4_options_flows(struct ovn_port *op,
>                             struct lport_addresses *lsp_addrs,
>                             const char *json_key, bool is_external,
> +                           struct shash *meter_groups,
>                             struct hmap *lflows)
>  {
>      struct ds match = DS_EMPTY_INITIALIZER;
> @@ -6626,11 +6641,14 @@ build_dhcpv4_options_flows(struct ovn_port *op,
>                                op->json_key);
>              }
>  
> -            ovn_lflow_add_with_hint(lflows, op->od,
> -                                    S_SWITCH_IN_DHCP_OPTIONS, 100,
> -                                    ds_cstr(&match),
> -                                    ds_cstr(&options_action),
> -                                    &op->nbsp->dhcpv4_options->header_);
> +            ovn_lflow_add_with_hint__(lflows, op->od,
> +                                      S_SWITCH_IN_DHCP_OPTIONS, 100,
> +                                      ds_cstr(&match),
> +                                      ds_cstr(&options_action),
> +                                      copp_meter_get(COPP_DHCPV4_OPTS,
> +                                                     op->od->nbs->copp,
> +                                                     meter_groups),
> +                                      &op->nbsp->dhcpv4_options->header_);
>              ds_clear(&match);
>              /* Allow ip4.src = OFFER_IP and
>               * ip4.dst = {SERVER_IP, 255.255.255.255} for the below
> @@ -6650,11 +6668,14 @@ build_dhcpv4_options_flows(struct ovn_port *op,
>                                op->json_key);
>              }
>  
> -            ovn_lflow_add_with_hint(lflows, op->od,
> -                                    S_SWITCH_IN_DHCP_OPTIONS, 100,
> -                                    ds_cstr(&match),
> -                                    ds_cstr(&options_action),
> -                                    &op->nbsp->dhcpv4_options->header_);
> +            ovn_lflow_add_with_hint__(lflows, op->od,
> +                                      S_SWITCH_IN_DHCP_OPTIONS, 100,
> +                                      ds_cstr(&match),
> +                                      ds_cstr(&options_action),
> +                                      copp_meter_get(COPP_DHCPV4_OPTS,
> +                                                     op->od->nbs->copp,
> +                                                     meter_groups),
> +                                      &op->nbsp->dhcpv4_options->header_);
>              ds_clear(&match);
>  
>              /* If REGBIT_DHCP_OPTS_RESULT is set, it means the
> @@ -6688,6 +6709,7 @@ static void
>  build_dhcpv6_options_flows(struct ovn_port *op,
>                             struct lport_addresses *lsp_addrs,
>                             const char *json_key, bool is_external,
> +                           struct shash *meter_groups,
>                             struct hmap *lflows)
>  {
>      struct ds match = DS_EMPTY_INITIALIZER;
> @@ -6710,11 +6732,14 @@ build_dhcpv6_options_flows(struct ovn_port *op,
>                                op->json_key);
>              }
>  
> -            ovn_lflow_add_with_hint(lflows, op->od,
> -                                    S_SWITCH_IN_DHCP_OPTIONS, 100,
> -                                    ds_cstr(&match),
> -                                    ds_cstr(&options_action),
> -                                    &op->nbsp->dhcpv6_options->header_);
> +            ovn_lflow_add_with_hint__(lflows, op->od,
> +                                      S_SWITCH_IN_DHCP_OPTIONS, 100,
> +                                      ds_cstr(&match),
> +                                      ds_cstr(&options_action),
> +                                      copp_meter_get(COPP_DHCPV6_OPTS,
> +                                                     op->od->nbs->copp,
> +                                                     meter_groups),
> +                                      &op->nbsp->dhcpv6_options->header_);
>  
>              /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
>               * put_dhcpv6_opts action is successful */
> @@ -6853,7 +6878,7 @@ build_lswitch_lflows_pre_acl_and_acl(struct ovn_datapath *od,
>          build_acls(od, lflows, port_groups, meter_groups);
>          build_qos(od, lflows);
>          build_lb(od, lflows);
> -        build_stateful(od, lflows, lbs);
> +        build_stateful(od, lflows, lbs, meter_groups);
>          build_lb_hairpin(od, lflows);
>      }
>  }
> @@ -6909,6 +6934,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)
>  {
> @@ -7050,11 +7076,14 @@ build_lswitch_arp_nd_responder_known_ips(struct ovn_port *op,
>                              op->lsp_addrs[i].ipv6_addrs[j].addr_s,
>                              op->lsp_addrs[i].ipv6_addrs[j].addr_s,
>                              op->lsp_addrs[i].ea_s);
> -                    ovn_lflow_add_with_hint(lflows, op->od,
> -                                            S_SWITCH_IN_ARP_ND_RSP, 50,
> -                                            ds_cstr(match),
> -                                            ds_cstr(actions),
> -                                            &op->nbsp->header_);
> +                    ovn_lflow_add_with_hint__(lflows, op->od,
> +                                              S_SWITCH_IN_ARP_ND_RSP, 50,
> +                                              ds_cstr(match),
> +                                              ds_cstr(actions),
> +                                              copp_meter_get(COPP_ND_NA,
> +                                                  op->od->nbs->copp,
> +                                                  meter_groups),
> +                                              &op->nbsp->header_);
>  
>                      /* Do not reply to a solicitation from the port that owns
>                       * the address (otherwise DAD detection will fail). */
> @@ -7132,7 +7161,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)) {
> @@ -7161,17 +7191,17 @@ build_lswitch_dhcp_options_and_response(struct ovn_port *op,
>                      build_dhcpv4_options_flows(
>                          op, &op->lsp_addrs[i],
>                          op->od->localnet_ports[j]->json_key, is_external,
> -                        lflows);
> +                        meter_groups, lflows);
>                      build_dhcpv6_options_flows(
>                          op, &op->lsp_addrs[i],
>                          op->od->localnet_ports[j]->json_key, is_external,
> -                        lflows);
> +                        meter_groups, lflows);
>                  }
>              } else {
>                  build_dhcpv4_options_flows(op, &op->lsp_addrs[i], op->json_key,
> -                                           is_external, lflows);
> +                                           is_external, meter_groups, lflows);
>                  build_dhcpv6_options_flows(op, &op->lsp_addrs[i], op->json_key,
> -                                           is_external, lflows);
> +                                           is_external, meter_groups, lflows);
>              }
>          }
>      }
> @@ -7201,13 +7231,19 @@ 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_add_ctrl(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));

Why do you add the same flow twice but one with a meter instead of
modifying the flow above?

>          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;";
> @@ -7244,7 +7280,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) {
>  
> @@ -7265,12 +7302,16 @@ build_lswitch_destination_lookup_bmcast(struct ovn_datapath *od,
>              }
>              ds_put_cstr(actions, "igmp;");
>              /* Punt IGMP traffic to controller. */
> -            ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
> -                                 "ip4 && ip.proto == 2", ds_cstr(actions));
> +            ovn_lflow_add_unique__(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
> +                                   "ip4 && ip.proto == 2", ds_cstr(actions),
> +                                   copp_meter_get(COPP_IGMP, od->nbs->copp,
> +                                                  meter_groups));
>  
>              /* Punt MLD traffic to controller. */
> -            ovn_lflow_add_unique(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
> -                                 "mldv1 || mldv2", ds_cstr(actions));
> +            ovn_lflow_add_unique__(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
> +                                   "mldv1 || mldv2", ds_cstr(actions),
> +                                   copp_meter_get(COPP_IGMP, od->nbs->copp,
> +                                                  meter_groups));
>  
>              /* Flood all IP multicast traffic destined to 224.0.0.X to all
>               * ports - RFC 4541, section 2.1.2, item 2.
> @@ -8650,23 +8691,29 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
>                     struct ds *match, struct ds *actions, int priority,
>                     enum lb_snat_type snat_type, struct ovn_lb_vip *lb_vip,
>                     const char *proto, struct nbrec_load_balancer *lb,
> -                   struct shash *meter_groups, struct sset *nat_entries)
> +                   struct shash *meter_groups, struct sset *nat_entries,
> +                   bool reject)
>  {
>      build_empty_lb_event_flow(od, lflows, lb_vip, lb, S_ROUTER_IN_DNAT,
>                                meter_groups);
>  
> +    const char *meter = NULL;
> +    if (reject) {
> +        meter = copp_meter_get(COPP_REJECT, od->nbr->copp, meter_groups);
> +    }
>      /* A match and actions for new connections. */
>      char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
>      if (snat_type == FORCE_SNAT || snat_type == SKIP_SNAT) {
>          char *new_actions = xasprintf("flags.%s_snat_for_lb = 1; %s",
>                  snat_type == SKIP_SNAT ? "skip" : "force",
>                  ds_cstr(actions));
> -        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
> -                                new_match, new_actions, &lb->header_);
> +        ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, priority,
> +                                  new_match, new_actions, meter, &lb->header_);
>          free(new_actions);
>      } else {
> -        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_DNAT, priority,
> -                                new_match, ds_cstr(actions), &lb->header_);
> +        ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_DNAT, priority,
> +                                new_match, ds_cstr(actions), meter,
> +                                &lb->header_);
>      }
>  
>      /* A match and actions for established connections. */
> @@ -8792,8 +8839,8 @@ build_lrouter_lb_flows(struct hmap *lflows, struct ovn_datapath *od,
>              struct ovn_lb_vip *lb_vip = &lb->vips[j];
>              struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[j];
>              ds_clear(actions);
> -            build_lb_vip_actions(lb_vip, lb_vip_nb, actions,
> -                                 lb->selection_fields, false);
> +            bool reject = build_lb_vip_actions(lb_vip, lb_vip_nb, actions,
> +                                               lb->selection_fields, false);
>  
>              if (!sset_contains(&all_ips, lb_vip->vip_str)) {
>                  sset_add(&all_ips, lb_vip->vip_str);
> @@ -8858,7 +8905,7 @@ build_lrouter_lb_flows(struct hmap *lflows, struct ovn_datapath *od,
>              }
>              add_router_lb_flow(lflows, od, match, actions, prio,
>                                 snat_type, lb_vip, proto, nb_lb,
> -                               meter_groups, nat_entries);
> +                               meter_groups, nat_entries, reject);
>          }
>      }
>      sset_destroy(&all_ips);
> @@ -9097,7 +9144,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;
> @@ -9119,6 +9166,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 { "
> @@ -9135,11 +9184,13 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op,
>                        ip_address,
>                        ip_address,
>                        eth_addr);
> +        ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_IP_INPUT, priority,
> +                                  ds_cstr(&match), ds_cstr(&actions),
> +                                  copp_meter_get(COPP_ND_NA, od->nbr->copp,
> +                                                 meter_groups),
> +                                  hint);
>      }
>  
> -    ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_IP_INPUT, priority,
> -                            ds_cstr(&match), ds_cstr(&actions), hint);
> -
>      ds_destroy(&match);
>      ds_destroy(&actions);
>  }
> @@ -9147,7 +9198,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;
> @@ -9157,7 +9209,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,
> @@ -9169,7 +9221,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;
> @@ -9212,12 +9265,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,
> @@ -9390,7 +9443,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;
> @@ -9409,9 +9463,11 @@ build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op)
>          ds_clear(&match);
>          ds_put_format(&match, "ip4.dst == %s && udp.dst == 3784",
>                        ds_cstr(&ip_list));
> -        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
> -                                ds_cstr(&match), "handle_bfd_msg(); ",
> -                                &op->nbrp->header_);
> +        ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
> +                                  ds_cstr(&match), "handle_bfd_msg(); ",
> +                                  copp_meter_get(COPP_BFD, op->od->nbr->copp,
> +                                                 meter_groups),
> +                                  &op->nbrp->header_);
>      }
>      if (op->lrp_networks.n_ipv6_addrs) {
>          ds_clear(&ip_list);
> @@ -9426,9 +9482,11 @@ build_lrouter_bfd_flows(struct hmap *lflows, struct ovn_port *op)
>          ds_clear(&match);
>          ds_put_format(&match, "ip6.dst == %s && udp.dst == 3784",
>                        ds_cstr(&ip_list));
> -        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
> -                                ds_cstr(&match), "handle_bfd_msg(); ",
> -                                &op->nbrp->header_);
> +        ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
> +                                  ds_cstr(&match), "handle_bfd_msg(); ",
> +                                  copp_meter_get(COPP_BFD, op->od->nbr->copp,
> +                                                 meter_groups),
> +                                  &op->nbrp->header_);
>      }
>  
>      ds_destroy(&ip_list);
> @@ -9508,7 +9566,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) {
>  
> @@ -9588,14 +9647,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_add_ctrl(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_add_ctrl(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_add_ctrl(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));
>      }
>  
>  }
> @@ -9670,7 +9735,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;
> @@ -9763,9 +9829,12 @@ build_ND_RA_flows_for_lrouter_port(
>  
>      if (add_rs_response_flow) {
>          ds_put_cstr(actions, "); next;");
> -        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS,
> -                                50, ds_cstr(match), ds_cstr(actions),
> -                                &op->nbrp->header_);
> +        ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS,
> +                                  50, ds_cstr(match), ds_cstr(actions),
> +                                  copp_meter_get(COPP_ND_RA_OPTS,
> +                                                 op->od->nbr->copp,
> +                                                 meter_groups),
> +                                  &op->nbrp->header_);
>          ds_clear(actions);
>          ds_clear(match);
>          ds_put_format(match, "inport == %s && ip6.dst == ff02::2 && "
> @@ -10388,7 +10457,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) {
>  
> @@ -10450,10 +10520,14 @@ build_check_pkt_len_flows_for_lrouter(
>                          rp->lrp_networks.ipv4_addrs[0].addr_s,
>                          gw_mtu,
>                          ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
> -                    ovn_lflow_add_with_hint(lflows, od,
> -                                            S_ROUTER_IN_LARGER_PKTS, 50,
> -                                            ds_cstr(match), ds_cstr(actions),
> -                                            &rp->nbrp->header_);
> +                    ovn_lflow_add_with_hint__(lflows, od,
> +                                              S_ROUTER_IN_LARGER_PKTS, 50,
> +                                              ds_cstr(match), ds_cstr(actions),
> +                                              copp_meter_get(
> +                                                    COPP_ICMP4_ERR,
> +                                                    rp->od->nbr->copp,
> +                                                    meter_groups),
> +                                              &rp->nbrp->header_);
>                  }
>  
>                  if (rp->lrp_networks.ipv6_addrs) {
> @@ -10479,10 +10553,14 @@ build_check_pkt_len_flows_for_lrouter(
>                          rp->lrp_networks.ipv6_addrs[0].addr_s,
>                          gw_mtu,
>                          ovn_stage_get_table(S_ROUTER_IN_ADMISSION));
> -                    ovn_lflow_add_with_hint(lflows, od,
> -                                            S_ROUTER_IN_LARGER_PKTS, 50,
> -                                            ds_cstr(match), ds_cstr(actions),
> -                                            &rp->nbrp->header_);
> +                    ovn_lflow_add_with_hint__(lflows, od,
> +                                              S_ROUTER_IN_LARGER_PKTS, 50,
> +                                              ds_cstr(match), ds_cstr(actions),
> +                                              copp_meter_get(
> +                                                    COPP_ICMP6_ERR,
> +                                                    rp->od->nbr->copp,
> +                                                    meter_groups),
> +                                              &rp->nbrp->header_);
>                  }
>              }
>          }
> @@ -10537,7 +10615,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++) {
> @@ -10574,26 +10653,33 @@ build_arp_request_flows_for_lrouter(
>                            "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s,
>                            route->nexthop);
>  
> -            ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
> -                                    ds_cstr(match), ds_cstr(actions),
> -                                    &route->header_);
> -        }
> -
> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
> -                      "eth.dst == 00:00:00:00:00:00 && ip4",
> -                      "arp { "
> -                      "eth.dst = ff:ff:ff:ff:ff:ff; "
> -                      "arp.spa = " REG_SRC_IPV4 "; "
> -                      "arp.tpa = " REG_NEXT_HOP_IPV4 "; "
> -                      "arp.op = 1; " /* ARP request */
> -                      "output; "
> -                      "};");
> -        ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
> -                      "eth.dst == 00:00:00:00:00:00 && ip6",
> -                      "nd_ns { "
> -                      "nd.target = " REG_NEXT_HOP_IPV6 "; "
> -                      "output; "
> -                      "};");
> +            ovn_lflow_add_with_hint__(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
> +                                      ds_cstr(match), ds_cstr(actions),
> +                                      copp_meter_get(COPP_ND_NS_RESOLVE,
> +                                                     od->nbr->copp,
> +                                                     meter_groups),
> +                                      &route->header_);
> +        }
> +
> +        ovn_lflow_add_ctrl(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_add_ctrl(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;");
>      }
>  }
> @@ -10724,7 +10810,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
> @@ -10767,7 +10854,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 */
> @@ -10782,9 +10869,13 @@ build_ipv6_input_flows_for_lrouter_port(
>                                       "eth.dst <-> eth.src; "
>                                       "ip6.dst <-> ip6.src; "
>                                       "next; };";
> -                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> -                                        80, ds_cstr(match), action,
> -                                        &op->nbrp->header_);
> +                ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                          80, ds_cstr(match), action,
> +                                          copp_meter_get(
> +                                              COPP_TCP_RESET,
> +                                              op->od->nbr->copp,
> +                                              meter_groups),
> +                                          &op->nbrp->header_);
>  
>                  ds_clear(match);
>                  ds_put_format(match,
> @@ -10809,9 +10900,13 @@ build_ipv6_input_flows_for_lrouter_port(
>                           "icmp6.type = 1; "
>                           "icmp6.code = 4; "
>                           "next; };";
> -                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> -                                        80, ds_cstr(match), action,
> -                                        &op->nbrp->header_);
> +                ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                          80, ds_cstr(match), action,
> +                                          copp_meter_get(
> +                                              COPP_ICMP6_ERR,
> +                                              op->od->nbr->copp,
> +                                              meter_groups),
> +                                          &op->nbrp->header_);
>  
>                  ds_clear(match);
>                  ds_put_format(match,
> @@ -10824,9 +10919,13 @@ build_ipv6_input_flows_for_lrouter_port(
>                           "icmp6.type = 1; "
>                           "icmp6.code = 3; "
>                           "next; };";
> -                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> -                                        70, ds_cstr(match), action,
> -                                        &op->nbrp->header_);
> +                ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                          70, ds_cstr(match), action,
> +                                          copp_meter_get(
> +                                              COPP_ICMP6_ERR,
> +                                              op->od->nbr->copp,
> +                                              meter_groups),
> +                                          &op->nbrp->header_);
>              }
>          }
>  
> @@ -10857,9 +10956,12 @@ build_ipv6_input_flows_for_lrouter_port(
>                            "icmp6.code = 0; /* TTL exceeded in transit */ "
>                            "next; };",
>                            op->lrp_networks.ipv6_addrs[i].addr_s);
> -            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> -                                    ds_cstr(match), ds_cstr(actions),
> -                                    &op->nbrp->header_);
> +            ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> +                                      ds_cstr(match), ds_cstr(actions),
> +                                      copp_meter_get(COPP_ICMP6_ERR,
> +                                                     op->od->nbr->copp,
> +                                                     meter_groups),
> +                                      &op->nbrp->header_);
>          }
>      }
>  
> @@ -10867,7 +10969,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) {
>  
> @@ -10893,7 +10996,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. */
> @@ -10908,7 +11011,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);
>          }
>      }
>  }
> @@ -10917,7 +11020,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. */
> @@ -10955,7 +11059,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++) {
> @@ -10975,9 +11079,12 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                            "ip.ttl = 255; "
>                            "next; };",
>                            op->lrp_networks.ipv4_addrs[i].addr_s);
> -            ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> -                                    ds_cstr(match), ds_cstr(actions),
> -                                    &op->nbrp->header_);
> +            ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
> +                                      ds_cstr(match), ds_cstr(actions),
> +                                      copp_meter_get(COPP_ICMP4_ERR,
> +                                                     op->od->nbr->copp,
> +                                                     meter_groups),
> +                                      &op->nbrp->header_);
>          }
>  
>          /* ARP reply.  These flows reply to ARP requests for the router's own
> @@ -11051,7 +11158,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);
>          }
>  
>          sset_destroy(&all_ips_v4);
> @@ -11072,9 +11180,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                                       "icmp4.type = 3; "
>                                       "icmp4.code = 3; "
>                                       "next; };";
> -                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> -                                        80, ds_cstr(match), action,
> -                                        &op->nbrp->header_);
> +                ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                          80, ds_cstr(match), action,
> +                                          copp_meter_get(
> +                                              COPP_ICMP4_ERR,
> +                                              op->od->nbr->copp,
> +                                              meter_groups),
> +                                          &op->nbrp->header_);
>  
>                  ds_clear(match);
>                  ds_put_format(match,
> @@ -11084,9 +11196,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                           "eth.dst <-> eth.src; "
>                           "ip4.dst <-> ip4.src; "
>                           "next; };";
> -                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> -                                        80, ds_cstr(match), action,
> -                                        &op->nbrp->header_);
> +                ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                          80, ds_cstr(match), action,
> +                                          copp_meter_get(
> +                                              COPP_TCP_RESET,
> +                                              op->od->nbr->copp,
> +                                              meter_groups),
> +                                          &op->nbrp->header_);
>  
>                  ds_clear(match);
>                  ds_put_format(match,
> @@ -11111,9 +11227,13 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                           "icmp4.type = 3; "
>                           "icmp4.code = 2; "
>                           "next; };";
> -                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> -                                        70, ds_cstr(match), action,
> -                                        &op->nbrp->header_);
> +                ovn_lflow_add_with_hint__(lflows, op->od, S_ROUTER_IN_IP_INPUT,
> +                                          70, ds_cstr(match), action,
> +                                          copp_meter_get(
> +                                              COPP_ICMP4_ERR,
> +                                              op->od->nbr->copp,
> +                                              meter_groups),
> +                                          &op->nbrp->header_);
>              }
>          }
>  
> @@ -11157,7 +11277,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. */
> @@ -11172,7 +11293,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);
>          }
>      }
>  }
> @@ -11853,15 +11975,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);
> @@ -11870,13 +11993,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->meter_groups,
>                                      lsi->lbs, &lsi->match, &lsi->actions);
>  }
> @@ -11896,9 +12020,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);
> @@ -11912,16 +12038,17 @@ build_lswitch_and_lrouter_iterate_by_op(struct ovn_port *op,
>                                                  &lsi->actions);
>      build_ip_routing_flows_for_lrouter_port(op, lsi->lflows);
>      build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> -                                       &lsi->actions);
> +                                       &lsi->actions, lsi->meter_groups);
>      build_arp_resolve_flows_for_lrouter_port(op, lsi->lflows, lsi->ports,
>                                               &lsi->match, &lsi->actions);
>      build_egress_delivery_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
>                                                   &lsi->actions);
>      build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, &lsi->match);
>      build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows,
> -                                            &lsi->match, &lsi->actions);
> +                                            &lsi->match, &lsi->actions,
> +                                            lsi->meter_groups);
>      build_lrouter_ipv4_ip_input(op, lsi->lflows,
> -                                &lsi->match, &lsi->actions);
> +                                &lsi->match, &lsi->actions, lsi->meter_groups);
>      build_lrouter_force_snat_flows_op(op, lsi->lflows, &lsi->match,
>                                        &lsi->actions);
>  }
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 0f03a12f2..7695514a5 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -399,6 +399,9 @@
>      <column name="meters" key="bfd">
>        Rate limiting meter for BFD packets.
>      </column>
> +    <column name="meters" key="reject">
> +      Rate limiting meter for packets that trigger a reject action
> +    </column>
>    </table>
>  
>    <table name="Logical_Switch" title="L2 logical switch">
> 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/system-ovn.at b/tests/system-ovn.at
> index b6c679907..8a9cbde98 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -5890,3 +5890,122 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
>  /.*terminating with signal 15.*/d"])
>  AT_CLEANUP
>  ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([ovn -- CoPP])
> +AT_SKIP_IF([test $HAVE_TCPDUMP = no])
> +AT_SKIP_IF([test $HAVE_SCAPY = no])
> +AT_KEYWORDS([ovn-copp])
> +
> +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")
> +NS_CHECK_EXEC([server], [ip addr add 1000::b/64 dev s1])

Why do you add this IPv6 address here?

> +
> +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_CHECK_EXEC([sw01], [tcpdump -nni 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"
> +])
> +
> +NS_CHECK_EXEC([server], [tcpdump -nni s1 arp[[24:4]]=0xac100164 > arp.pcap &])
> +check ovn-nbctl meter-add arp-meter drop 1 pktps 0
> +check ovn-nbctl --wait=hv lr-copp-add R1 arp-resolve arp-meter
> +AT_CHECK([ovn-nbctl lr-copp-list R1], [0], [dnl
> +arp-resolve: arp-meter
> +])
> +
> +ip netns exec sw01 scapy -H <<-EOF
> +p = IP(src="192.168.1.2", dst="172.16.1.100")/ TCP(dport = 80, flags="S") / Raw(b"X"*64)
> +send (p, iface='sw01', loop = 0, verbose = 0, count = 100)
> +EOF
> +
> +sleep 2
> +kill $(pidof tcpdump)
> +
> +# 1pps + 1 burst size
> +OVS_WAIT_UNTIL([
> +    n_arp=$(grep ARP arp.pcap | wc -l)
> +    test "${n_arp}" = "2"
> +])
> +
> +kill $(pidof ovn-controller)
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
> +/.*terminating with signal 15.*/d"])
> +AT_CLEANUP

Did you test this with the meters disabled? Does it behave as expected?

> +])
> diff --git a/utilities/ovn-nbctl.8.xml b/utilities/ovn-nbctl.8.xml
> index 6c95e8104..5f5f71015 100644
> --- a/utilities/ovn-nbctl.8.xml
> +++ b/utilities/ovn-nbctl.8.xml
> @@ -1151,6 +1151,7 @@
>            <li>packets that need to be replied to with ICMP Errors</li>
>            <li>packets that need to be replied to with TCP RST</li>
>            <li>packets that need to be replied to with DHCP_OPTS</li>
> +          <li>packets that trigger a reject action</li>
>            <li>BFD</li>
>        </ul>
>      </p>
> 



More information about the dev mailing list