Recheck-request: github-robot-_Build_and_Test
On Tue, Jan 30, 2024 at 4:22 PM <[email protected]> wrote:
>
> From: Numan Siddique <[email protected]>
>
> Previous commits added new engine nodes to store logical router's
> stateful (LB and NAT data). Make use of the data stored by these
> engine nodes to generate logical flows related to router's LBs and NATs.
>
> Acked-by: Dumitru Ceara <[email protected]>
> Signed-off-by: Numan Siddique <[email protected]>
Recheck-request: github-robot-_Build_and_Test
> ---
> lib/stopwatch-names.h | 1 +
> northd/en-lflow.c | 3 -
> northd/en-lr-stateful.h | 4 +
> northd/inc-proc-northd.c | 1 -
> northd/northd.c | 816 +++++++++++++++++++++++++--------------
> northd/northd.h | 1 -
> northd/ovn-northd.c | 1 +
> 7 files changed, 531 insertions(+), 296 deletions(-)
>
> diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h
> index e5e41fbfd8..1e8a5b656f 100644
> --- a/lib/stopwatch-names.h
> +++ b/lib/stopwatch-names.h
> @@ -24,6 +24,7 @@
> #define LFLOWS_DATAPATHS_STOPWATCH_NAME "lflows_datapaths"
> #define LFLOWS_PORTS_STOPWATCH_NAME "lflows_ports"
> #define LFLOWS_LBS_STOPWATCH_NAME "lflows_lbs"
> +#define LFLOWS_LR_STATEFUL_STOPWATCH_NAME "lflows_lr_stateful"
> #define LFLOWS_IGMP_STOPWATCH_NAME "lflows_igmp"
> #define LFLOWS_DP_GROUPS_STOPWATCH_NAME "lflows_dp_groups"
> #define LFLOWS_TO_SB_STOPWATCH_NAME "lflows_to_sb"
> diff --git a/northd/en-lflow.c b/northd/en-lflow.c
> index 97c7f383cd..bd2296ac43 100644
> --- a/northd/en-lflow.c
> +++ b/northd/en-lflow.c
> @@ -42,8 +42,6 @@ lflow_get_input_data(struct engine_node *node,
> engine_get_input_data("port_group", node);
> struct sync_meters_data *sync_meters_data =
> engine_get_input_data("sync_meters", node);
> - struct ed_type_lr_nat_data *lr_nat_data =
> - engine_get_input_data("lr_nat", node);
> struct ed_type_lr_stateful *lr_stateful_data =
> engine_get_input_data("lr_stateful", node);
>
> @@ -68,7 +66,6 @@ lflow_get_input_data(struct engine_node *node,
> lflow_input->ls_ports = &northd_data->ls_ports;
> lflow_input->lr_ports = &northd_data->lr_ports;
> lflow_input->ls_port_groups = &pg_data->ls_port_groups;
> - lflow_input->lr_nats = &lr_nat_data->lr_nats;
> lflow_input->lr_stateful_table = &lr_stateful_data->table;
> lflow_input->meter_groups = &sync_meters_data->meter_groups;
> lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
> diff --git a/northd/en-lr-stateful.h b/northd/en-lr-stateful.h
> index bc3265adac..c6d62cb835 100644
> --- a/northd/en-lr-stateful.h
> +++ b/northd/en-lr-stateful.h
> @@ -73,6 +73,10 @@ struct lr_stateful_table {
> #define LR_STATEFUL_TABLE_FOR_EACH(LR_LB_NAT_REC, TABLE) \
> HMAP_FOR_EACH (LR_LB_NAT_REC, key_node, &(TABLE)->entries)
>
> +#define LR_STATEFUL_TABLE_FOR_EACH_IN_P(LR_STATEFUL_REC, JOBID, TABLE) \
> + HMAP_FOR_EACH_IN_PARALLEL (LR_STATEFUL_REC, key_node, JOBID, \
> + &(TABLE)->entries)
> +
> struct lr_stateful_tracked_data {
> /* Created or updated logical router with LB and/or NAT data. */
> struct hmapx crupdated; /* Stores 'struct lr_stateful_record'. */
> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
> index 97bcce9655..adb38dde78 100644
> --- a/northd/inc-proc-northd.c
> +++ b/northd/inc-proc-northd.c
> @@ -221,7 +221,6 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
> engine_add_input(&en_lflow, &en_sb_logical_flow, NULL);
> engine_add_input(&en_lflow, &en_sb_multicast_group, NULL);
> engine_add_input(&en_lflow, &en_sb_igmp_group, NULL);
> - engine_add_input(&en_lflow, &en_lr_nat, NULL);
> engine_add_input(&en_lflow, &en_lr_stateful, NULL);
> engine_add_input(&en_lflow, &en_northd, lflow_northd_handler);
> engine_add_input(&en_lflow, &en_port_group, lflow_port_group_handler);
> diff --git a/northd/northd.c b/northd/northd.c
> index a425a8d7ab..c4b4f7f886 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -8857,18 +8857,14 @@ build_lrouter_groups(struct hmap *lr_ports, struct
> ovs_list *lr_list)
> */
> static void
> build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
> - uint32_t priority,
> - struct ovn_datapath *od,
> - const struct lr_nat_table
> *lr_nats,
> - struct hmap *lflows)
> + uint32_t priority,
> + const struct ovn_datapath *od,
> + const struct lr_nat_record
> *lrnat_rec,
> + struct hmap *lflows)
> {
> struct ds eth_src = DS_EMPTY_INITIALIZER;
> struct ds match = DS_EMPTY_INITIALIZER;
>
> - const struct lr_nat_record *lrnat_rec = lr_nat_table_find_by_index(
> - lr_nats, op->od->index);
> - ovs_assert(lrnat_rec);
> -
> /* Self originated ARP requests/RARP/ND need to be flooded to the L2
> domain
> * (except on router ports). Determine that packets are self originated
> * by also matching on source MAC. Matching on ingress port is not
> @@ -8954,10 +8950,11 @@ lrouter_port_ipv6_reachable(const struct ovn_port *op,
> * switching domain as regular broadcast.
> */
> static void
> -build_lswitch_rport_arp_req_flow(const char *ips,
> - int addr_family, struct ovn_port *patch_op, struct ovn_datapath *od,
> - uint32_t priority, struct hmap *lflows,
> - const struct ovsdb_idl_row *stage_hint)
> +build_lswitch_rport_arp_req_flow(const char *ips, int addr_family,
> + struct ovn_port *patch_op,
> + const struct ovn_datapath *od,
> + uint32_t priority, struct hmap *lflows,
> + const struct ovsdb_idl_row *stage_hint)
> {
> struct ds match = DS_EMPTY_INITIALIZER;
> struct ds actions = DS_EMPTY_INITIALIZER;
> @@ -8993,10 +8990,47 @@ build_lswitch_rport_arp_req_flow(const char *ips,
> * - 75: ARP requests to router owned IPs (interface IP/LB/NAT).
> */
> static void
> -build_lswitch_rport_arp_req_flows(
> - struct ovn_port *op, struct ovn_datapath *sw_od,
> - struct ovn_port *sw_op, const struct lr_nat_table *lr_nats,
> - const struct lr_stateful_table *lr_stateful_table,
> +build_lswitch_rport_arp_req_flows(struct ovn_port *op,
> + struct ovn_datapath *sw_od,
> + struct ovn_port *sw_op,
> + struct hmap *lflows,
> + const struct ovsdb_idl_row *stage_hint)
> +{
> + if (!op || !op->nbrp) {
> + return;
> + }
> +
> + if (!lrport_is_enabled(op->nbrp)) {
> + return;
> + }
> +
> + /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to
> this
> + * router port.
> + * Priority: 80.
> + */
> + for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> + build_lswitch_rport_arp_req_flow(
> + op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, sw_op, sw_od, 80,
> + lflows, stage_hint);
> + }
> + for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> + build_lswitch_rport_arp_req_flow(
> + op->lrp_networks.ipv6_addrs[i].addr_s, AF_INET6, sw_op, sw_od,
> 80,
> + lflows, stage_hint);
> + }
> +}
> +
> +/*
> + * Ingress table 25: Flows that forward ARP/ND requests only to the routers
> + * that own the addresses.
> + * Priorities:
> + * - 80: self originated GARPs that need to follow regular processing.
> + * - 75: ARP requests to router owned IPs (interface IP/LB/NAT).
> + */
> +static void
> +build_lswitch_rport_arp_req_flows_for_lbnats(
> + struct ovn_port *op, const struct lr_stateful_record *lr_stateful_rec,
> + const struct ovn_datapath *sw_od, struct ovn_port *sw_op,
> struct hmap *lflows, const struct ovsdb_idl_row *stage_hint)
> {
> if (!op || !op->nbrp) {
> @@ -9007,16 +9041,14 @@ build_lswitch_rport_arp_req_flows(
> return;
> }
>
> + ovs_assert(uuid_equals(&op->od->nbr->header_.uuid,
> + &lr_stateful_rec->nbr_uuid));
> +
> /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to
> this
> * router port.
> * Priority: 80.
> */
> - const struct lr_stateful_record *lr_stateful_rec = NULL;
> if (op->od->nbr->n_load_balancer || op->od->nbr->n_load_balancer_group) {
> - lr_stateful_rec = lr_stateful_table_find_by_index(lr_stateful_table,
> - op->od->index);
> - ovs_assert(lr_stateful_rec);
> -
> const char *ip_addr;
> SSET_FOR_EACH (ip_addr, &lr_stateful_rec->lb_ips->ips_v4_reachable) {
> ovs_be32 ipv4_addr;
> @@ -9046,17 +9078,6 @@ build_lswitch_rport_arp_req_flows(
> }
> }
>
> - for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> - build_lswitch_rport_arp_req_flow(
> - op->lrp_networks.ipv4_addrs[i].addr_s, AF_INET, sw_op, sw_od, 80,
> - lflows, stage_hint);
> - }
> - for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> - build_lswitch_rport_arp_req_flow(
> - op->lrp_networks.ipv6_addrs[i].addr_s, AF_INET6, sw_op, sw_od,
> 80,
> - lflows, stage_hint);
> - }
> -
> /* Self originated ARP requests/RARP/ND need to be flooded as usual.
> *
> * However, if the switch doesn't have any non-router ports we shouldn't
> @@ -9065,19 +9086,14 @@ build_lswitch_rport_arp_req_flows(
> * Priority: 75.
> */
> if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> - build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od, lr_nats,
> + build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
> +
> lr_stateful_rec->lrnat_rec,
> lflows);
> }
>
> - const struct lr_nat_record *lrnat_rec =
> - lr_nat_table_find_by_index(lr_nats, op->od->index);
> -
> - if (!lrnat_rec) {
> - return;
> - }
> -
> - for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
> - struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
> + for (size_t i = 0; i < lr_stateful_rec->lrnat_rec->n_nat_entries; i++) {
> + struct ovn_nat *nat_entry =
> + &lr_stateful_rec->lrnat_rec->nat_entries[i];
> const struct nbrec_nat *nat = nat_entry->nb;
>
> if (!nat_entry_is_valid(nat_entry)) {
> @@ -9092,16 +9108,14 @@ build_lswitch_rport_arp_req_flows(
> * expect ARP requests/NS for the DNAT external_ip.
> */
> if (nat_entry_is_v6(nat_entry)) {
> - if (!lr_stateful_rec ||
> - !sset_contains(&lr_stateful_rec->lb_ips->ips_v6,
> + if (!sset_contains(&lr_stateful_rec->lb_ips->ips_v6,
> nat->external_ip)) {
> build_lswitch_rport_arp_req_flow(
> nat->external_ip, AF_INET6, sw_op, sw_od, 80, lflows,
> stage_hint);
> }
> } else {
> - if (!lr_stateful_rec ||
> - !sset_contains(&lr_stateful_rec->lb_ips->ips_v4,
> + if (!sset_contains(&lr_stateful_rec->lb_ips->ips_v4,
> nat->external_ip)) {
> build_lswitch_rport_arp_req_flow(
> nat->external_ip, AF_INET, sw_op, sw_od, 80, lflows,
> @@ -9111,7 +9125,7 @@ build_lswitch_rport_arp_req_flows(
> }
>
> struct shash_node *snat_snode;
> - SHASH_FOR_EACH (snat_snode, &lrnat_rec->snat_ips) {
> + SHASH_FOR_EACH (snat_snode, &lr_stateful_rec->lrnat_rec->snat_ips) {
> struct ovn_snat_ip *snat_ip = snat_snode->data;
>
> if (ovs_list_is_empty(&snat_ip->snat_entries)) {
> @@ -10183,11 +10197,8 @@ build_lswitch_ip_mcast_igmp_mld(struct
> ovn_igmp_group *igmp_group,
>
> /* Ingress table 25: Destination lookup, unicast handling (priority 50), */
> static void
> -build_lswitch_ip_unicast_lookup(
> - struct ovn_port *op, const struct lr_nat_table *lr_nats,
> - const struct lr_stateful_table *lr_stateful_table,
> - struct hmap *lflows, struct ds *actions,
> - struct ds *match)
> +build_lswitch_ip_unicast_lookup(struct ovn_port *op, struct hmap *lflows,
> + struct ds *actions, struct ds *match)
> {
> ovs_assert(op->nbsp);
> if (lsp_is_external(op->nbsp)) {
> @@ -10199,8 +10210,7 @@ build_lswitch_ip_unicast_lookup(
> * requests only to the router port that owns the IP address.
> */
> if (lsp_is_router(op->nbsp)) {
> - build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lr_nats,
> - lr_stateful_table, lflows,
> + build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows,
> &op->nbsp->header_);
> }
>
> @@ -10297,33 +10307,6 @@ build_lswitch_ip_unicast_lookup(
> S_SWITCH_IN_L2_LKUP, 50,
> ds_cstr(match), ds_cstr(actions),
> &op->nbsp->header_);
> -
> - /* Add ethernet addresses specified in NAT rules on
> - * distributed logical routers. */
> - if (is_l3dgw_port(op->peer)) {
> - for (int j = 0; j < op->peer->od->nbr->n_nat; j++) {
> - const struct nbrec_nat *nat
> - = op->peer->od->nbr->nat[j];
> - if (!strcmp(nat->type, "dnat_and_snat")
> - && nat->logical_port && nat->external_mac
> - && eth_addr_from_string(nat->external_mac, &mac)) {
> -
> - ds_clear(match);
> - ds_put_format(match, "eth.dst == "ETH_ADDR_FMT
> - " && is_chassis_resident(\"%s\")",
> - ETH_ADDR_ARGS(mac),
> - nat->logical_port);
> -
> - ds_clear(actions);
> - ds_put_format(actions, action, op->json_key);
> - ovn_lflow_add_with_hint(lflows, op->od,
> - S_SWITCH_IN_L2_LKUP, 50,
> - ds_cstr(match),
> - ds_cstr(actions),
> - &op->nbsp->header_);
> - }
> - }
> - }
> } else {
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>
> @@ -10334,6 +10317,55 @@ build_lswitch_ip_unicast_lookup(
> }
> }
>
> +/* Ingress table 25: Destination lookup, unicast handling (priority 50), */
> +static void
> +build_lswitch_ip_unicast_lookup_for_nats(
> + struct ovn_port *op, const struct lr_stateful_record *lr_stateful_rec,
> + struct hmap *lflows, struct ds *match, struct ds *actions)
> +{
> + ovs_assert(op->nbsp);
> +
> + if (!op->peer || !is_l3dgw_port(op->peer)) {
> + return;
> + }
> +
> + /* Make sure the lr_stateful_rec belongs to the peer port's
> + * logical router. */
> + ovs_assert(uuid_equals(&op->peer->od->nbr->header_.uuid,
> + &lr_stateful_rec->nbr_uuid));
> +
> + const char *action = lsp_is_enabled(op->nbsp) ?
> + "outport = %s; output;" :
> + debug_drop_action();
> + struct eth_addr mac;
> +
> + /* Add ethernet addresses specified in NAT rules on
> + * distributed logical routers. */
> + for (size_t i = 0; i < lr_stateful_rec->lrnat_rec->n_nat_entries; i++) {
> + const struct ovn_nat *nat =
> + &lr_stateful_rec->lrnat_rec->nat_entries[i];
> +
> + if (!strcmp(nat->nb->type, "dnat_and_snat")
> + && nat->nb->logical_port && nat->nb->external_mac
> + && eth_addr_from_string(nat->nb->external_mac, &mac)) {
> +
> + ds_clear(match);
> + ds_put_format(match, "eth.dst == "ETH_ADDR_FMT
> + " && is_chassis_resident(\"%s\")",
> + ETH_ADDR_ARGS(mac),
> + nat->nb->logical_port);
> +
> + ds_clear(actions);
> + ds_put_format(actions, action, op->json_key);
> + ovn_lflow_add_with_hint(lflows, op->od,
> + S_SWITCH_IN_L2_LKUP, 50,
> + ds_cstr(match),
> + ds_cstr(actions),
> + &op->nbsp->header_);
> + }
> + }
> +}
> +
> struct bfd_entry {
> struct hmap_node hmap_node;
>
> @@ -11711,16 +11743,17 @@ build_gw_lrouter_nat_flows_for_lb(struct
> lrouter_nat_lb_flows_ctx *ctx,
> }
>
> static void
> -build_lrouter_nat_flows_for_lb(struct ovn_lb_vip *lb_vip,
> - struct ovn_lb_datapaths *lb_dps,
> - struct ovn_northd_lb_vip *vips_nb,
> - const struct ovn_datapaths *lr_datapaths,
> - const struct lr_nat_table *lr_nats,
> - struct hmap *lflows,
> - struct ds *match, struct ds *action,
> - const struct shash *meter_groups,
> - const struct chassis_features *features,
> - const struct hmap *svc_monitor_map)
> +build_lrouter_nat_flows_for_lb(
> + struct ovn_lb_vip *lb_vip,
> + struct ovn_lb_datapaths *lb_dps,
> + struct ovn_northd_lb_vip *vips_nb,
> + const struct ovn_datapaths *lr_datapaths,
> + const struct lr_stateful_table *lr_stateful_table,
> + struct hmap *lflows,
> + struct ds *match, struct ds *action,
> + const struct shash *meter_groups,
> + const struct chassis_features *features,
> + const struct hmap *svc_monitor_map)
> {
> const struct ovn_northd_lb *lb = lb_dps->lb;
> bool ipv4 = lb_vip->address_family == AF_INET;
> @@ -11821,9 +11854,11 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip
> *lb_vip,
> struct ovn_datapath *od = lr_datapaths->array[index];
> enum lrouter_nat_lb_flow_type type;
>
> - const struct lr_nat_record *lrnat_rec =
> - lr_nat_table_find_by_index(lr_nats, od->index);
> - ovs_assert(lrnat_rec);
> + const struct lr_stateful_record *lr_stateful_rec =
> + lr_stateful_table_find_by_index(lr_stateful_table, od->index);
> + ovs_assert(lr_stateful_rec);
> +
> + const struct lr_nat_record *lrnat_rec = lr_stateful_rec->lrnat_rec;
> if (lb->skip_snat) {
> type = LROUTER_NAT_LB_FLOW_SKIP_SNAT;
> } else if (!lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs)
> @@ -11973,7 +12008,7 @@ build_lrouter_flows_for_lb(struct ovn_lb_datapaths
> *lb_dps,
> struct hmap *lflows,
> const struct shash *meter_groups,
> const struct ovn_datapaths *lr_datapaths,
> - const struct lr_nat_table *lr_nats,
> + const struct lr_stateful_table *lr_stateful_table,
> const struct chassis_features *features,
> const struct hmap *svc_monitor_map,
> struct ds *match, struct ds *action)
> @@ -11989,8 +12024,8 @@ build_lrouter_flows_for_lb(struct ovn_lb_datapaths
> *lb_dps,
> struct ovn_lb_vip *lb_vip = &lb->vips[i];
>
> build_lrouter_nat_flows_for_lb(lb_vip, lb_dps, &lb->vips_nb[i],
> - lr_datapaths, lr_nats, lflows, match,
> - action, meter_groups, features,
> + lr_datapaths, lr_stateful_table,
> lflows,
> + match, action, meter_groups, features,
> svc_monitor_map);
>
> if (!build_empty_lb_event_flow(lb_vip, lb, match, action)) {
> @@ -12127,7 +12162,7 @@ lrouter_dnat_and_snat_is_stateless(const struct
> nbrec_nat *nat)
> * and action says "next" instead of ct*.
> */
> static inline void
> -lrouter_nat_add_ext_ip_match(struct ovn_datapath *od,
> +lrouter_nat_add_ext_ip_match(const struct ovn_datapath *od,
> struct hmap *lflows, struct ds *match,
> const struct nbrec_nat *nat,
> bool is_v6, bool is_src, int cidr_bits)
> @@ -12191,7 +12226,7 @@ lrouter_nat_add_ext_ip_match(struct ovn_datapath *od,
> * with the given priority.
> */
> static void
> -build_lrouter_arp_flow(struct ovn_datapath *od, struct ovn_port *op,
> +build_lrouter_arp_flow(const struct ovn_datapath *od, struct ovn_port *op,
> const char *ip_address, const char *eth_addr,
> struct ds *extra_match, bool drop, uint16_t priority,
> const struct ovsdb_idl_row *hint,
> @@ -12240,7 +12275,7 @@ build_lrouter_arp_flow(struct ovn_datapath *od,
> struct ovn_port *op,
> * 'sn_ip_address'.
> */
> static void
> -build_lrouter_nd_flow(struct ovn_datapath *od, struct ovn_port *op,
> +build_lrouter_nd_flow(const struct ovn_datapath *od, struct ovn_port *op,
> const char *action, const char *ip_address,
> const char *sn_ip_address, const char *eth_addr,
> struct ds *extra_match, bool drop, uint16_t priority,
> @@ -12294,7 +12329,7 @@ build_lrouter_nd_flow(struct ovn_datapath *od, struct
> ovn_port *op,
> }
>
> static void
> -build_lrouter_nat_arp_nd_flow(struct ovn_datapath *od,
> +build_lrouter_nat_arp_nd_flow(const struct ovn_datapath *od,
> struct ovn_nat *nat_entry,
> struct hmap *lflows,
> const struct shash *meter_groups)
> @@ -12390,7 +12425,6 @@ build_lrouter_port_nat_arp_nd_flow(struct ovn_port
> *op,
>
> static void
> build_lrouter_drop_own_dest(struct ovn_port *op,
> - const struct lr_nat_record *lrnat_rec,
> const struct lr_stateful_record *lr_stateful_rec,
> enum ovn_stage stage,
> uint16_t priority, bool drop_snat_ip,
> @@ -12402,9 +12436,9 @@ build_lrouter_drop_own_dest(struct ovn_port *op,
> for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> const char *ip = op->lrp_networks.ipv4_addrs[i].addr_s;
>
> - bool router_ip_in_snat_ips = !!shash_find(&lrnat_rec->snat_ips,
> - ip);
> - bool router_ip_in_lb_ips = lr_stateful_rec &&
> + bool router_ip_in_snat_ips =
> + !!shash_find(&lr_stateful_rec->lrnat_rec->snat_ips, ip);
> + bool router_ip_in_lb_ips =
> !!sset_find(&lr_stateful_rec->lb_ips->ips_v4, ip);
> bool drop_router_ip = (drop_snat_ip == (router_ip_in_snat_ips ||
> router_ip_in_lb_ips));
> @@ -12432,9 +12466,9 @@ build_lrouter_drop_own_dest(struct ovn_port *op,
> for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
> const char *ip = op->lrp_networks.ipv6_addrs[i].addr_s;
>
> - bool router_ip_in_snat_ips = !!shash_find(&lrnat_rec->snat_ips,
> - ip);
> - bool router_ip_in_lb_ips = lr_stateful_rec &&
> + bool router_ip_in_snat_ips =
> + !!shash_find(&lr_stateful_rec->lrnat_rec->snat_ips, ip);
> + bool router_ip_in_lb_ips =
> !!sset_find(&lr_stateful_rec->lb_ips->ips_v6, ip);
> bool drop_router_ip = (drop_snat_ip == (router_ip_in_snat_ips ||
> router_ip_in_lb_ips));
> @@ -12459,7 +12493,8 @@ build_lrouter_drop_own_dest(struct ovn_port *op,
> }
>
> static void
> -build_lrouter_force_snat_flows(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_force_snat_flows(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const char *ip_version, const char *ip_addr,
> const char *context)
> {
> @@ -13530,10 +13565,8 @@ routable_addresses_to_lflows(struct hmap *lflows,
> struct ovn_port *router_port,
>
> /* This function adds ARP resolve flows related to a LRP. */
> static void
> -build_arp_resolve_flows_for_lrp(
> - struct ovn_port *op, const struct lr_nat_record *lrnat_rec,
> - const struct lr_stateful_record *lr_stateful_rec,
> - struct hmap *lflows, struct ds *match, struct ds *actions)
> +build_arp_resolve_flows_for_lrp(struct ovn_port *op, struct hmap *lflows,
> + struct ds *match, struct ds *actions)
> {
> ovs_assert(op->nbrp);
> /* This is a logical router port. If next-hop IP address in
> @@ -13602,15 +13635,6 @@ build_arp_resolve_flows_for_lrp(
> &op->nbrp->header_);
> }
> }
> -
> - /* Drop IP traffic destined to router owned IPs. Part of it is dropped
> - * in stage "lr_in_ip_input" but traffic that could have been unSNATed
> - * but didn't match any existing session might still end up here.
> - *
> - * Priority 2.
> - */
> - build_lrouter_drop_own_dest(op, lrnat_rec, lr_stateful_rec,
> - S_ROUTER_IN_ARP_RESOLVE, 2, true, lflows);
> }
>
> /* This function adds ARP resolve flows related to a LSP. */
> @@ -13618,7 +13642,6 @@ static void
> build_arp_resolve_flows_for_lsp(
> struct ovn_port *op, struct hmap *lflows,
> const struct hmap *lr_ports,
> - const struct lr_stateful_table *lr_stateful_table,
> struct ds *match, struct ds *actions)
> {
> ovs_assert(op->nbsp);
> @@ -13759,15 +13782,50 @@ build_arp_resolve_flows_for_lsp(
> ds_cstr(match), ds_cstr(actions),
> &op->nbsp->header_);
> }
> + }
> + }
> +}
>
> - if (smap_get(&peer->od->nbr->options, "chassis")
> - || peer->cr_port) {
> - const struct lr_stateful_record *lr_stateful_rec =
> - lr_stateful_table_find_by_index(lr_stateful_table,
> - router_port->od->index);
> - routable_addresses_to_lflows(lflows, router_port, peer,
> - lr_stateful_rec, match,
> actions);
> - }
> +static void
> +build_arp_resolve_flows_for_lsp_routable_addresses(
> + struct ovn_port *op, struct hmap *lflows,
> + const struct hmap *lr_ports,
> + const struct lr_stateful_table *lr_stateful_table,
> + struct ds *match, struct ds *actions)
> +{
> + if (!lsp_is_router(op->nbsp)) {
> + return;
> + }
> +
> + struct ovn_port *peer = ovn_port_get_peer(lr_ports, op);
> + if (!peer || !peer->nbrp) {
> + return;
> + }
> +
> + if (peer->od->nbr &&
> + smap_get_bool(&peer->od->nbr->options,
> + "dynamic_neigh_routers", false)) {
> + return;
> + }
> +
> + for (size_t i = 0; i < op->od->n_router_ports; i++) {
> + struct ovn_port *router_port =
> + ovn_port_get_peer(lr_ports, op->od->router_ports[i]);
> + if (!router_port || !router_port->nbrp) {
> + continue;
> + }
> +
> + /* Skip the router port under consideration. */
> + if (router_port == peer) {
> + continue;
> + }
> +
> + if (smap_get(&peer->od->nbr->options, "chassis") || peer->cr_port) {
> + const struct lr_stateful_record *lr_stateful_rec;
> + lr_stateful_rec = lr_stateful_table_find_by_index(
> + lr_stateful_table, router_port->od->index);
> + routable_addresses_to_lflows(lflows, router_port, peer,
> + lr_stateful_rec, match, actions);
> }
> }
> }
> @@ -13944,7 +14002,6 @@ build_check_pkt_len_flows_for_lrouter(
> static void
> build_gateway_redirect_flows_for_lrouter(
> struct ovn_datapath *od, struct hmap *lflows,
> - const struct lr_nat_table *lr_nats,
> struct ds *match, struct ds *actions)
> {
> ovs_assert(od->nbr);
> @@ -13961,7 +14018,6 @@ build_gateway_redirect_flows_for_lrouter(
> }
>
> const struct ovsdb_idl_row *stage_hint = NULL;
> - bool add_def_flow = true;
>
> if (od->l3dgw_ports[i]->nbrp) {
> stage_hint = &od->l3dgw_ports[i]->nbrp->header_;
> @@ -13980,14 +14036,33 @@ build_gateway_redirect_flows_for_lrouter(
> ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
> ds_cstr(match), ds_cstr(actions),
> stage_hint);
> + }
>
> - const struct lr_nat_record *lrnat_rec = lr_nat_table_find_by_index(
> - lr_nats, od->index);
> + /* Packets are allowed by default. */
> + ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 0, "1", "next;");
> +}
>
> - if (!lrnat_rec) {
> +/* Logical router ingress table GW_REDIRECT: Gateway redirect. */
> +static void
> +build_lr_gateway_redirect_flows_for_nats(
> + const struct ovn_datapath *od, const struct lr_nat_record *lrnat_rec,
> + struct hmap *lflows, struct ds *match, struct ds *actions)
> +{
> + ovs_assert(od->nbr);
> + for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> + if (l3dgw_port_has_associated_vtep_lports(od->l3dgw_ports[i])) {
> + /* Skip adding redirect lflow for vtep-enabled l3dgw ports.
> + * Traffic from hypervisor to VTEP (ramp) switch should go in
> + * distributed manner. Only returning routed traffic must go
> + * through centralized gateway (or ha-chassis-group).
> + * This assumes that attached logical switch with vtep lport(s)
> has
> + * no localnet port(s) for NAT. Otherwise centralized NAT will
> not
> + * work. */
> continue;
> }
>
> + bool add_def_flow = true;
> +
> for (int j = 0; j < lrnat_rec->n_nat_entries; j++) {
> const struct ovn_nat *nat = &lrnat_rec->nat_entries[j];
>
> @@ -13996,6 +14071,12 @@ build_gateway_redirect_flows_for_lrouter(
> continue;
> }
>
> + const struct ovsdb_idl_row *stage_hint = NULL;
> +
> + if (od->l3dgw_ports[i]->nbrp) {
> + stage_hint = &od->l3dgw_ports[i]->nbrp->header_;
> + }
> +
> struct ds match_ext = DS_EMPTY_INITIALIZER;
> struct nbrec_address_set *as = nat->nb->allowed_ext_ips
> ? nat->nb->allowed_ext_ips : nat->nb->exempted_ext_ips;
> @@ -14025,9 +14106,6 @@ build_gateway_redirect_flows_for_lrouter(
> ds_destroy(&match_ext);
> }
> }
> -
> - /* Packets are allowed by default. */
> - ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 0, "1", "next;");
> }
>
> /* Local router ingress table ARP_REQUEST: ARP request.
> @@ -14426,8 +14504,8 @@ build_ipv6_input_flows_for_lrouter_port(
> }
>
> static void
> -build_lrouter_arp_nd_for_datapath(struct ovn_datapath *od,
> - const struct lr_nat_table *lr_nats,
> +build_lrouter_arp_nd_for_datapath(const struct ovn_datapath *od,
> + const struct lr_nat_record *lrnat_rec,
> struct hmap *lflows,
> const struct shash *meter_groups)
> {
> @@ -14444,10 +14522,6 @@ build_lrouter_arp_nd_for_datapath(struct
> ovn_datapath *od,
> * port to handle the special cases. In case we get the packet
> * on a regular port, just reply with the port's ETH address.
> */
> - const struct lr_nat_record *lrnat_rec = lr_nat_table_find_by_index(
> - lr_nats, od->index);
> - ovs_assert(lrnat_rec);
> -
> for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
> struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>
> @@ -14485,8 +14559,6 @@ build_lrouter_arp_nd_for_datapath(struct ovn_datapath
> *od,
> static void
> build_lrouter_ipv4_ip_input(struct ovn_port *op,
> struct hmap *lflows,
> - const struct lr_nat_record *lrnat_rec,
> - const struct lr_stateful_record *lr_stateful_rec,
> struct ds *match, struct ds *actions,
> const struct shash *meter_groups)
> {
> @@ -14611,41 +14683,6 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> &op->nbrp->header_, lflows);
> }
>
> - if (lr_stateful_rec && sset_count(
> - &lr_stateful_rec->lb_ips->ips_v4_reachable)) {
> - ds_clear(match);
> - if (is_l3dgw_port(op)) {
> - ds_put_format(match, "is_chassis_resident(%s)",
> - op->cr_port->json_key);
> - }
> -
> - /* Create a single ARP rule for all IPs that are used as VIPs. */
> - char *lb_ips_v4_as = lr_lb_address_set_ref(op->od->tunnel_key,
> - AF_INET);
> - build_lrouter_arp_flow(op->od, op, lb_ips_v4_as,
> - REG_INPORT_ETH_ADDR,
> - match, false, 90, NULL, lflows);
> - free(lb_ips_v4_as);
> - }
> -
> - if (lr_stateful_rec && sset_count(
> - &lr_stateful_rec->lb_ips->ips_v6_reachable)) {
> - ds_clear(match);
> -
> - if (is_l3dgw_port(op)) {
> - ds_put_format(match, "is_chassis_resident(%s)",
> - op->cr_port->json_key);
> - }
> -
> - /* Create a single ND rule for all IPs that are used as VIPs. */
> - char *lb_ips_v6_as = lr_lb_address_set_ref(op->od->tunnel_key,
> - AF_INET6);
> - build_lrouter_nd_flow(op->od, op, "nd_na", lb_ips_v6_as, NULL,
> - REG_INPORT_ETH_ADDR, match, false, 90,
> - NULL, lflows, meter_groups);
> - free(lb_ips_v6_as);
> - }
> -
> if (!op->od->is_gw_router && !op->od->n_l3dgw_ports) {
> /* UDP/TCP/SCTP port unreachable. */
> for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> @@ -14720,20 +14757,55 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> &op->nbrp->header_);
> }
> }
> +}
>
> - /* Drop IP traffic destined to router owned IPs except if the IP is
> - * also a SNAT IP. Those are dropped later, in stage
> - * "lr_in_arp_resolve", if unSNAT was unsuccessful.
> - *
> - * If lrnat_rec->lb_force_snat_router_ip is true, it means the IP of the
> - * router port is also SNAT IP.
> - *
> - * Priority 60.
> - */
> - if (!lrnat_rec->lb_force_snat_router_ip) {
> - build_lrouter_drop_own_dest(op, lrnat_rec, lr_stateful_rec,
> - S_ROUTER_IN_IP_INPUT, 60, false, lflows);
> +/* Logical router ingress table 3: IP Input for IPv4. */
> +static void
> +build_lrouter_ipv4_ip_input_for_lbnats(
> + struct ovn_port *op, struct hmap *lflows,
> + const struct lr_stateful_record *lr_stateful_rec,
> + struct ds *match, const struct shash *meter_groups)
> +{
> + ovs_assert(op->nbrp);
> + /* No ingress packets are accepted on a chassisredirect
> + * port, so no need to program flows for that port. */
> + if (is_cr_port(op)) {
> + return;
> + }
> +
> + if (sset_count(&lr_stateful_rec->lb_ips->ips_v4_reachable)) {
> + ds_clear(match);
> + if (is_l3dgw_port(op)) {
> + ds_put_format(match, "is_chassis_resident(%s)",
> + op->cr_port->json_key);
> + }
> +
> + /* Create a single ARP rule for all IPs that are used as VIPs. */
> + char *lb_ips_v4_as = lr_lb_address_set_ref(op->od->tunnel_key,
> + AF_INET);
> + build_lrouter_arp_flow(op->od, op, lb_ips_v4_as,
> + REG_INPORT_ETH_ADDR,
> + match, false, 90, NULL, lflows);
> + free(lb_ips_v4_as);
> + }
> +
> + if (sset_count(&lr_stateful_rec->lb_ips->ips_v6_reachable)) {
> + ds_clear(match);
> +
> + if (is_l3dgw_port(op)) {
> + ds_put_format(match, "is_chassis_resident(%s)",
> + op->cr_port->json_key);
> + }
> +
> + /* Create a single ND rule for all IPs that are used as VIPs. */
> + char *lb_ips_v6_as = lr_lb_address_set_ref(op->od->tunnel_key,
> + AF_INET6);
> + build_lrouter_nd_flow(op->od, op, "nd_na", lb_ips_v6_as, NULL,
> + REG_INPORT_ETH_ADDR, match, false, 90,
> + NULL, lflows, meter_groups);
> + free(lb_ips_v6_as);
> }
> +
> /* ARP / ND handling for external IP addresses.
> *
> * DNAT and SNAT IP addresses are external IP addresses that need ARP
> @@ -14747,8 +14819,9 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> return;
> }
>
> - for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
> - struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
> + for (size_t i = 0; i < lr_stateful_rec->lrnat_rec->n_nat_entries; i++) {
> + struct ovn_nat *nat_entry =
> + &lr_stateful_rec->lrnat_rec->nat_entries[i];
>
> /* Skip entries we failed to parse. */
> if (!nat_entry_is_valid(nat_entry)) {
> @@ -14767,7 +14840,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>
> /* Now handle SNAT entries too, one per unique SNAT IP. */
> struct shash_node *snat_snode;
> - SHASH_FOR_EACH (snat_snode, &lrnat_rec->snat_ips) {
> + SHASH_FOR_EACH (snat_snode, &lr_stateful_rec->lrnat_rec->snat_ips) {
> struct ovn_snat_ip *snat_ip = snat_snode->data;
>
> if (ovs_list_is_empty(&snat_ip->snat_entries)) {
> @@ -14783,7 +14856,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
> }
>
> static void
> -build_lrouter_in_unsnat_match(struct ovn_datapath *od,
> +build_lrouter_in_unsnat_match(const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds *match,
> bool distributed_nat, bool is_v6,
> struct ovn_port *l3dgw_port)
> @@ -14810,7 +14883,7 @@ build_lrouter_in_unsnat_match(struct ovn_datapath *od,
>
> static void
> build_lrouter_in_unsnat_stateless_flow(struct hmap *lflows,
> - struct ovn_datapath *od,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat,
> struct ds *match,
> bool distributed_nat, bool is_v6,
> @@ -14832,7 +14905,7 @@ build_lrouter_in_unsnat_stateless_flow(struct hmap
> *lflows,
>
> static void
> build_lrouter_in_unsnat_in_czone_flow(struct hmap *lflows,
> - struct ovn_datapath *od,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat,
> struct ds *match, bool distributed_nat,
> bool is_v6, struct ovn_port
> *l3dgw_port)
> @@ -14865,7 +14938,8 @@ build_lrouter_in_unsnat_in_czone_flow(struct hmap
> *lflows,
> }
>
> static void
> -build_lrouter_in_unsnat_flow(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_in_unsnat_flow(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds *match,
> bool distributed_nat, bool is_v6,
> struct ovn_port *l3dgw_port)
> @@ -14886,7 +14960,8 @@ build_lrouter_in_unsnat_flow(struct hmap *lflows,
> struct ovn_datapath *od,
> }
>
> static void
> -build_lrouter_in_dnat_flow(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_in_dnat_flow(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct lr_nat_record *lrnat_rec,
> const struct nbrec_nat *nat, struct ds *match,
> struct ds *actions, bool distributed_nat,
> @@ -14957,7 +15032,8 @@ build_lrouter_in_dnat_flow(struct hmap *lflows,
> struct ovn_datapath *od,
> }
>
> static void
> -build_lrouter_out_undnat_flow(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_out_undnat_flow(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds *match,
> struct ds *actions, bool distributed_nat,
> struct eth_addr mac, bool is_v6,
> @@ -15007,7 +15083,8 @@ build_lrouter_out_undnat_flow(struct hmap *lflows,
> struct ovn_datapath *od,
> }
>
> static void
> -build_lrouter_out_is_dnat_local(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_out_is_dnat_local(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds
> *match,
> struct ds *actions, bool distributed_nat,
> bool is_v6, struct ovn_port *l3dgw_port)
> @@ -15037,7 +15114,8 @@ build_lrouter_out_is_dnat_local(struct hmap *lflows,
> struct ovn_datapath *od,
> }
>
> static void
> -build_lrouter_out_snat_match(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_out_snat_match(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds *match,
> bool distributed_nat, int cidr_bits, bool is_v6,
> struct ovn_port *l3dgw_port)
> @@ -15066,7 +15144,7 @@ build_lrouter_out_snat_match(struct hmap *lflows,
> struct ovn_datapath *od,
>
> static void
> build_lrouter_out_snat_stateless_flow(struct hmap *lflows,
> - struct ovn_datapath *od,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat,
> struct ds *match, struct ds *actions,
> bool distributed_nat,
> @@ -15109,7 +15187,7 @@ build_lrouter_out_snat_stateless_flow(struct hmap
> *lflows,
>
> static void
> build_lrouter_out_snat_in_czone_flow(struct hmap *lflows,
> - struct ovn_datapath *od,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat,
> struct ds *match,
> struct ds *actions, bool
> distributed_nat,
> @@ -15170,7 +15248,8 @@ build_lrouter_out_snat_in_czone_flow(struct hmap
> *lflows,
> }
>
> static void
> -build_lrouter_out_snat_flow(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_out_snat_flow(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds *match,
> struct ds *actions, bool distributed_nat,
> struct eth_addr mac, int cidr_bits, bool is_v6,
> @@ -15217,9 +15296,10 @@ build_lrouter_out_snat_flow(struct hmap *lflows,
> struct ovn_datapath *od,
> static void
> build_lrouter_ingress_nat_check_pkt_len(struct hmap *lflows,
> const struct nbrec_nat *nat,
> - struct ovn_datapath *od, bool is_v6,
> - struct ds *match, struct ds *actions,
> - int mtu, struct ovn_port *l3dgw_port,
> + const struct ovn_datapath *od,
> + bool is_v6, struct ds *match,
> + struct ds *actions, int mtu,
> + struct ovn_port *l3dgw_port,
> const struct shash *meter_groups)
> {
> ds_clear(match);
> @@ -15286,7 +15366,8 @@ build_lrouter_ingress_nat_check_pkt_len(struct hmap
> *lflows,
> }
>
> static void
> -build_lrouter_ingress_flow(struct hmap *lflows, struct ovn_datapath *od,
> +build_lrouter_ingress_flow(struct hmap *lflows,
> + const struct ovn_datapath *od,
> const struct nbrec_nat *nat, struct ds *match,
> struct ds *actions, struct eth_addr mac,
> bool distributed_nat, bool is_v6,
> @@ -15336,7 +15417,8 @@ build_lrouter_ingress_flow(struct hmap *lflows,
> struct ovn_datapath *od,
> }
>
> static int
> -lrouter_check_nat_entry(struct ovn_datapath *od, const struct nbrec_nat *nat,
> +lrouter_check_nat_entry(const struct ovn_datapath *od,
> + const struct nbrec_nat *nat,
> const struct hmap *lr_ports, ovs_be32 *mask,
> bool *is_v6, int *cidr_bits, struct eth_addr *mac,
> bool *distributed, struct ovn_port **nat_l3dgw_port)
> @@ -15463,15 +15545,8 @@ lrouter_check_nat_entry(struct ovn_datapath *od,
> const struct nbrec_nat *nat,
> }
>
> /* NAT, Defrag and load balancing. */
> -static void
> -build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od, struct hmap *lflows,
> - const struct hmap *ls_ports,
> - const struct hmap *lr_ports,
> - const struct lr_nat_table *lr_nats,
> - struct ds *match,
> - struct ds *actions,
> - const struct shash *meter_groups,
> - const struct chassis_features *features)
> +static void build_lr_nat_defrag_and_lb_default_flows(struct ovn_datapath *od,
> + struct hmap *lflows)
> {
> ovs_assert(od->nbr);
>
> @@ -15488,6 +15563,21 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath
> *od, struct hmap *lflows,
> ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;");
> ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;");
>
> + /* Send the IPv6 NS packets to next table. When ovn-controller
> + * generates IPv6 NS (for the action - nd_ns{}), the injected
> + * packet would go through conntrack - which is not required. */
> + ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 120, "nd_ns", "next;");
> +}
> +
> +static void
> +build_lrouter_nat_defrag_and_lb(
> + const struct lr_stateful_record *lr_stateful_rec,
> + const struct ovn_datapath *od, struct hmap *lflows,
> + const struct hmap *ls_ports, const struct hmap *lr_ports,
> + struct ds *match, struct ds *actions,
> + const struct shash *meter_groups,
> + const struct chassis_features *features)
> +{
> const char *ct_flag_reg = features->ct_no_masked_label
> ? "ct_mark"
> : "ct_label";
> @@ -15565,11 +15655,6 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath
> *od, struct hmap *lflows,
> "ip && ct.new", "ct_commit { } ; next; ");
> }
>
> - /* Send the IPv6 NS packets to next table. When ovn-controller
> - * generates IPv6 NS (for the action - nd_ns{}), the injected
> - * packet would go through conntrack - which is not required. */
> - ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 120, "nd_ns", "next;");
> -
> /* NAT rules are only valid on Gateway routers and routers with
> * l3dgw_ports (router has port(s) with gateway chassis
> * specified). */
> @@ -15578,8 +15663,7 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath
> *od, struct hmap *lflows,
> }
>
> struct sset nat_entries = SSET_INITIALIZER(&nat_entries);
> - const struct lr_nat_record *lrnat_rec =
> lr_nat_table_find_by_index(lr_nats,
> -
> od->index);
> + const struct lr_nat_record *lrnat_rec = lr_stateful_rec->lrnat_rec;
> ovs_assert(lrnat_rec);
>
> bool dnat_force_snat_ip =
> @@ -15862,7 +15946,129 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath
> *od, struct hmap *lflows,
> sset_destroy(&nat_entries);
> }
>
> +static void
> +build_lsp_lflows_for_lbnats(struct ovn_port *lsp,
> + const struct lr_stateful_record *lr_stateful_rec,
> + const struct lr_stateful_table
> *lr_stateful_table,
> + const struct hmap *lr_ports,
> + struct hmap *lflows,
> + struct ds *match,
> + struct ds *actions)
> +{
> + ovs_assert(lsp->nbsp);
> + ovs_assert(lsp->peer);
> + start_collecting_lflows();
> + build_lswitch_rport_arp_req_flows_for_lbnats(
> + lsp->peer, lr_stateful_rec, lsp->od, lsp,
> + lflows, &lsp->nbsp->header_);
> + build_ip_routing_flows_for_router_type_lsp(lsp, lr_stateful_table,
> + lr_ports, lflows);
> + build_arp_resolve_flows_for_lsp_routable_addresses(
> + lsp, lflows, lr_ports, lr_stateful_table, match, actions);
> + build_lswitch_ip_unicast_lookup_for_nats(lsp, lr_stateful_rec, lflows,
> + match, actions);
> + link_ovn_port_to_lflows(lsp, &collected_lflows);
> + end_collecting_lflows();
> +}
> +
> +static void
> +build_lbnat_lflows_iterate_by_lsp(
> + struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> + const struct hmap *lr_ports, struct ds *match, struct ds *actions,
> + struct hmap *lflows)
> +{
> + ovs_assert(op->nbsp);
> +
> + if (!lsp_is_router(op->nbsp) || !op->peer) {
> + return;
> + }
> +
> + const struct lr_stateful_record *lr_stateful_rec;
> + lr_stateful_rec = lr_stateful_table_find_by_index(lr_stateful_table,
> + op->peer->od->index);
> + ovs_assert(lr_stateful_rec);
> +
> + build_lsp_lflows_for_lbnats(op, lr_stateful_rec, lr_stateful_table,
> + lr_ports, lflows, match, actions);
> +}
> +
> +static void
> +build_lrp_lflows_for_lbnats(struct ovn_port *op,
> + const struct lr_stateful_record *lr_stateful_rec,
> + const struct shash *meter_groups,
> + struct ds *match, struct ds *actions,
> + struct hmap *lflows)
> +{
> + /* Drop IP traffic destined to router owned IPs except if the IP is
> + * also a SNAT IP. Those are dropped later, in stage
> + * "lr_in_arp_resolve", if unSNAT was unsuccessful.
> + *
> + * If lrnat_rec->lb_force_snat_router_ip is true, it means the IP of the
> + * router port is also SNAT IP.
> + *
> + * Priority 60.
> + */
> + if (!lr_stateful_rec->lrnat_rec->lb_force_snat_router_ip) {
> + build_lrouter_drop_own_dest(op, lr_stateful_rec,
> + S_ROUTER_IN_IP_INPUT, 60, false, lflows);
> + }
> +
> + /* Drop IP traffic destined to router owned IPs. Part of it is dropped
> + * in stage "lr_in_ip_input" but traffic that could have been unSNATed
> + * but didn't match any existing session might still end up here.
> + *
> + * Priority 2.
> + */
> + build_lrouter_drop_own_dest(op, lr_stateful_rec,
> + S_ROUTER_IN_ARP_RESOLVE, 2, true, lflows);
> +
> + build_lrouter_ipv4_ip_input_for_lbnats(op, lflows, lr_stateful_rec,
> + match, meter_groups);
> + build_lrouter_force_snat_flows_op(op, lr_stateful_rec->lrnat_rec, lflows,
> + match, actions);
> +}
> +
> +static void
> +build_lbnat_lflows_iterate_by_lrp(
> + struct ovn_port *op, const struct lr_stateful_table *lr_stateful_table,
> + const struct shash *meter_groups, struct ds *match,
> + struct ds *actions, struct hmap *lflows)
> +{
> + ovs_assert(op->nbrp);
> +
> + const struct lr_stateful_record *lr_stateful_rec;
> + lr_stateful_rec = lr_stateful_table_find_by_index(lr_stateful_table,
> + op->od->index);
> + ovs_assert(lr_stateful_rec);
>
> + build_lrp_lflows_for_lbnats(op, lr_stateful_rec, meter_groups, match,
> + actions, lflows);
> +}
> +
> +static void
> +build_lr_stateful_flows(const struct lr_stateful_record *lr_stateful_rec,
> + const struct ovn_datapaths *lr_datapaths,
> + struct hmap *lflows,
> + const struct hmap *ls_ports,
> + const struct hmap *lr_ports,
> + struct ds *match,
> + struct ds *actions,
> + const struct shash *meter_groups,
> + const struct chassis_features *features)
> +{
> + const struct ovn_datapath *od =
> + ovn_datapaths_find_by_index(lr_datapaths, lr_stateful_rec->lr_index);
> + ovs_assert(od->nbr);
> + ovs_assert(uuid_equals(&od->nbr->header_.uuid,
> + &lr_stateful_rec->nbr_uuid));
> + build_lrouter_nat_defrag_and_lb(lr_stateful_rec, od, lflows, ls_ports,
> + lr_ports, match, actions,
> + meter_groups, features);
> + build_lr_gateway_redirect_flows_for_nats(od, lr_stateful_rec->lrnat_rec,
> + lflows, match, actions);
> + build_lrouter_arp_nd_for_datapath(od, lr_stateful_rec->lrnat_rec, lflows,
> + meter_groups);
> +}
>
> struct lswitch_flow_build_info {
> const struct ovn_datapaths *ls_datapaths;
> @@ -15870,7 +16076,6 @@ struct lswitch_flow_build_info {
> const struct hmap *ls_ports;
> const struct hmap *lr_ports;
> const struct ls_port_group_table *ls_port_groups;
> - const struct lr_nat_table *lr_nats;
> const struct lr_stateful_table *lr_stateful_table;
> struct hmap *lflows;
> struct hmap *igmp_groups;
> @@ -15937,17 +16142,13 @@ build_lswitch_and_lrouter_iterate_by_lr(struct
> ovn_datapath *od,
> build_check_pkt_len_flows_for_lrouter(od, lsi->lflows, lsi->lr_ports,
> &lsi->match, &lsi->actions,
> lsi->meter_groups);
> - build_gateway_redirect_flows_for_lrouter(od, lsi->lflows, lsi->lr_nats,
> - &lsi->match, &lsi->actions);
> + 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->meter_groups);
> build_misc_local_traffic_drop_flows_for_lrouter(od, lsi->lflows);
> - build_lrouter_arp_nd_for_datapath(od, lsi->lr_nats, lsi->lflows,
> - lsi->meter_groups);
> - build_lrouter_nat_defrag_and_lb(od, lsi->lflows, lsi->ls_ports,
> - lsi->lr_ports,lsi->lr_nats, &lsi->match,
> - &lsi->actions, lsi->meter_groups,
> - lsi->features);
> +
> + build_lr_nat_defrag_and_lb_default_flows(od, lsi->lflows);
> build_lrouter_lb_affinity_default_flows(od, lsi->lflows);
> }
>
> @@ -15955,15 +16156,13 @@ build_lswitch_and_lrouter_iterate_by_lr(struct
> ovn_datapath *od,
> * switch port.
> */
> static void
> -build_lswitch_and_lrouter_iterate_by_lsp(
> - struct ovn_port *op, const struct hmap *ls_ports,
> - const struct hmap *lr_ports,
> - const struct lr_nat_table *lr_nats,
> - const struct lr_stateful_table *lr_stateful_table,
> - const struct shash *meter_groups,
> - struct ds *match,
> - struct ds *actions,
> - struct hmap *lflows)
> +build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op,
> + const struct hmap *ls_ports,
> + const struct hmap *lr_ports,
> + const struct shash *meter_groups,
> + struct ds *match,
> + struct ds *actions,
> + struct hmap *lflows)
> {
> ovs_assert(op->nbsp);
> start_collecting_lflows();
> @@ -15977,14 +16176,11 @@ build_lswitch_and_lrouter_iterate_by_lsp(
> build_lswitch_dhcp_options_and_response(op, lflows, meter_groups);
> build_lswitch_external_port(op, lflows);
> build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, actions);
> - build_lswitch_ip_unicast_lookup(op, lr_nats, lr_stateful_table, lflows,
> - actions, match);
> + build_lswitch_ip_unicast_lookup(op, lflows, actions,
> + match);
>
> /* Build Logical Router Flows. */
> - build_ip_routing_flows_for_router_type_lsp(op, lr_stateful_table,
> lr_ports,
> - lflows);
> - build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, lr_stateful_table,
> - match, actions);
> + build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
>
> link_ovn_port_to_lflows(op, &collected_lflows);
> end_collecting_lflows();
> @@ -15999,12 +16195,6 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct
> ovn_port *op,
> {
> ovs_assert(op->nbrp);
>
> - const struct lr_nat_record *lrnet_rec = lr_nat_table_find_by_index(
> - lsi->lr_nats, op->od->index);
> - ovs_assert(lrnet_rec);
> -
> - const struct lr_stateful_record *lr_stateful_rec =
> - lr_stateful_table_find_by_index(lsi->lr_stateful_table,
> op->od->index);
> build_adm_ctrl_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> &lsi->actions);
> build_neigh_learning_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> @@ -16012,18 +16202,16 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct
> ovn_port *op,
> build_ip_routing_flows_for_lrp(op, lsi->lflows);
> build_ND_RA_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> &lsi->actions, lsi->meter_groups);
> - build_arp_resolve_flows_for_lrp(op, lrnet_rec, lr_stateful_rec,
> - lsi->lflows, &lsi->match, &lsi->actions);
> + build_arp_resolve_flows_for_lrp(op, lsi->lflows,
> + &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->meter_groups);
> - build_lrouter_ipv4_ip_input(op, lsi->lflows, lrnet_rec, lr_stateful_rec,
> - &lsi->match, &lsi->actions,
> lsi->meter_groups);
> - build_lrouter_force_snat_flows_op(op, lrnet_rec, lsi->lflows,
> &lsi->match,
> - &lsi->actions);
> + build_lrouter_ipv4_ip_input(op, lsi->lflows, &lsi->match, &lsi->actions,
> + lsi->meter_groups);
> build_lrouter_icmp_packet_toobig_admin_flows(op, lsi->lflows,
> &lsi->match,
> &lsi->actions);
> }
> @@ -16032,12 +16220,12 @@ static void *
> build_lflows_thread(void *arg)
> {
> struct worker_control *control = (struct worker_control *) arg;
> + const struct lr_stateful_record *lr_stateful_rec;
> struct lswitch_flow_build_info *lsi;
> -
> + struct ovn_igmp_group *igmp_group;
> + struct ovn_lb_datapaths *lb_dps;
> struct ovn_datapath *od;
> struct ovn_port *op;
> - struct ovn_lb_datapaths *lb_dps;
> - struct ovn_igmp_group *igmp_group;
> int bnum;
>
> while (!stop_parallel_processing()) {
> @@ -16082,10 +16270,15 @@ build_lflows_thread(void *arg)
> if (stop_parallel_processing()) {
> return NULL;
> }
> - build_lswitch_and_lrouter_iterate_by_lsp(
> - op, lsi->ls_ports, lsi->lr_ports, lsi->lr_nats,
> - lsi->lr_stateful_table, lsi->meter_groups,
> - &lsi->match, &lsi->actions, lsi->lflows);
> + build_lswitch_and_lrouter_iterate_by_lsp(op,
> lsi->ls_ports,
> + lsi->lr_ports,
> +
> lsi->meter_groups,
> + &lsi->match,
> + &lsi->actions,
> + lsi->lflows);
> + build_lbnat_lflows_iterate_by_lsp(
> + op, lsi->lr_stateful_table, lsi->lr_ports,
> &lsi->match,
> + &lsi->actions, lsi->lflows);
> }
> }
> for (bnum = control->id;
> @@ -16098,6 +16291,9 @@ build_lflows_thread(void *arg)
> return NULL;
> }
> build_lswitch_and_lrouter_iterate_by_lrp(op, lsi);
> + build_lbnat_lflows_iterate_by_lrp(
> + op, lsi->lr_stateful_table, lsi->meter_groups,
> + &lsi->match, &lsi->actions, lsi->lflows);
> }
> }
> for (bnum = control->id;
> @@ -16120,7 +16316,7 @@ build_lflows_thread(void *arg)
> build_lrouter_flows_for_lb(lb_dps, lsi->lflows,
> lsi->meter_groups,
> lsi->lr_datapaths,
> - lsi->lr_nats,
> + lsi->lr_stateful_table,
> lsi->features,
> lsi->svc_monitor_map,
> &lsi->match, &lsi->actions);
> @@ -16132,6 +16328,23 @@ build_lflows_thread(void *arg)
> &lsi->match, &lsi->actions);
> }
> }
> + for (bnum = control->id;
> + bnum <= lsi->lr_stateful_table->entries.mask;
> + bnum += control->pool->size)
> + {
> + LR_STATEFUL_TABLE_FOR_EACH_IN_P (lr_stateful_rec, bnum,
> + lsi->lr_stateful_table) {
> + if (stop_parallel_processing()) {
> + return NULL;
> + }
> + build_lr_stateful_flows(lr_stateful_rec,
> lsi->lr_datapaths,
> + lsi->lflows, lsi->ls_ports,
> + lsi->lr_ports, &lsi->match,
> + &lsi->actions,
> + lsi->meter_groups,
> + lsi->features);
> + }
> + }
> for (bnum = control->id;
> bnum <= lsi->igmp_groups->mask;
> bnum += control->pool->size)
> @@ -16192,7 +16405,6 @@ build_lswitch_and_lrouter_flows(
> const struct hmap *ls_ports,
> const struct hmap *lr_ports,
> const struct ls_port_group_table *ls_pgs,
> - const struct lr_nat_table *lr_nats,
> const struct lr_stateful_table *lr_stateful_table,
> struct hmap *lflows,
> struct hmap *igmp_groups,
> @@ -16223,7 +16435,6 @@ build_lswitch_and_lrouter_flows(
> lsiv[index].ls_ports = ls_ports;
> lsiv[index].lr_ports = lr_ports;
> lsiv[index].ls_port_groups = ls_pgs;
> - lsiv[index].lr_nats = lr_nats;
> lsiv[index].lr_stateful_table = lr_stateful_table;
> lsiv[index].igmp_groups = igmp_groups;
> lsiv[index].meter_groups = meter_groups;
> @@ -16249,17 +16460,18 @@ build_lswitch_and_lrouter_flows(
> }
> free(lsiv);
> } else {
> + const struct lr_stateful_record *lr_stateful_rec;
> + struct ovn_igmp_group *igmp_group;
> + struct ovn_lb_datapaths *lb_dps;
> struct ovn_datapath *od;
> struct ovn_port *op;
> - struct ovn_lb_datapaths *lb_dps;
> - struct ovn_igmp_group *igmp_group;
> +
> struct lswitch_flow_build_info lsi = {
> .ls_datapaths = ls_datapaths,
> .lr_datapaths = lr_datapaths,
> .ls_ports = ls_ports,
> .lr_ports = lr_ports,
> .ls_port_groups = ls_pgs,
> - .lr_nats = lr_nats,
> .lr_stateful_table = lr_stateful_table,
> .lflows = lflows,
> .igmp_groups = igmp_groups,
> @@ -16288,14 +16500,21 @@ build_lswitch_and_lrouter_flows(
> HMAP_FOR_EACH (op, key_node, ls_ports) {
> build_lswitch_and_lrouter_iterate_by_lsp(op, lsi.ls_ports,
> lsi.lr_ports,
> - lsi.lr_nats,
> - lsi.lr_stateful_table,
> lsi.meter_groups,
> - &lsi.match,
> &lsi.actions,
> + &lsi.match,
> + &lsi.actions,
> lsi.lflows);
> + build_lbnat_lflows_iterate_by_lsp(op, lsi.lr_stateful_table,
> + lsi.lr_ports, &lsi.match,
> + &lsi.actions, lsi.lflows);
> }
> HMAP_FOR_EACH (op, key_node, lr_ports) {
> build_lswitch_and_lrouter_iterate_by_lrp(op, &lsi);
> + build_lbnat_lflows_iterate_by_lrp(op, lsi.lr_stateful_table,
> + lsi.meter_groups,
> + &lsi.match,
> + &lsi.actions,
> + lsi.lflows);
> }
> stopwatch_stop(LFLOWS_PORTS_STOPWATCH_NAME, time_msec());
> stopwatch_start(LFLOWS_LBS_STOPWATCH_NAME, time_msec());
> @@ -16306,7 +16525,7 @@ build_lswitch_and_lrouter_flows(
> build_lrouter_defrag_flows_for_lb(lb_dps, lsi.lflows,
> lsi.lr_datapaths, &lsi.match);
> build_lrouter_flows_for_lb(lb_dps, lsi.lflows, lsi.meter_groups,
> - lsi.lr_datapaths, lsi.lr_nats,
> + lsi.lr_datapaths,
> lsi.lr_stateful_table,
> lsi.features, lsi.svc_monitor_map,
> &lsi.match, &lsi.actions);
> build_lswitch_flows_for_lb(lb_dps, lsi.lflows, lsi.meter_groups,
> @@ -16315,6 +16534,14 @@ build_lswitch_and_lrouter_flows(
> &lsi.match, &lsi.actions);
> }
> stopwatch_stop(LFLOWS_LBS_STOPWATCH_NAME, time_msec());
> + stopwatch_start(LFLOWS_LR_STATEFUL_STOPWATCH_NAME, time_msec());
> + LR_STATEFUL_TABLE_FOR_EACH (lr_stateful_rec, lr_stateful_table) {
> + build_lr_stateful_flows(lr_stateful_rec, lsi.lr_datapaths,
> + lsi.lflows, lsi.ls_ports, lsi.lr_ports,
> + &lsi.match, &lsi.actions,
> + lsi.meter_groups, lsi.features);
> + }
> + stopwatch_stop(LFLOWS_LR_STATEFUL_STOPWATCH_NAME, time_msec());
> stopwatch_start(LFLOWS_IGMP_STOPWATCH_NAME, time_msec());
> HMAP_FOR_EACH (igmp_group, hmap_node, igmp_groups) {
> build_lswitch_ip_mcast_igmp_mld(igmp_group,
> @@ -16410,7 +16637,6 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn,
> input_data->ls_ports,
> input_data->lr_ports,
> input_data->ls_port_groups,
> - input_data->lr_nats,
> input_data->lr_stateful_table,
> lflows,
> &igmp_groups,
> @@ -16887,10 +17113,14 @@ lflow_handle_northd_port_changes(struct
> ovsdb_idl_txn *ovnsb_txn,
> /* Generate new lflows. */
> struct ds match = DS_EMPTY_INITIALIZER;
> struct ds actions = DS_EMPTY_INITIALIZER;
> - build_lswitch_and_lrouter_iterate_by_lsp(
> - op, lflow_input->ls_ports, lflow_input->lr_ports,
> - lflow_input->lr_nats, lflow_input->lr_stateful_table,
> - lflow_input->meter_groups, &match, &actions, lflows);
> + build_lswitch_and_lrouter_iterate_by_lsp(op, lflow_input->ls_ports,
> + lflow_input->lr_ports,
> + lflow_input->meter_groups,
> + &match, &actions,
> + lflows);
> + build_lbnat_lflows_iterate_by_lsp(op, lflow_input->lr_stateful_table,
> + lflow_input->lr_ports, &match,
> + &actions, lflows);
> ds_destroy(&match);
> ds_destroy(&actions);
>
> @@ -16922,10 +17152,14 @@ lflow_handle_northd_port_changes(struct
> ovsdb_idl_txn *ovnsb_txn,
>
> struct ds match = DS_EMPTY_INITIALIZER;
> struct ds actions = DS_EMPTY_INITIALIZER;
> - build_lswitch_and_lrouter_iterate_by_lsp(
> - op, lflow_input->ls_ports, lflow_input->lr_ports,
> - lflow_input->lr_nats, lflow_input->lr_stateful_table,
> - lflow_input->meter_groups, &match, &actions, lflows);
> + build_lswitch_and_lrouter_iterate_by_lsp(op, lflow_input->ls_ports,
> + lflow_input->lr_ports,
> + lflow_input->meter_groups,
> + &match, &actions, lflows);
> +
> + build_lbnat_lflows_iterate_by_lsp(op, lflow_input->lr_stateful_table,
> + lflow_input->lr_ports, &match,
> + &actions, lflows);
> ds_destroy(&match);
> ds_destroy(&actions);
>
> diff --git a/northd/northd.h b/northd/northd.h
> index a6733b0f8f..9291e97abd 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -179,7 +179,6 @@ struct lflow_input {
> const struct hmap *ls_ports;
> const struct hmap *lr_ports;
> const struct ls_port_group_table *ls_port_groups;
> - const struct lr_nat_table *lr_nats;
> const struct lr_stateful_table *lr_stateful_table;
> const struct shash *meter_groups;
> const struct hmap *lb_datapaths_map;
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index 5c68350442..0ad160b9ff 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -873,6 +873,7 @@ main(int argc, char *argv[])
> stopwatch_create(LFLOWS_DATAPATHS_STOPWATCH_NAME, SW_MS);
> stopwatch_create(LFLOWS_PORTS_STOPWATCH_NAME, SW_MS);
> stopwatch_create(LFLOWS_LBS_STOPWATCH_NAME, SW_MS);
> + stopwatch_create(LFLOWS_LR_STATEFUL_STOPWATCH_NAME, SW_MS);
> stopwatch_create(LFLOWS_IGMP_STOPWATCH_NAME, SW_MS);
> stopwatch_create(LFLOWS_DP_GROUPS_STOPWATCH_NAME, SW_MS);
> stopwatch_create(LFLOWS_TO_SB_STOPWATCH_NAME, SW_MS);
> --
> 2.43.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev