On Wed, Apr 30, 2025 at 7:54 AM Ales Musil <amu...@redhat.com> wrote:

> The vector has the same functionality as the x2nrealloc
> for insert. Use vector instead which slightly simplifies
> the final code.
>
> Signed-off-by: Ales Musil <amu...@redhat.com>
> Acked-by: Mark Michelson <mmich...@redhat.com>
> ---
> v4: Rebase on top of current main.
>     Add ack from Mark.
> v3: Rebase on top of current main.
> v2: Rebase on top of current main.
> ---
>  northd/en-advertised-route-sync.c |  41 ++--
>  northd/en-lr-nat.c                |  17 +-
>  northd/en-multicast.c             |  47 ++---
>  northd/en-multicast.h             |   3 +-
>  northd/northd.c                   | 321 ++++++++++++++----------------
>  northd/northd.h                   |  19 +-
>  6 files changed, 207 insertions(+), 241 deletions(-)
>
> diff --git a/northd/en-advertised-route-sync.c
> b/northd/en-advertised-route-sync.c
> index 0f5c5e403..75fbbd942 100644
> --- a/northd/en-advertised-route-sync.c
> +++ b/northd/en-advertised-route-sync.c
> @@ -338,11 +338,8 @@ build_nat_connected_routes(
>          struct ovn_datapath *peer_od = op->peer->od;
>          ovs_assert(peer_od->nbs || peer_od->nbr);
>
> -        const struct ovn_port *peer_port = NULL;
>          /* This is a directly connected LR peer. */
>          if (peer_od->nbr) {
> -            peer_port = op->peer;
> -
>              const struct lr_stateful_record *peer_lr_stateful =
>                  lr_stateful_table_find_by_index(lr_stateful_table,
>                                                   peer_od->index);
> @@ -358,16 +355,16 @@ build_nat_connected_routes(
>
>          /* This peer is LSP, we need to check all connected router ports
>           * for NAT.*/
> -        for (size_t i = 0; i < peer_od->n_router_ports; i++) {
> -            peer_port = peer_od->router_ports[i]->peer;
> -            if (peer_port == op) {
> +        const struct ovn_port *rp;
> +        VECTOR_FOR_EACH (&peer_od->router_ports, rp) {
> +            if (rp->peer == op) {
>                  /* Skip advertising router. */
>                  continue;
>              }
>
>              const struct lr_stateful_record *peer_lr_stateful =
>                  lr_stateful_table_find_by_index(lr_stateful_table,
> -                                                peer_port->od->index);
> +                                                rp->peer->od->index);
>              if (!peer_lr_stateful) {
>                  continue;
>              }
> @@ -423,30 +420,28 @@ build_lb_connected_routes(const struct ovn_datapath
> *od,
>          ovs_assert(peer_od->nbs || peer_od->nbr);
>
>          const struct lr_stateful_record *lr_stateful_rec;
> -        const struct ovn_port *peer_port = NULL;
>          /* This is directly connected LR peer. */
>          if (peer_od->nbr) {
>              lr_stateful_rec = lr_stateful_table_find_by_index(
>                  lr_stateful_table, peer_od->index);
> -            peer_port = op->peer;
> -            build_lb_route_for_port(op, peer_port,
> lr_stateful_rec->lb_ips,
> +            build_lb_route_for_port(op, op->peer, lr_stateful_rec->lb_ips,
>                                      routes);
>              return;
>          }
>
>          /* This peer is LSP, we need to check all connected router ports
> for
>           * LBs.*/
> -        for (size_t i = 0; i < peer_od->n_router_ports; i++) {
> -            peer_port = peer_od->router_ports[i]->peer;
> -            if (peer_port == op) {
> +        struct ovn_port *rp;
> +        VECTOR_FOR_EACH (&peer_od->router_ports, rp) {
> +            if (rp->peer == op) {
>                  /* no need to check for LBs on ovn_port that initiated
> this
>                   * function.*/
>                  continue;
>              }
>              lr_stateful_rec = lr_stateful_table_find_by_index(
> -                lr_stateful_table, peer_port->od->index);
> +                lr_stateful_table, rp->peer->od->index);
>
> -            build_lb_route_for_port(op, peer_port,
> lr_stateful_rec->lb_ips,
> +            build_lb_route_for_port(op, rp->peer, lr_stateful_rec->lb_ips,
>                                      routes);
>          }
>      }
> @@ -470,14 +465,14 @@ build_lb_routes(const struct ovn_datapath *od,
>           *
>           * Advertise the LB IPs via all 'op' if this is a gateway router
> or
>           * throuh all DGPs of this distributed router otherwise. */
> -        struct ovn_port *op_ = NULL;
> -        size_t n_tracked_ports = !od->is_gw_router ? od->n_l3dgw_ports :
> 1;
> -        struct ovn_port **tracked_ports = !od->is_gw_router
> -                                          ? od->l3dgw_ports
> -                                          : &op_;
> -
> -        for (size_t i = 0; i < n_tracked_ports; i++) {
> -            build_lb_route_for_port(op, tracked_ports[i], lb_ips, routes);
> +
> +        if (od->is_gw_router) {
> +            build_lb_route_for_port(op, NULL, lb_ips, routes);
> +        } else {
> +            struct ovn_port *dgp;
> +            VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
> +                build_lb_route_for_port(op, dgp, lb_ips, routes);
> +            }
>          }
>      }
>  }
> diff --git a/northd/en-lr-nat.c b/northd/en-lr-nat.c
> index 44009ca6f..68c4d0e29 100644
> --- a/northd/en-lr-nat.c
> +++ b/northd/en-lr-nat.c
> @@ -295,13 +295,15 @@ lr_nat_entry_set_dgw_port(const struct ovn_datapath
> *od,
>      /* Validate gateway_port of NAT rule. */
>      nat_entry->l3dgw_port = NULL;
>      if (nat->gateway_port == NULL) {
> -        if (od->n_l3dgw_ports == 1) {
> -            nat_entry->l3dgw_port = od->l3dgw_ports[0];
> -        } else if (od->n_l3dgw_ports > 1) {
> +        if (vector_len(&od->l3dgw_ports) == 1) {
> +            nat_entry->l3dgw_port = vector_get(&od->l3dgw_ports, 0,
> +                                               struct ovn_port *);
> +        } else if (vector_len(&od->l3dgw_ports) > 1) {
>              /* Find the DGP reachable for the NAT external IP. */
> -            for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> -               if (lrp_find_member_ip(od->l3dgw_ports[i],
> nat->external_ip)) {
> -                   nat_entry->l3dgw_port = od->l3dgw_ports[i];
> +            struct ovn_port *dgp;
> +            VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
> +               if (lrp_find_member_ip(dgp, nat->external_ip)) {
> +                   nat_entry->l3dgw_port = dgp;
>                     break;
>                 }
>              }
> @@ -342,7 +344,8 @@ lr_nat_entry_set_dgw_port(const struct ovn_datapath
> *od,
>          return true;
>      }
>
> -    if (od->n_l3dgw_ports && nat_entry->type == DNAT_AND_SNAT &&
> +    if (!vector_is_empty(&od->l3dgw_ports) &&
> +        nat_entry->type == DNAT_AND_SNAT &&
>          nat->logical_port && nat->external_mac) {
>              nat_entry->is_distributed = true;
>      }
> diff --git a/northd/en-multicast.c b/northd/en-multicast.c
> index 7f3a6077c..3d918f15f 100644
> --- a/northd/en-multicast.c
> +++ b/northd/en-multicast.c
> @@ -332,8 +332,8 @@ build_mcast_groups(struct multicast_igmp_data *data,
>           * The router IGMP groups are based on the groups learnt by their
>           * multicast enabled peers.
>           */
> -        for (size_t i = 0; i < od->n_router_ports; i++) {
> -            struct ovn_port *router_port = od->router_ports[i]->peer;
> +        VECTOR_FOR_EACH (&od->router_ports, op) {
> +            struct ovn_port *router_port = op->peer;
>
>              /* If the router the port connects to doesn't have multicast
>               * relay enabled or if it was already configured to flood
> @@ -483,24 +483,10 @@ ovn_multicast_add_ports(struct hmap *mcgroups,
> struct ovn_datapath *od,
>          hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od,
> group));
>          mc->datapath = od;
>          mc->group = group;
> -        mc->n_ports = 0;
> -        mc->allocated_ports = 4;
> -        mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports);
> +        mc->ports = VECTOR_CAPACITY_INITIALIZER(struct ovn_port *, 4);
>      }
>
> -    size_t n_ports_total = mc->n_ports + n_ports;
> -
> -    if (n_ports_total > 2 * mc->allocated_ports) {
> -        mc->allocated_ports = n_ports_total;
> -        mc->ports = xrealloc(mc->ports,
> -                             mc->allocated_ports * sizeof *mc->ports);
> -    } else if (n_ports_total > mc->allocated_ports) {
> -        mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports,
> -                               sizeof *mc->ports);
> -    }
> -
> -    memcpy(&mc->ports[mc->n_ports], &ports[0], n_ports * sizeof *ports);
> -    mc->n_ports += n_ports;
> +    vector_push_array(&mc->ports, ports, n_ports);
>  }
>
>  static void
> @@ -521,7 +507,7 @@ ovn_multicast_destroy(struct hmap *mcgroups, struct
> ovn_multicast *mc)
>  {
>      if (mc) {
>          hmap_remove(mcgroups, &mc->hmap_node);
> -        free(mc->ports);
> +        vector_destroy(&mc->ports);
>          free(mc);
>      }
>  }
> @@ -530,12 +516,17 @@ static void
>  ovn_multicast_update_sbrec(const struct ovn_multicast *mc,
>                             const struct sbrec_multicast_group *sb)
>  {
> -    struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof
> *ports);
> -    for (size_t i = 0; i < mc->n_ports; i++) {
> -        ports[i] = CONST_CAST(struct sbrec_port_binding *,
> mc->ports[i]->sb);
> +    struct vector sb_ports =
> +        VECTOR_CAPACITY_INITIALIZER(struct sbrec_port_binding *,
> +                                    vector_len(&mc->ports));
> +
> +    struct ovn_port *op;
> +    VECTOR_FOR_EACH (&mc->ports, op) {
> +        vector_push(&sb_ports, &op->sb);
>      }
> -    sbrec_multicast_group_set_ports(sb, ports, mc->n_ports);
> -    free(ports);
> +    sbrec_multicast_group_set_ports(sb, vector_get_array(&sb_ports),
> +                                    vector_len(&sb_ports));
> +    vector_destroy(&sb_ports);
>  }
>
>  static void
> @@ -716,11 +707,11 @@ ovn_igmp_group_aggregate_ports(struct ovn_igmp_group
> *igmp_group,
>          free(entry);
>      }
>
> -    if (igmp_group->datapath->n_localnet_ports) {
> +    if (!vector_is_empty(&igmp_group->datapath->localnet_ports)) {
>          ovn_multicast_add_ports(mcast_groups, igmp_group->datapath,
> -                                &igmp_group->mcgroup,
> -                                igmp_group->datapath->localnet_ports,
> -                                igmp_group->datapath->n_localnet_ports);
> +            &igmp_group->mcgroup,
> +            vector_get_array(&igmp_group->datapath->localnet_ports),
> +            vector_len(&igmp_group->datapath->localnet_ports));
>      }
>  }
>
> diff --git a/northd/en-multicast.h b/northd/en-multicast.h
> index 131c01d65..c1ff7820d 100644
> --- a/northd/en-multicast.h
> +++ b/northd/en-multicast.h
> @@ -43,8 +43,7 @@ struct ovn_multicast {
>      struct ovn_datapath *datapath;
>      const struct multicast_group *group;
>
> -    struct ovn_port **ports;
> -    size_t n_ports, allocated_ports;
> +    struct vector ports; /* Vector of struct ovn_port *. */
>  };
>
>  /*
> diff --git a/northd/northd.c b/northd/northd.c
> index bed05e614..464b3798a 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -499,6 +499,10 @@ ovn_datapath_create(struct hmap *datapaths, const
> struct uuid *key,
>      od->lr_group = NULL;
>      hmap_init(&od->ports);
>      sset_init(&od->router_ips);
> +    od->ls_peers = VECTOR_EMPTY_INITIALIZER(struct ovn_datapath *);
> +    od->router_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
> +    od->l3dgw_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
> +    od->localnet_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
>      return od;
>  }
>
> @@ -521,10 +525,10 @@ ovn_datapath_destroy(struct hmap *datapaths, struct
> ovn_datapath *od)
>          hmap_remove(datapaths, &od->key_node);
>          ovn_destroy_tnlids(&od->port_tnlids);
>          destroy_ipam_info(&od->ipam_info);
> -        free(od->router_ports);
> -        free(od->ls_peers);
> -        free(od->localnet_ports);
> -        free(od->l3dgw_ports);
> +        vector_destroy(&od->router_ports);
> +        vector_destroy(&od->ls_peers);
> +        vector_destroy(&od->localnet_ports);
> +        vector_destroy(&od->l3dgw_ports);
>          destroy_mcast_info_for_datapath(od);
>          destroy_ports_for_datapath(od);
>          sset_destroy(&od->router_ips);
> @@ -593,27 +597,6 @@ ovn_datapath_from_sbrec(const struct hmap
> *ls_datapaths,
>      return NULL;
>  }
>
> -static void
> -ovn_datapath_add_router_port(struct ovn_datapath *od, struct ovn_port *op)
> -{
> -    if (od->n_router_ports == od->n_allocated_router_ports) {
> -        od->router_ports = x2nrealloc(od->router_ports,
> -                                      &od->n_allocated_router_ports,
> -                                      sizeof *od->router_ports);
> -    }
> -    od->router_ports[od->n_router_ports++] = op;
> -}
> -
> -static void
> -ovn_datapath_add_ls_peer(struct ovn_datapath *od, struct ovn_datapath
> *peer)
> -{
> -    if (od->n_ls_peers == od->n_allocated_ls_peers) {
> -        od->ls_peers = x2nrealloc(od->ls_peers, &od->n_allocated_ls_peers,
> -                                  sizeof *od->ls_peers);
> -    }
> -    od->ls_peers[od->n_ls_peers++] = peer;
> -}
> -
>  static bool
>  lrouter_is_enabled(const struct nbrec_logical_router *lrouter)
>  {
> @@ -1601,7 +1584,7 @@ ipam_add_port_addresses(struct ovn_datapath *od,
> struct ovn_port *op)
>  static bool
>  is_nat_gateway_port(const struct nbrec_nat *nat, const struct ovn_port
> *op)
>  {
> -    if (op->od->n_l3dgw_ports > 1
> +    if (vector_len(&op->od->l3dgw_ports) > 1
>          && ((!nat->gateway_port && !lrp_find_member_ip(op,
> nat->external_ip))
>              || (nat->gateway_port && nat->gateway_port != op->nbrp))) {
>          return false;
> @@ -2302,12 +2285,7 @@ join_logical_ports_lsp(struct hmap *ports,
>      }
>
>      if (lsp_is_localnet(nbsp)) {
> -       if (od->n_localnet_ports >= od->n_allocated_localnet_ports) {
> -           od->localnet_ports = x2nrealloc(
> -               od->localnet_ports, &od->n_allocated_localnet_ports,
> -               sizeof *od->localnet_ports);
> -       }
> -       od->localnet_ports[od->n_localnet_ports++] = op;
> +        vector_push(&od->localnet_ports, &op);
>      }
>
>      if (lsp_is_vtep(nbsp)) {
> @@ -2456,8 +2434,8 @@ static bool
>  peer_needs_cr_port_creation(struct ovn_port *op)
>  {
>      if ((op->nbrp->n_gateway_chassis || op->nbrp->ha_chassis_group)
> -        && op->od->n_l3dgw_ports == 1 && op->peer && op->peer->nbsp
> -        && !op->peer->od->n_localnet_ports) {
> +        && vector_len(&op->od->l3dgw_ports) == 1 && op->peer &&
> op->peer->nbsp
> +        && vector_is_empty(&op->peer->od->localnet_ports)) {
>          return true;
>      }
>
> @@ -2552,8 +2530,8 @@ join_logical_ports(const struct
> sbrec_port_binding_table *sbrec_pb_table,
>                  continue;
>              }
>
> -            ovn_datapath_add_router_port(op->od, op);
> -            ovn_datapath_add_ls_peer(peer->od, op->od);
> +            vector_push(&op->od->router_ports, &op);
> +            vector_push(&peer->od->ls_peers, &op->od);
>              peer->peer = op;
>              op->peer = peer;
>
> @@ -2659,12 +2637,7 @@ join_logical_ports(const struct
> sbrec_port_binding_table *sbrec_pb_table,
>          ovs_assert(crp);
>
>          /* Add to l3dgw_ports in od, for later use during flow creation.
> */
> -        if (od->n_l3dgw_ports == od->n_allocated_l3dgw_ports) {
> -            od->l3dgw_ports = x2nrealloc(od->l3dgw_ports,
> -                                        &od->n_allocated_l3dgw_ports,
> -                                        sizeof *od->l3dgw_ports);
> -        }
> -        od->l3dgw_ports[od->n_l3dgw_ports++] = op;
> +        vector_push(&od->l3dgw_ports, &op);
>
>          if (op->peer && op->peer->nbsp) {
>              /* Only used for the router type LSP whose peer is l3dgw_port
> */
> @@ -2770,8 +2743,9 @@ get_nat_addresses(const struct ovn_port *op, size_t
> *n, bool routable_only,
>
>          /* Determine whether this NAT rule satisfies the conditions for
>           * distributed NAT processing. */
> -        if (op->od->n_l3dgw_ports && !strcmp(nat->type, "dnat_and_snat")
> -            && nat->logical_port && nat->external_mac) {
> +        if (!vector_is_empty(&op->od->l3dgw_ports) &&
> +            !strcmp(nat->type, "dnat_and_snat") &&
> +            nat->logical_port && nat->external_mac) {
>              /* Distributed NAT rule. */
>              if (eth_addr_from_string(nat->external_mac, &mac)) {
>                  struct ds address = DS_EMPTY_INITIALIZER;
> @@ -3410,8 +3384,9 @@ ovn_port_update_sbrec(struct ovsdb_idl_txn
> *ovnsb_txn,
>              smap_clone(&options, &op->nbsp->options);
>
>              if (queue_id) {
> -                if (op->od->n_localnet_ports) {
> -                    struct ovn_port *port = op->od->localnet_ports[0];
> +                if (!vector_is_empty(&op->od->localnet_ports)) {
> +                    struct ovn_port *port =
> vector_get(&op->od->localnet_ports,
> +                                                       0, struct ovn_port
> *);
>                      const char *physical_network = smap_get(
>                              &port->nbsp->options, "network_name");
>                      if (physical_network) {
> @@ -4090,7 +4065,8 @@ build_lswitch_lbs_from_lrouter(struct ovn_datapaths
> *lr_datapaths,
>      HMAP_FOR_EACH (lb_dps, hmap_node, lb_dps_map) {
>          BITMAP_FOR_EACH_1 (index, ods_size(lr_datapaths),
> lb_dps->nb_lr_map) {
>              struct ovn_datapath *od = lr_datapaths->array[index];
> -            ovn_lb_datapaths_add_ls(lb_dps, od->n_ls_peers, od->ls_peers);
> +            ovn_lb_datapaths_add_ls(lb_dps, vector_len(&od->ls_peers),
> +                                    vector_get_array(&od->ls_peers));
>          }
>      }
>
> @@ -4098,14 +4074,16 @@ build_lswitch_lbs_from_lrouter(struct
> ovn_datapaths *lr_datapaths,
>      HMAP_FOR_EACH (lb_group_dps, hmap_node, lb_group_dps_map) {
>          for (size_t i = 0; i < lb_group_dps->n_lr; i++) {
>              struct ovn_datapath *od = lb_group_dps->lr[i];
> -            ovn_lb_group_datapaths_add_ls(lb_group_dps, od->n_ls_peers,
> -                                          od->ls_peers);
> +            ovn_lb_group_datapaths_add_ls(lb_group_dps,
> +                                          vector_len(&od->ls_peers),
> +
> vector_get_array(&od->ls_peers));
>              for (size_t j = 0; j < lb_group_dps->lb_group->n_lbs; j++) {
>                  const struct uuid *lb_uuid =
>                      &lb_group_dps->lb_group->lbs[j]->nlb->header_.uuid;
>                  lb_dps = ovn_lb_datapaths_find(lb_dps_map, lb_uuid);
>                  ovs_assert(lb_dps);
> -                ovn_lb_datapaths_add_ls(lb_dps, od->n_ls_peers,
> od->ls_peers);
> +                ovn_lb_datapaths_add_ls(lb_dps, vector_len(&od->ls_peers),
> +                                        vector_get_array(&od->ls_peers));
>              }
>          }
>      }
> @@ -4170,12 +4148,13 @@ should_add_router_port_garp(const struct ovn_port
> *op, const char *chassis)
>      }
>
>      bool add_router_port_garp = false;
> -    if (op->peer && op->peer->nbrp && op->peer->od->n_l3dgw_ports) {
> +    if (op->peer && op->peer->nbrp &&
> +        !vector_is_empty(&op->peer->od->l3dgw_ports)) {
>          if (lrp_is_l3dgw(op->peer)) {
>              add_router_port_garp = true;
>          } else if (smap_get_bool(&op->peer->nbrp->options,
>                                   "reside-on-redirect-chassis", false)) {
> -            if (op->peer->od->n_l3dgw_ports == 1) {
> +            if (vector_len(&op->peer->od->l3dgw_ports) == 1) {
>                  add_router_port_garp = true;
>              } else {
>                  static struct vlog_rate_limit rl =
> @@ -4187,10 +4166,10 @@ should_add_router_port_garp(const struct ovn_port
> *op, const char *chassis)
>                                  "option can only be used when there is "
>                                  "a single distributed gateway port.",
>                                  op->peer->key, op->peer->od->nbr->name,
> -                                op->peer->od->n_l3dgw_ports);
> +                                vector_len(&op->peer->od->l3dgw_ports));
>              }
>          }
> -    } else if (chassis && op->od->n_localnet_ports) {
> +    } else if (chassis && !vector_is_empty(&op->od->localnet_ports)) {
>          add_router_port_garp = true;
>      }
>
> @@ -4217,10 +4196,10 @@ sync_pb_for_lsp(struct ovn_port *op,
>          size_t n_nats = 0;
>          char **nats = NULL;
>          bool l3dgw_ports = op->peer && op->peer->od &&
> -                            op->peer->od->n_l3dgw_ports;
> +                           !vector_is_empty(&op->peer->od->l3dgw_ports);
>          if (nat_addresses && !strcmp(nat_addresses, "router")) {
>              if (op->peer && op->peer->od
> -                && (chassis || op->peer->od->n_l3dgw_ports)) {
> +                && (chassis ||
> !vector_is_empty(&op->peer->od->l3dgw_ports))) {
>                  bool include_lb_vips = !smap_get_bool(&op->nbsp->options,
>                          "exclude-lb-vips-from-garp", false);
>
> @@ -4249,7 +4228,8 @@ sync_pb_for_lsp(struct ovn_port *op,
>                      const struct ovn_port *l3dgw_port = (
>                          lrp_is_l3dgw(op->peer)
>                          ? op->peer
> -                        : op->peer->od->l3dgw_ports[0]);
> +                        : vector_get(&op->peer->od->l3dgw_ports, 0,
> +                                     struct ovn_port *));
>                      ds_put_format(&nat_addr, " is_chassis_resident(%s)",
>                          l3dgw_port->cr_port->json_key);
>                  }
> @@ -4286,11 +4266,12 @@ sync_pb_for_lsp(struct ovn_port *op,
>
>  op->peer->lrp_networks.ipv4_addrs[i].addr_s);
>              }
>
> -            if (op->peer->od->n_l3dgw_ports) {
> +            if (!vector_is_empty(&op->peer->od->l3dgw_ports)) {
>                  const struct ovn_port *l3dgw_port = (
>                      lrp_is_l3dgw(op->peer)
>                      ? op->peer
> -                    : op->peer->od->l3dgw_ports[0]);
> +                    : vector_get(&op->peer->od->l3dgw_ports, 0,
> +                                 struct ovn_port *));
>                  ds_put_format(&garp_info, " is_chassis_resident(%s)",
>                                  l3dgw_port->cr_port->json_key);
>              }
> @@ -4999,8 +4980,8 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
>          return true;
>      }
>
> -    bool ls_had_only_router_ports = (od->n_router_ports
> -            && (od->n_router_ports == hmap_count(&od->ports)));
> +    bool ls_had_only_router_ports = (!vector_is_empty(&od->router_ports)
> +            && (vector_len(&od->router_ports) == hmap_count(&od->ports)));
>
>      struct ovn_port *op;
>      HMAP_FOR_EACH (op, dp_node, &od->ports) {
> @@ -5102,8 +5083,8 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
>          }
>      }
>
> -    bool ls_has_only_router_ports = (od->n_router_ports
> -            && (od->n_router_ports == hmap_count(&od->ports)));
> +    bool ls_has_only_router_ports = (!vector_is_empty(&od->router_ports)
> +            && (vector_len(&od->router_ports)  ==
> hmap_count(&od->ports)));
>
>      /* There are lflows related to router ports that depend on whether
>       * there are switch ports on the logical switch (see
> @@ -5112,8 +5093,7 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
>       * to the tracked 'updated' ovn ports so that lflow engine can
>       * regenerate lflows for these router ports. */
>      if (ls_had_only_router_ports != ls_has_only_router_ports) {
> -        for (size_t i = 0; i < od->n_router_ports; i++) {
> -            op = od->router_ports[i];
> +        VECTOR_FOR_EACH (&od->router_ports, op) {
>              add_op_to_northd_tracked_ports(&trk_lsps->updated, op);
>          }
>      }
> @@ -6058,7 +6038,8 @@ build_lswitch_port_sec_op(struct ovn_port *op,
> struct lflow_table *lflows,
>                                            op->key, &op->nbsp->header_,
>                                            op->lflow_ref);
>
> -        if (!lsp_is_localnet(op->nbsp) && !op->od->n_localnet_ports) {
> +        if (!lsp_is_localnet(op->nbsp) &&
> +            vector_is_empty(&op->od->localnet_ports)) {
>              return;
>          }
>
> @@ -6073,16 +6054,15 @@ build_lswitch_port_sec_op(struct ovn_port *op,
> struct lflow_table *lflows,
>                                                ds_cstr(match),
> ds_cstr(actions),
>                                                op->key, &op->nbsp->header_,
>                                                op->lflow_ref);
> -        } else if (op->od->n_localnet_ports) {
> +        } else if (!vector_is_empty(&op->od->localnet_ports)) {
> +            const struct ovn_port *lp =
> vector_get(&op->od->localnet_ports, 0,
> +                                                   struct ovn_port *);
>              ds_put_format(match, "outport == %s && inport == %s",
> -                          op->od->localnet_ports[0]->json_key,
> -                          op->json_key);
> +                          lp->json_key, op->json_key);
>              ovn_lflow_add_with_lport_and_hint(lflows, op->od,
>                      S_SWITCH_OUT_APPLY_PORT_SEC, 110,
>                      ds_cstr(match), ds_cstr(actions),
> -                    op->od->localnet_ports[0]->key,
> -                    &op->od->localnet_ports[0]->nbsp->header_,
> -                    op->lflow_ref);
> +                    lp->key, &lp->nbsp->header_, op->lflow_ref);
>          }
>      } else if (lsp_is_router(op->nbsp)) {
>          ds_put_format(actions, REGBIT_FROM_ROUTER_PORT" = 1; next;");
> @@ -6304,8 +6284,8 @@ build_ls_stateful_rec_pre_acls(
>       * send IP packets for some (allow) filters through the conntrack
> action,
>       * which handles defragmentation, in order to match L4 headers. */
>      if (ls_stateful_rec->has_stateful_acl) {
> -        for (size_t i = 0; i < od->n_router_ports; i++) {
> -            struct ovn_port *op = od->router_ports[i];
> +        struct ovn_port *op;
> +        VECTOR_FOR_EACH (&od->router_ports, op) {
>              if (op->enable_router_port_acl) {
>                  continue;
>              }
> @@ -6313,11 +6293,11 @@ build_ls_stateful_rec_pre_acls(
>                                       S_SWITCH_IN_PRE_ACL,
> S_SWITCH_OUT_PRE_ACL,
>                                       110, lflows, lflow_ref);
>          }
> -        for (size_t i = 0; i < od->n_localnet_ports; i++) {
> -            skip_port_from_conntrack(od, od->localnet_ports[i], true,
> -                                     S_SWITCH_IN_PRE_ACL,
> -                                     S_SWITCH_OUT_PRE_ACL,
> -                                     110, lflows, lflow_ref);
> +        struct ovn_port *lp;
> +        VECTOR_FOR_EACH (&od->localnet_ports, lp) {
> +            skip_port_from_conntrack(od, lp, true, S_SWITCH_IN_PRE_ACL,
> +                                     S_SWITCH_OUT_PRE_ACL, 110, lflows,
> +                                     lflow_ref);
>          }
>
>          /* stateless filters always take precedence over stateful ACLs. */
> @@ -6515,9 +6495,9 @@ build_ls_stateful_rec_pre_lb(const struct
> ls_stateful_record *ls_stateful_rec,
>                               struct lflow_table *lflows,
>                               struct lflow_ref *lflow_ref)
>  {
> -    for (size_t i = 0; i < od->n_router_ports; i++) {
> -        skip_port_from_conntrack(od, od->router_ports[i],
> -                                 ls_stateful_rec->has_stateful_acl,
> +    struct ovn_port *op;
> +    VECTOR_FOR_EACH (&od->router_ports, op) {
> +        skip_port_from_conntrack(od, op,
> ls_stateful_rec->has_stateful_acl,
>                                   S_SWITCH_IN_PRE_LB, S_SWITCH_OUT_PRE_LB,
>                                   110, lflows, lflow_ref);
>      }
> @@ -6528,9 +6508,9 @@ build_ls_stateful_rec_pre_lb(const struct
> ls_stateful_record *ls_stateful_rec,
>       * balanced.
>       */
>      if (!ls_stateful_rec->has_lb_vip) {
> -        for (size_t i = 0; i < od->n_localnet_ports; i++) {
> -            skip_port_from_conntrack(od, od->localnet_ports[i],
> -                                     ls_stateful_rec->has_stateful_acl,
> +        struct ovn_port *lp;
> +        VECTOR_FOR_EACH (&od->localnet_ports, lp) {
> +            skip_port_from_conntrack(od, lp,
> ls_stateful_rec->has_stateful_acl,
>                                       S_SWITCH_IN_PRE_LB,
> S_SWITCH_OUT_PRE_LB,
>                                       110, lflows, lflow_ref);
>          }
> @@ -8721,8 +8701,9 @@ build_vtep_hairpin(struct ovn_datapath *od, struct
> lflow_table *lflows,
>      * current chassis, should be passed to next table for ARP/ND hairpin
>      * processing. */
>      struct ds match = DS_EMPTY_INITIALIZER;
> -    for (int i = 0; i < od->n_router_ports; i++) {
> -        struct ovn_port *op = od->router_ports[i]->peer;
> +    struct ovn_port *rp;
> +    VECTOR_FOR_EACH (&od->router_ports, rp) {
> +        struct ovn_port *op = rp->peer;
>          if (lrp_is_l3dgw(op)) {
>              ds_clear(&match);
>              ds_put_format(&match,
> @@ -8825,8 +8806,9 @@ build_lrouter_groups__(struct hmap *lr_ports, struct
> ovn_datapath *od)
>      /* For logical router with distributed gateway ports. If it
>       * has HA_Chassis_Group associated to it in SB DB, then store the
>       * ha chassis group name. */
> -    for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> -        struct ovn_port *crp = od->l3dgw_ports[i]->cr_port;
> +    struct ovn_port *dgp;
> +    VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
> +        struct ovn_port *crp = dgp->cr_port;
>          if (crp->sb->ha_chassis_group &&
>              crp->sb->ha_chassis_group->n_ha_chassis > 1) {
>              sset_add(&od->lr_group->ha_chassis_groups,
> @@ -8852,15 +8834,16 @@ build_lrouter_groups__(struct hmap *lr_ports,
> struct ovn_datapath *od)
>                  build_lrouter_groups__(lr_ports, peer_dp);
>              }
>          } else {
> -            for (size_t j = 0; j < peer_dp->n_router_ports; j++) {
> -                if (!peer_dp->router_ports[j]->peer) {
> +            struct ovn_port *op;
> +            VECTOR_FOR_EACH (&peer_dp->router_ports, op) {
> +                if (!op->peer) {
>                      /* If there is no peer port connecting to the
>                      * router port, ignore it. */
>                      continue;
>                  }
>
>                  struct ovn_datapath *router_dp;
> -                router_dp = peer_dp->router_ports[j]->peer->od;
> +                router_dp = op->peer->od;
>                  if (router_dp == od) {
>                      continue;
>                  }
> @@ -9056,7 +9039,7 @@ build_lswitch_rport_arp_req_flow(
>      /* Send a the packet to the router pipeline.  If the switch has
> non-router
>       * ports then flood it there as well.
>       */
> -    if (od->n_router_ports != od->nbs->n_ports) {
> +    if (vector_len(&od->router_ports) != od->nbs->n_ports) {
>          ds_put_format(&actions, "clone {outport = %s; output; }; "
>                                  "outport = \""MC_FLOOD_L2"\"; output;",
>                        patch_op->json_key);
> @@ -9078,7 +9061,7 @@ build_lswitch_rport_arp_req_flow(
>          ds_put_format(&match, "%s && !is_chassis_resident(%s)",
> ds_cstr(&m),
>                        patch_op->cr_port->json_key);
>          ds_clear(&actions);
> -        if (od->n_router_ports != od->nbs->n_ports) {
> +        if (vector_len(&od->router_ports) != od->nbs->n_ports) {
>              ds_put_format(&actions, "clone {outport = %s; output; }; "
>                                      "outport = \""MC_FLOOD_L2"\";
> output;",
>                            patch_op->cr_port->json_key);
> @@ -9206,7 +9189,7 @@ build_lswitch_rport_arp_req_flows_for_lbnats(
>       *
>       * Priority: 75.
>       */
> -    if (sw_od->n_router_ports != sw_od->nbs->n_ports) {
> +    if (vector_len(&sw_od->router_ports) != sw_od->nbs->n_ports) {
>          build_lswitch_rport_arp_req_self_orig_flow(op, 75, sw_od,
>
> lr_stateful_rec->lrnat_rec,
>                                                     lflows, lflow_ref);
> @@ -9496,7 +9479,7 @@ build_lswitch_dhcp_relay_flows(struct ovn_port *op,
>          return;
>      }
>
> -    if (!op->od || !op->od->n_router_ports || !op->od->nbs) {
> +    if (!op->od || vector_is_empty(&op->od->router_ports) ||
> !op->od->nbs) {
>          return;
>      }
>
> @@ -9566,8 +9549,8 @@
> build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
>      struct ds match = DS_EMPTY_INITIALIZER;
>
>      for (size_t i = 0; i < op->n_lsp_addrs; i++) {
> -        for (size_t j = 0; j < op->od->n_router_ports; j++) {
> -            struct ovn_port *rp = op->od->router_ports[j];
> +        struct ovn_port *rp;
> +        VECTOR_FOR_EACH (&op->od->router_ports, rp) {
>              for (size_t k = 0; k < rp->n_lsp_addrs; k++) {
>                  for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv4_addrs;
> l++) {
>                      ds_clear(&match);
> @@ -10187,7 +10170,7 @@ build_lswitch_dhcp_options_and_response(struct
> ovn_port *op,
>      }
>
>      bool is_external = lsp_is_external(op->nbsp);
> -    if (is_external && (!op->od->n_localnet_ports ||
> +    if (is_external && (vector_is_empty(&op->od->localnet_ports) ||
>                          !op->nbsp->ha_chassis_group)) {
>          /* If it's an external port and there are no localnet ports
>           * and if it doesn't belong to an HA chassis group ignore it. */
> @@ -10196,14 +10179,13 @@ build_lswitch_dhcp_options_and_response(struct
> ovn_port *op,
>
>      for (size_t i = 0; i < op->n_lsp_addrs; i++) {
>          if (is_external) {
> -            for (size_t j = 0; j < op->od->n_localnet_ports; j++) {
> +            struct ovn_port *lp;
> +            VECTOR_FOR_EACH (&op->od->localnet_ports, lp) {
>                  build_dhcpv4_options_flows(
> -                    op, &op->lsp_addrs[i],
> -                    op->od->localnet_ports[j], is_external,
> +                    op, &op->lsp_addrs[i], lp, is_external,
>                      meter_groups, lflows, op->lflow_ref);
>                  build_dhcpv6_options_flows(
> -                    op, &op->lsp_addrs[i],
> -                    op->od->localnet_ports[j], is_external,
> +                    op, &op->lsp_addrs[i], lp, is_external,
>                      meter_groups, lflows, op->lflow_ref);
>              }
>          } else {
> @@ -10284,10 +10266,10 @@ build_lswitch_external_port(struct ovn_port *op,
>      if (!lsp_is_external(op->nbsp)) {
>          return;
>      }
> -    for (size_t i = 0; i < op->od->n_localnet_ports; i++) {
> -        build_drop_arp_nd_flows_for_unbound_router_ports(
> -            op, op->od->localnet_ports[i], lflows,
> -            op->lflow_ref);
> +    struct ovn_port *lp;
> +    VECTOR_FOR_EACH (&op->od->localnet_ports, lp) {
> +        build_drop_arp_nd_flows_for_unbound_router_ports(op, lp, lflows,
> +                                                         op->lflow_ref);
>      }
>  }
>
> @@ -10524,7 +10506,8 @@ build_lswitch_ip_unicast_lookup(struct ovn_port
> *op,
>              ds_put_format(match, "eth.dst == %s",
> op->peer->lrp_networks.ea_s);
>          }
>
> -        if (op->peer->od->n_l3dgw_ports && op->od->n_localnet_ports) {
> +        if (!vector_is_empty(&op->peer->od->l3dgw_ports) &&
> +            !vector_is_empty(&op->od->localnet_ports)) {
>              bool add_chassis_resident_check = false;
>              const char *json_key;
>              if (lrp_is_l3dgw(op->peer)) {
> @@ -10545,8 +10528,9 @@ build_lswitch_ip_unicast_lookup(struct ovn_port
> *op,
>                  add_chassis_resident_check = smap_get_bool(
>                      &op->peer->nbrp->options,
>                      "reside-on-redirect-chassis", false) &&
> -                    op->peer->od->n_l3dgw_ports == 1;
> -                json_key =
> op->peer->od->l3dgw_ports[0]->cr_port->json_key;
> +                    vector_len(&op->peer->od->l3dgw_ports) == 1;
> +                json_key = vector_get(&op->peer->od->l3dgw_ports, 0,
> +                                      struct ovn_port
> *)->cr_port->json_key;
>              }
>
>              if (add_chassis_resident_check) {
> @@ -12546,16 +12530,16 @@ build_lrouter_nat_flows_for_lb(
>              type = LROUTER_NAT_LB_FLOW_NORMAL;
>          }
>
> -        if (!od->n_l3dgw_ports) {
> +        if (vector_is_empty(&od->l3dgw_ports)) {
>              bitmap_set1(gw_dp_bitmap[type], index);
>          } else {
>              /* Create stateless LB NAT rules when using multiple DGPs and
>               * use_stateless_nat is true.
>               */
> -            bool stateless_nat = (od->n_l3dgw_ports > 1)
> +            bool stateless_nat = (vector_len(&od->l3dgw_ports) > 1)
>                  ? use_stateless_nat : false;
> -            for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> -                struct ovn_port *dgp = od->l3dgw_ports[i];
> +            struct ovn_port *dgp;
> +            VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
>                  build_distr_lrouter_nat_flows_for_lb(&ctx, type, od,
>                                                       lb_dps->lflow_ref,
> dgp,
>                                                       stateless_nat);
> @@ -12873,7 +12857,7 @@ lrouter_nat_get_priority(const struct ovn_datapath
> *od,
>       * nat->logical_ip with the longest mask gets a higher
>       * priority. */
>      uint16_t priority = prefix_len + 1;
> -    if (!od->is_gw_router && od->n_l3dgw_ports) {
> +    if (!od->is_gw_router && !vector_is_empty(&od->l3dgw_ports)) {
>          priority += 128;
>      }
>
> @@ -13560,9 +13544,9 @@ build_gateway_get_l2_hdr_size(struct ovn_port *op)
>          /* Check if vlans are enabled on a localnet port running the
> logical
>           * switch connected to this logical router.
>           */
> -        for (size_t i = 0; i < peer->od->n_localnet_ports; i++) {
> -            struct ovn_port *localnet_port = peer->od->localnet_ports[i];
> -            const struct nbrec_logical_switch_port *nbsp =
> localnet_port->nbsp;
> +        struct ovn_port *lp;
> +        VECTOR_FOR_EACH (&peer->od->localnet_ports, lp) {
> +            const struct nbrec_logical_switch_port *nbsp = lp->nbsp;
>
>              if (!nbsp || !nbsp->tag_request) {
>                  continue;
> @@ -14813,7 +14797,8 @@ build_arp_resolve_flows_for_lsp(
>          return;
>      }
>
> -    if (op->od->n_router_ports && !lsp_is_router(op->nbsp)
> +    struct ovn_port *rp;
> +    if (!vector_is_empty(&op->od->router_ports) &&
> !lsp_is_router(op->nbsp)
>          && strcmp(op->nbsp->type, "virtual")) {
>          /* This is a logical switch port that backs a VM or a container.
>           * Extract its addresses. For each of the address, go through all
> @@ -14825,12 +14810,11 @@ build_arp_resolve_flows_for_lsp(
>              const char *ea_s = op->lsp_addrs[i].ea_s;
>              for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
>                  const char *ip_s = op->lsp_addrs[i].ipv4_addrs[j].addr_s;
> -                for (size_t k = 0; k < op->od->n_router_ports; k++) {
> +                VECTOR_FOR_EACH (&op->od->router_ports, rp) {
>                      /* Get the Logical_Router_Port that the
>                       * Logical_Switch_Port is connected to, as
>                       * 'peer'. */
> -                    struct ovn_port *peer = ovn_port_get_peer(
> -                            lr_ports, op->od->router_ports[k]);
> +                    struct ovn_port *peer = ovn_port_get_peer(lr_ports,
> rp);
>                      if (!peer || !peer->nbrp) {
>                          continue;
>                      }
> @@ -14857,12 +14841,11 @@ build_arp_resolve_flows_for_lsp(
>
>              for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
>                  const char *ip_s = op->lsp_addrs[i].ipv6_addrs[j].addr_s;
> -                for (size_t k = 0; k < op->od->n_router_ports; k++) {
> +                VECTOR_FOR_EACH (&op->od->router_ports, rp) {
>                      /* Get the Logical_Router_Port that the
>                       * Logical_Switch_Port is connected to, as
>                       * 'peer'. */
> -                    struct ovn_port *peer = ovn_port_get_peer(
> -                            lr_ports, op->od->router_ports[k]);
> +                    struct ovn_port *peer = ovn_port_get_peer(lr_ports,
> rp);
>                      if (!peer || !peer->nbrp) {
>                          continue;
>                      }
> @@ -14904,10 +14887,8 @@ build_arp_resolve_flows_for_lsp(
>                            "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]);
> +        VECTOR_FOR_EACH (&op->od->router_ports, rp) {
> +            struct ovn_port *router_port = ovn_port_get_peer(lr_ports,
> rp);
>              if (!router_port || !router_port->nbrp) {
>                  continue;
>              }
> @@ -15165,8 +15146,9 @@ build_gateway_redirect_flows_for_lrouter(
>          struct lflow_ref *lflow_ref)
>  {
>      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])) {
> +    const struct ovn_port *dgp;
> +    VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
> +        if (l3dgw_port_has_associated_vtep_lports(dgp)) {
>              /* 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
> @@ -15179,8 +15161,8 @@ build_gateway_redirect_flows_for_lrouter(
>
>          const struct ovsdb_idl_row *stage_hint = NULL;
>
> -        if (od->l3dgw_ports[i]->nbrp) {
> -            stage_hint = &od->l3dgw_ports[i]->nbrp->header_;
> +        if (dgp->nbrp) {
> +            stage_hint = &dgp->nbrp->header_;
>          }
>
>          /* For traffic with outport == l3dgw_port, if the
> @@ -15188,11 +15170,9 @@ build_gateway_redirect_flows_for_lrouter(
>           * rule, then the traffic is redirected to the central
>           * instance of the l3dgw_port. */
>          ds_clear(match);
> -        ds_put_format(match, "outport == %s",
> -                      od->l3dgw_ports[i]->json_key);
> +        ds_put_format(match, "outport == %s", dgp->json_key);
>          ds_clear(actions);
> -        ds_put_format(actions, "outport = %s; next;",
> -                      od->l3dgw_ports[i]->cr_port->json_key);
> +        ds_put_format(actions, "outport = %s; next;",
> dgp->cr_port->json_key);
>          ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
>                                  ds_cstr(match), ds_cstr(actions),
>                                  stage_hint, lflow_ref);
> @@ -15211,8 +15191,9 @@ build_lr_gateway_redirect_flows_for_nats(
>          struct lflow_ref *lflow_ref)
>  {
>      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])) {
> +    const struct ovn_port *dgp;
> +    VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
> +        if (l3dgw_port_has_associated_vtep_lports(dgp)) {
>              /* 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
> @@ -15235,8 +15216,8 @@ build_lr_gateway_redirect_flows_for_nats(
>
>              const struct ovsdb_idl_row *stage_hint = NULL;
>
> -            if (od->l3dgw_ports[i]->nbrp) {
> -                stage_hint = &od->l3dgw_ports[i]->nbrp->header_;
> +            if (dgp->nbrp) {
> +                stage_hint = &dgp->nbrp->header_;
>              }
>
>              struct ds match_ext = DS_EMPTY_INITIALIZER;
> @@ -15823,7 +15804,7 @@ build_ipv6_input_flows_for_lrouter_port(
>      }
>
>      /* UDP/TCP/SCTP port unreachable */
> -    if (!op->od->is_gw_router && !op->od->n_l3dgw_ports) {
> +    if (!op->od->is_gw_router && vector_is_empty(&op->od->l3dgw_ports)) {
>          for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
>              ds_clear(match);
>              ds_put_format(match,
> @@ -16087,8 +16068,8 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                        op->lrp_networks.ipv4_addrs[i].network_s,
>                        op->lrp_networks.ipv4_addrs[i].plen);
>
> -        if (op->od->n_l3dgw_ports && op->peer
> -            && op->peer->od->n_localnet_ports) {
> +        if (!vector_is_empty(&op->od->l3dgw_ports) && op->peer
> +            && !vector_is_empty(&op->peer->od->localnet_ports)) {
>              bool add_chassis_resident_check = false;
>              const char *json_key;
>              if (lrp_is_l3dgw(op)) {
> @@ -16111,8 +16092,9 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                  add_chassis_resident_check = smap_get_bool(
>                      &op->nbrp->options,
>                      "reside-on-redirect-chassis", false) &&
> -                    op->od->n_l3dgw_ports == 1;
> -                json_key = op->od->l3dgw_ports[0]->cr_port->json_key;
> +                    vector_len(&op->od->l3dgw_ports) == 1;
> +                json_key = vector_get(&op->od->l3dgw_ports, 0,
> +                                      struct ovn_port
> *)->cr_port->json_key;
>              }
>
>              if (add_chassis_resident_check) {
> @@ -16127,7 +16109,7 @@ build_lrouter_ipv4_ip_input(struct ovn_port *op,
>                                 &op->nbrp->header_, lflows, lflow_ref);
>      }
>
> -    if (!op->od->is_gw_router && !op->od->n_l3dgw_ports) {
> +    if (!op->od->is_gw_router && vector_is_empty(&op->od->l3dgw_ports)) {
>          /* UDP/TCP/SCTP port unreachable. */
>          for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
>              ds_clear(match);
> @@ -16321,7 +16303,7 @@ build_lrouter_in_unsnat_match(const struct
> ovn_datapath *od,
>          /* Traffic received on l3dgw_port is subject to NAT. */
>          ds_put_format(match, " && inport == %s", l3dgw_port->json_key);
>
> -        if (!distributed_nat && od->n_l3dgw_ports) {
> +        if (!distributed_nat && !vector_is_empty(&od->l3dgw_ports)) {
>              /* Flows for NAT rules that are centralized are only
>               * programmed on the gateway chassis. */
>              ds_put_format(match, " && is_chassis_resident(%s)",
> @@ -16459,7 +16441,7 @@ build_lrouter_in_dnat_flow(struct lflow_table
> *lflows,
>
>          /* Traffic received on l3dgw_port is subject to NAT. */
>          ds_put_format(match, " && inport == %s", l3dgw_port->json_key);
> -        if (!distributed_nat && od->n_l3dgw_ports) {
> +        if (!distributed_nat && !vector_is_empty(&od->l3dgw_ports)) {
>              /* Flows for NAT rules that are centralized are only
>              * programmed on the gateway chassis. */
>              ds_put_format(match, " && is_chassis_resident(%s)",
> @@ -16509,7 +16491,7 @@ build_lrouter_out_undnat_flow(struct lflow_table
> *lflows,
>      *
>      * Note that this only applies for NAT on a distributed router.
>      */
> -    if (!od->n_l3dgw_ports ||
> +    if (vector_is_empty(&od->l3dgw_ports) ||
>          !(nat_entry->type == DNAT || nat_entry->type == DNAT_AND_SNAT)) {
>          return;
>      }
> @@ -16521,7 +16503,7 @@ build_lrouter_out_undnat_flow(struct lflow_table
> *lflows,
>      ds_put_format(match, "ip && ip%c.src == %s && outport == %s",
>                    is_v6 ? '6' : '4', nat->logical_ip,
>                    l3dgw_port->json_key);
> -    if (!distributed_nat && od->n_l3dgw_ports) {
> +    if (!distributed_nat && !vector_is_empty(&od->l3dgw_ports)) {
>          /* Flows for NAT rules that are centralized are only
>          * programmed on the gateway chassis. */
>          ds_put_format(match, " && is_chassis_resident(%s)",
> @@ -16556,7 +16538,7 @@ build_lrouter_out_is_dnat_local(struct lflow_table
> *lflows,
>  {
>      /* Note that this only applies for NAT on a distributed router.
>       */
> -    if (!od->n_l3dgw_ports) {
> +    if (vector_is_empty(&od->l3dgw_ports)) {
>          return;
>      }
>
> @@ -16601,7 +16583,7 @@ build_lrouter_out_snat_match(struct lflow_table
> *lflows,
>          ds_put_format(match, " && %s == %s",
>                        is_reverse ? "inport" : "outport",
>                        l3dgw_port->json_key);
> -        if (od->n_l3dgw_ports) {
> +        if (!vector_is_empty(&od->l3dgw_ports)) {
>              ds_put_format(match, " && is_chassis_resident(\"%s\")",
>                            distributed_nat
>                            ? nat->logical_port
> @@ -16871,7 +16853,7 @@ build_lrouter_ingress_flow(struct lflow_table
> *lflows,
>                             struct lflow_ref *lflow_ref)
>  {
>      const struct nbrec_nat *nat = nat_entry->nb;
> -    if (od->n_l3dgw_ports && nat_entry->type == SNAT) {
> +    if (!vector_is_empty(&od->l3dgw_ports) && nat_entry->type == SNAT) {
>          ds_clear(match);
>          ds_put_format(
>              match, "inport == %s && %s == %s",
> @@ -17011,7 +16993,7 @@ build_dgp_lrouter_commit_all(const struct
> ovn_datapath *od,
>                               const struct chassis_features *features,
>                               struct ds *match, struct lflow_ref
> *lflow_ref)
>  {
> -    ovs_assert(od->n_l3dgw_ports);
> +    ovs_assert(!vector_is_empty(&od->l3dgw_ports));
>      if (!(features->ct_commit_to_zone && features->ct_next_zone)) {
>          return;
>      }
> @@ -17155,7 +17137,7 @@ build_lrouter_nat_defrag_and_lb(
>      /* NAT rules are only valid on Gateway routers and routers with
>       * l3dgw_ports (router has port(s) with gateway chassis
>       * specified). */
> -    if (!od->is_gw_router && !od->n_l3dgw_ports) {
> +    if (!od->is_gw_router && vector_is_empty(&od->l3dgw_ports)) {
>          return;
>      }
>
> @@ -17262,7 +17244,8 @@ build_lrouter_nat_defrag_and_lb(
>                              match,
>                              "outport == %s && ip%s.src == %s "
>                              "&& is_chassis_resident(\"%s\")",
> -                            od->l3dgw_ports[0]->json_key,
> +                            vector_get(&od->l3dgw_ports, 0,
> +                                       const struct ovn_port *)->json_key,
>                              is_v6 ? "6" : "4", nat->logical_ip,
>                              nat->logical_port);
>                      ds_clear(actions);
> @@ -17375,7 +17358,7 @@ build_lrouter_nat_defrag_and_lb(
>           * gateway port have ip.dst matching a NAT external IP, then
>           * loop a clone of the packet back to the beginning of the
>           * ingress pipeline with inport = outport. */
> -        if (od->n_l3dgw_ports) {
> +        if (!vector_is_empty(&od->l3dgw_ports)) {
>              /* Distributed router. */
>              ds_clear(match);
>              ds_put_format(match, "ip%s.dst == %s && outport == %s",
> @@ -17441,10 +17424,10 @@ build_lrouter_nat_defrag_and_lb(
>              build_gw_lrouter_commit_all(od, lflows, features, lflow_ref);
>          }
>
> -        for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> -            struct ovn_port *l3dgw_port = od->l3dgw_ports[i];
> -            build_dgp_lrouter_commit_all(od, l3dgw_port, lflows,
> -                                         features, match, lflow_ref);
> +        const struct ovn_port *dgp;
> +        VECTOR_FOR_EACH (&od->l3dgw_ports, dgp) {
> +            build_dgp_lrouter_commit_all(od, dgp, lflows, features,
> +                                         match, lflow_ref);
>          }
>      }
>
> @@ -17548,8 +17531,9 @@ build_routable_flows_for_router_port(
>      struct ovn_port_routable_addresses ra =
>          get_op_routable_addresses(lrp, lr_stateful_rec);
>
> -    for (size_t i = 0; i < peer_ls->n_router_ports; i++) {
> -        struct ovn_port *router_port = peer_ls->router_ports[i]->peer;
> +    struct ovn_port *op;
> +    VECTOR_FOR_EACH (&peer_ls->router_ports, op) {
> +        struct ovn_port *router_port = op->peer;
>          struct lport_addresses *lrpaddrs = &router_port->lrp_networks;
>          char *router_port_lla_s = NULL;
>
> @@ -19513,12 +19497,13 @@ collect_lr_groups_for_ha_chassis_groups(const
> struct sbrec_port_binding *sb,
>                                          struct hmapx *lr_groups)
>  {
>      struct lrouter_group *lr_group = NULL;
> -    for (size_t i = 0; i < op->od->n_router_ports; i++) {
> -        if (!op->od->router_ports[i]->peer) {
> +    struct ovn_port *rp;
> +    VECTOR_FOR_EACH (&op->od->router_ports, rp) {
> +        if (!rp->peer) {
>              continue;
>          }
>
> -        lr_group = op->od->router_ports[i]->peer->od->lr_group;
> +        lr_group = rp->peer->od->lr_group;
>          /* If a logical switch has multiple router ports, then
>           * all the logical routers belong to the same logical
>           * router group. */
> diff --git a/northd/northd.h b/northd/northd.h
> index d5294e9bf..5a698458f 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -26,6 +26,7 @@
>  #include "simap.h"
>  #include "ovs-thread.h"
>  #include "en-lr-stateful.h"
> +#include "vec.h"
>
>  struct northd_input {
>      /* Northbound table references */
> @@ -365,15 +366,11 @@ struct ovn_datapath {
>      uint32_t tunnel_key;
>
>      /* Logical router data. */
> -    struct ovn_datapath **ls_peers;
> -    size_t n_ls_peers;
> -    size_t n_allocated_ls_peers;
> +    struct vector ls_peers; /* Vector of struct ovn_datapath *. */
>      struct sset router_ips; /* Router port IPs except the IPv6 LLAs. */
>
>      /* Logical switch data. */
> -    struct ovn_port **router_ports;
> -    size_t n_router_ports;
> -    size_t n_allocated_router_ports;
> +    struct vector router_ports; /* Vector of struct ovn_port *. */
>
>      struct hmap port_tnlids;
>      uint32_t port_key_hint;
> @@ -402,9 +399,7 @@ struct ovn_datapath {
>       * populated only when there is a gateway chassis or ha chassis group
>       * specified for some of the ports on the logical router. Otherwise
> this
>       * will be NULL. */
> -    struct ovn_port **l3dgw_ports;
> -    size_t n_l3dgw_ports;
> -    size_t n_allocated_l3dgw_ports;
> +    struct vector l3dgw_ports; /* Vector of struct ovn_port *. */
>
>      /* router datapath has a logical port with redirect-type set to
> bridged. */
>      bool redirect_bridged;
> @@ -413,9 +408,7 @@ struct ovn_datapath {
>      /* The modes contained in the nbr option
> "dynamic-routing-redistribute". */
>      enum dynamic_routing_redistribute_mode dynamic_routing_redistribute;
>
> -    struct ovn_port **localnet_ports;
> -    size_t n_localnet_ports;
> -    size_t n_allocated_localnet_ports;
> +    struct vector localnet_ports; /* Vector of struct ovn_port *. */
>
>      struct ovs_list lr_list; /* In list of logical router datapaths. */
>      /* The logical router group to which this datapath belongs.
> @@ -971,7 +964,7 @@ bool lrouter_port_ipv6_reachable(const struct ovn_port
> *,
>  static inline bool
>  lr_has_multiple_gw_ports(const struct ovn_datapath *od)
>  {
> -    return od->n_l3dgw_ports > 1 && !od->is_gw_router;
> +    return vector_len(&od->l3dgw_ports) > 1 && !od->is_gw_router;
>  }
>
>  bool
> --
> 2.49.0
>
>
Recheck-request: github-robot
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to