On Mon, Jul 21, 2025 at 9:38 PM Mark Michelson via dev <
ovs-dev@openvswitch.org> wrote:

> Hi Lucas, thanks for the patch!
>
> I think any issues I could bring up with this patch would be so minor
> that it's not worth putting out a v2 to address them.
>
> Acked-by: Mark Michelson <mmich...@redhat.com>
>
> If you decide to make a change like this one in the future, consider
> breaking it up into two patches:
>
> Patch 1: Move existing code around, no functional changes.
> Patch 2: Add the new functionality.
>
> This way it's easier to review since it's more obvious which functions
> have simply been moved, and which functions have been moved and added on
> to.
>
> Thanks again for the contribution!
>
> On 7/2/25 1:13 PM, Lucas Vargas Dias via dev wrote:
> > Handle the changes from LSP with dynamic addreses when its Logical Switch
> > has mac, subnet or ipv6 prefix configured.
> > Before, all logical switches were iterated even when they don't have
> > changes.
> > Also, move functions related to ipam for ipam header.
> > Test with 30000 LS and 70000 LSPs creating a LSP with dynamic address:
> > ovn-nbctl --print-wait-time --wait=sb lsp-add -- lsp-set-addresses
> > dynamic
> >
> > Without the I-P:
> > Time spent on processing nb_cfg 272726:
> >      ovn-northd delay before processing:    10ms
> >      ovn-northd completion:            21422ms
> >
> > With the I-P:
> > Time spent on processing nb_cfg 272733:
> >      ovn-northd delay before processing:    20ms
> >      ovn-northd completion:            101ms
> >
> >
> > Signed-off-by: Lucas Vargas Dias <lucas.vd...@luizalabs.com>
> > ---
>

Hi Lucas and Mark,

thank you for the patch.

First of all I agree with Mark please split the commit into two,
first that will only move the code around and second that
adds/modifies functionality. I also have a few comments down below.



> >   lib/ovn-util.c     |  37 +++
> >   lib/ovn-util.h     |   6 +
> >   northd/en-northd.c |   8 +-
> >   northd/ipam.c      | 502 +++++++++++++++++++++++++++++++++++++
> >   northd/ipam.h      |  40 +++
> >   northd/northd.c    | 601 ++++++---------------------------------------
> >   northd/northd.h    |  14 ++
> >   tests/ovn.at       |  16 +-
> >   8 files changed, 693 insertions(+), 531 deletions(-)
> >
> > diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> > index 9f0bc99ba..26b7f75c9 100644
> > --- a/lib/ovn-util.c
> > +++ b/lib/ovn-util.c
> > @@ -21,6 +21,7 @@
> >
> >   #include "daemon.h"
> >   #include "include/ovn/actions.h"
> > +#include "openvswitch/hmap.h"
> >   #include "openvswitch/ofp-parse.h"
> >   #include "openvswitch/rconn.h"
> >   #include "openvswitch/vlog.h"
> > @@ -34,6 +35,8 @@
> >   #include "svec.h"
> >   #include "unixctl.h"
> >   #include "dummy.h"
> > +#include "northd/northd.h"
> > +
> >
> >   VLOG_DEFINE_THIS_MODULE(ovn_util);
> >
> > @@ -1484,3 +1487,37 @@ ovn_mirror_port_name(const char *datapath_name,
> >   {
> >       return xasprintf("mp-%s-%s", datapath_name, port_name);
> >   }
> > +
> > +/* Returns the ovn_port that matches 'name'.  If 'prefer_bound' is true
> and
> > + * multiple ports share the same name, gives precendence to ports bound
> to
> > + * an ovn_datapath.
> > + */
> > +static struct ovn_port *
> > +ovn_port_find__(const struct hmap *ports, const char *name,
> > +                bool prefer_bound)
> > +{
> > +    struct ovn_port *matched_op = NULL;
> > +    struct ovn_port *op;
> > +
> > +    HMAP_FOR_EACH_WITH_HASH (op, key_node, hash_string(name, 0), ports)
> {
> > +        if (!strcmp(op->key, name)) {
> > +            matched_op = op;
> > +            if (!prefer_bound || op->od) {
> > +                return op;
> > +            }
> > +        }
> > +    }
> > +    return matched_op;
> > +}
> > +
> > +struct ovn_port *
> > +ovn_port_find(const struct hmap *ports, const char *name)
> > +{
> > +    return ovn_port_find__(ports, name, false);
> > +}
> > +
> > +struct ovn_port *
> > +ovn_port_find_bound(const struct hmap *ports, const char *name)
> > +{
> > +    return ovn_port_find__(ports, name, true);
> > +}


Why do we need to move all of this into ovn-util?
ipam.c imports northd.h which declares the ovn_port_find.
Also this code is northd specific and should be kept within
northd directory.


> > diff --git a/lib/ovn-util.h b/lib/ovn-util.h
> > index 2468a6de4..cca124e2a 100644
> > --- a/lib/ovn-util.h
> > +++ b/lib/ovn-util.h
> > @@ -504,6 +504,12 @@ const struct sbrec_port_binding
> *lport_lookup_by_name(
> >       struct ovsdb_idl_index *sbrec_port_binding_by_name,
> >       const char *name);
> >
> > +
> > +struct ovn_port;
> > +struct ovn_port * ovn_port_find(const struct hmap *, const char *);
> > +
> > +struct ovn_port * ovn_port_find_bound(const struct hmap *, const char
> *);
> > +
> >   /* __NARG__ provides a way to count the number of arguments passed to a
> >    * variadic macro. As defined below, it's capable of counting up to
> >    * 16 arguments. This should be more than enough for our purposes.
> However
> > diff --git a/northd/en-northd.c b/northd/en-northd.c
> > index 8402ab943..5f05a0387 100644
> > --- a/northd/en-northd.c
> > +++ b/northd/en-northd.c
> > @@ -156,7 +156,13 @@ northd_nb_logical_switch_handler(struct engine_node
> *node,
> >           return EN_UNHANDLED;
> >       }
> >
> > -    if (northd_has_tracked_data(&nd->trk_data)) {
> > +    if (northd_has_tracked_data(&nd->trk_data) &&
> > +        !northd_has_ls_ipam_in_tracked_data(&nd->trk_data)) {
> > +        return EN_HANDLED_UPDATED;
> > +    }
> > +
> > +    if (northd_has_ls_ipam_in_tracked_data(&nd->trk_data) &&
> > +        northd_handle_ipam_changes(nd)) {
>

This logic is kind of strange, why can't we handle the IPAM changes
right after the 'northd_handle_ls_changes()' if?


> >           return EN_HANDLED_UPDATED;
> >       }
> >
> > diff --git a/northd/ipam.c b/northd/ipam.c
> > index 04fa357a5..e1274a8f2 100644
> > --- a/northd/ipam.c
> > +++ b/northd/ipam.c
> > @@ -6,6 +6,7 @@
> >   #include <netinet/in.h>
> >
> >   #include "ipam.h"
> > +#include "northd/northd.h"
> >   #include "ovn/lex.h"
> >
> >   #include "smap.h"
> > @@ -23,6 +24,34 @@ static void init_ipam_ipv4(const char *subnet_str,
> >   static bool ipam_is_duplicate_mac(struct eth_addr *ea, uint64_t mac64,
> >                                     bool warn);
> >
> > +static void
> > +ipam_insert_ip_for_datapath(struct ovn_datapath *, uint32_t, bool);
> > +
> > +static void
> > +ipam_insert_lsp_addresses(struct ovn_datapath *, struct lport_addresses
> *);
> > +
> > +static enum dynamic_update_type
> > +dynamic_mac_changed(const char *, struct dynamic_address_update *);
> > +
> > +static enum dynamic_update_type
> > +dynamic_ip4_changed(const char *, struct dynamic_address_update *);
> > +
> > +static enum dynamic_update_type
> > +dynamic_ip6_changed(const char *, struct dynamic_address_update *);
> > +
> > +static bool
> > +dynamic_addresses_check_for_updates(const char *,
> > +                                    struct dynamic_address_update *);
> > +
> > +static void
> > +update_unchanged_dynamic_addresses(struct dynamic_address_update *);
> > +
> > +static void
> > +set_lsp_dynamic_addresses(const char *, struct ovn_port *);
> > +
> > +static void
> > +set_dynamic_updates(const char *, struct dynamic_address_update *);
>

All function declarations should have the name and return type on
the same line.



> > +
> >   void
> >   init_ipam_info(struct ipam_info *info, const struct smap *config,
> const char *id)
> >   {
> > @@ -40,6 +69,19 @@ init_ipam_info(struct ipam_info *info, const struct
> smap *config, const char *id
> >       }
> >   }
> >
> > +void
> > +init_ipam_info_for_datapath(struct ovn_datapath *od)
> > +{
> > +    if (!od->nbs || od->ipam_info_initilized) {
> > +        return;
> > +    }
> > +
> > +    char uuid_s[UUID_LEN + 1];
> > +    sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
> > +    init_ipam_info(&od->ipam_info, &od->nbs->other_config, uuid_s);
> > +    od->ipam_info_initilized = true;
> > +}
> > +
> >   void
> >   destroy_ipam_info(struct ipam_info *info)
> >   {
> > @@ -69,6 +111,17 @@ ipam_insert_ip(struct ipam_info *info, uint32_t ip,
> bool dynamic)
> >       return true;
> >   }
> >
> > +static void
> > +ipam_insert_ip_for_datapath(struct ovn_datapath *od, uint32_t ip,
> > +                            bool dynamic)
> > +{
> > +    if (!od) {
> > +        return;
> > +    }
> > +
> > +    ipam_insert_ip(&od->ipam_info, ip, dynamic);
> > +}
> > +
> >   uint32_t
> >   ipam_get_unused_ip(struct ipam_info *info)
> >   {
> > @@ -338,3 +391,452 @@ ipam_is_duplicate_mac(struct eth_addr *ea,
> uint64_t mac64, bool warn)
> >       return false;
> >   }
> >
> > +static enum dynamic_update_type
> > +dynamic_mac_changed(const char *lsp_addresses,
> > +                    struct dynamic_address_update *update)
> > +{
> > +   struct eth_addr ea;
> > +
> > +   if (ovs_scan(lsp_addresses, ETH_ADDR_SCAN_FMT,
> ETH_ADDR_SCAN_ARGS(ea))) {
> > +       if (eth_addr_equals(ea, update->current_addresses.ea)) {
> > +           return NONE;
> > +       } else {
> > +           /* MAC is still static, but it has changed */
> > +           update->static_mac = ea;
> > +           return STATIC;
> > +       }
> > +   }
> > +
> > +   uint64_t mac64 = eth_addr_to_uint64(update->current_addresses.ea);
> > +   uint64_t prefix = eth_addr_to_uint64(get_mac_prefix());
> > +
> > +   if ((mac64 ^ prefix) >> 24) {
> > +       return DYNAMIC;
> > +   } else {
> > +       return NONE;
> > +   }
> > +}
> > +
> > +static enum dynamic_update_type
> > +dynamic_ip4_changed(const char *lsp_addrs,
> > +                    struct dynamic_address_update *update)
> > +{
> > +    const struct ipam_info *ipam = &update->op->od->ipam_info;
> > +    const struct lport_addresses *cur_addresses =
> &update->current_addresses;
> > +    bool dynamic_ip4 = ipam->allocated_ipv4s != NULL;
> > +
> > +    if (!dynamic_ip4) {
> > +        if (update->current_addresses.n_ipv4_addrs) {
> > +            return REMOVE;
> > +        } else {
> > +            return NONE;
> > +        }
> > +    }
> > +
> > +    if (!cur_addresses->n_ipv4_addrs) {
> > +        /* IPv4 was previously static but now is dynamic */
> > +        return DYNAMIC;
> > +    }
> > +
> > +    uint32_t ip4 = ntohl(cur_addresses->ipv4_addrs[0].addr);
> > +    if (ip4 < ipam->start_ipv4) {
> > +        return DYNAMIC;
> > +    }
> > +
> > +    uint32_t index = ip4 - ipam->start_ipv4;
> > +    if (index >= ipam->total_ipv4s - 1 ||
> > +        bitmap_is_set(ipam->allocated_ipv4s, index)) {
> > +        /* Previously assigned dynamic IPv4 address can no longer be
> used.
> > +         * It's either outside the subnet, conflicts with an excluded
> IP,
> > +         * or conflicts with a statically-assigned address on the switch
> > +         */
> > +        return DYNAMIC;
> > +    } else {
> > +        char ipv6_s[IPV6_SCAN_LEN + 1];
> > +        ovs_be32 new_ip;
> > +        int n = 0;
> > +
> > +        if ((ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT"%n",
> > +                     IP_SCAN_ARGS(&new_ip), &n)
> > +             && lsp_addrs[n] == '\0') ||
> > +            (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT"
> "IPV6_SCAN_FMT"%n",
> > +                      IP_SCAN_ARGS(&new_ip), ipv6_s, &n)
> > +             && lsp_addrs[n] == '\0')) {
> > +            index = ntohl(new_ip) - ipam->start_ipv4;
> > +            if (ntohl(new_ip) < ipam->start_ipv4 ||
> > +                index > ipam->total_ipv4s ||
> > +                bitmap_is_set(ipam->allocated_ipv4s, index)) {
> > +                /* new static ip is not valid */
> > +                return DYNAMIC;
> > +            } else if (cur_addresses->ipv4_addrs[0].addr != new_ip) {
> > +                update->ipv4 = STATIC;
> > +                update->static_ip = new_ip;
> > +                return STATIC;
> > +            }
> > +        }
> > +        return NONE;
> > +    }
> > +}
> > +
> > +static enum dynamic_update_type
> > +dynamic_ip6_changed(const char *lsp_addrs,
> > +                    struct dynamic_address_update *update)
> > +{
> > +    bool dynamic_ip6 = update->op->od->ipam_info.ipv6_prefix_set;
> > +    struct eth_addr ea;
> > +
> > +    if (!dynamic_ip6) {
> > +        if (update->current_addresses.n_ipv6_addrs) {
> > +            /* IPv6 was dynamic but now is not */
> > +            return REMOVE;
> > +        } else {
> > +            /* IPv6 has never been dynamic */
> > +            return NONE;
> > +        }
> > +    }
> > +
> > +    if (!update->current_addresses.n_ipv6_addrs ||
> > +        ovs_scan(lsp_addrs, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea)))
> {
> > +        /* IPv6 was previously static but now is dynamic */
> > +        return DYNAMIC;
> > +    }
> > +
> > +    const struct lport_addresses *cur_addresses;
> > +    char ipv6_s[IPV6_SCAN_LEN + 1];
> > +    ovs_be32 new_ip;
> > +    int n = 0;
> > +
> > +    if ((ovs_scan(lsp_addrs, "dynamic "IPV6_SCAN_FMT"%n",
> > +                  ipv6_s, &n) && lsp_addrs[n] == '\0') ||
> > +        (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT" "IPV6_SCAN_FMT"%n",
> > +                  IP_SCAN_ARGS(&new_ip), ipv6_s, &n)
> > +         && lsp_addrs[n] == '\0')) {
> > +        struct in6_addr ipv6;
> > +
> > +        if (!ipv6_parse(ipv6_s, &ipv6)) {
> > +            return DYNAMIC;
> > +        }
> > +
> > +        struct in6_addr masked = ipv6_addr_bitand(&ipv6,
> > +                &update->op->od->ipam_info.ipv6_prefix);
> > +        if (!IN6_ARE_ADDR_EQUAL(&masked,
> > +
> &update->op->od->ipam_info.ipv6_prefix)) {
> > +            return DYNAMIC;
> > +        }
> > +
> > +        cur_addresses = &update->current_addresses;
> > +
> > +        if (!IN6_ARE_ADDR_EQUAL(&cur_addresses->ipv6_addrs[0].addr,
> > +                                &ipv6)) {
> > +            update->static_ipv6 = ipv6;
> > +            return STATIC;
> > +        }
> > +    } else if (update->mac != NONE) {
> > +        return DYNAMIC;
> > +    }
> > +
> > +    return NONE;
> > +}
> > +
> > +/* Check previously assigned dynamic addresses for validity. This will
> > + * check if the assigned addresses need to change.
> > + *
> > + * Returns true if any changes to dynamic addresses are required
> > + */
> > +static bool
> > +dynamic_addresses_check_for_updates(const char *lsp_addrs,
> > +                                    struct dynamic_address_update
> *update)
> > +{
> > +    update->mac = dynamic_mac_changed(lsp_addrs, update);
> > +    update->ipv4 = dynamic_ip4_changed(lsp_addrs, update);
> > +    update->ipv6 = dynamic_ip6_changed(lsp_addrs, update);
> > +    if (update->mac == NONE &&
> > +        update->ipv4 == NONE &&
> > +        update->ipv6 == NONE) {
> > +        return false;
> > +    } else {
> > +        return true;
> > +    }
> > +}
> > +
> > +/* For addresses that do not need to be updated, go ahead and insert
> them
> > + * into IPAM. This way, their addresses will be claimed and cannot be
> assigned
> > + * elsewhere later.
> > + */
> > +static void
> > +update_unchanged_dynamic_addresses(struct dynamic_address_update
> *update)
> > +{
> > +    if (update->mac == NONE) {
> > +        ipam_insert_mac(&update->current_addresses.ea, false);
> > +    }
> > +    if (update->ipv4 == NONE && update->current_addresses.n_ipv4_addrs)
> {
> > +        ipam_insert_ip_for_datapath(update->op->od,
> > +
>  ntohl(update->current_addresses.ipv4_addrs[0].addr),
> > +                       true);
> > +    }
> > +}
> > +
> > +static void
> > +set_lsp_dynamic_addresses(const char *dynamic_addresses, struct
> ovn_port *op)
> > +{
> > +    extract_lsp_addresses(dynamic_addresses,
> &op->lsp_addrs[op->n_lsp_addrs]);
> > +    op->n_lsp_addrs++;
> > +}
> > +
> > +/* Determines which components (MAC, IPv4, and IPv6) of dynamic
> > + * addresses need to be assigned. This is used exclusively for
> > + * ports that do not have dynamic addresses already assigned.
> > + */
> > +static void
> > +set_dynamic_updates(const char *addrspec,
> > +                    struct dynamic_address_update *update)
> > +{
> > +    bool has_ipv4 = false, has_ipv6 = false;
> > +    char ipv6_s[IPV6_SCAN_LEN + 1];
> > +    struct eth_addr mac;
> > +    ovs_be32 ip;
> > +    int n = 0;
> > +    if (ovs_scan(addrspec, ETH_ADDR_SCAN_FMT" dynamic%n",
> > +                 ETH_ADDR_SCAN_ARGS(mac), &n)
> > +        && addrspec[n] == '\0') {
> > +        update->mac = STATIC;
> > +        update->static_mac = mac;
> > +    } else {
> > +        update->mac = DYNAMIC;
> > +    }
> > +
> > +    if ((ovs_scan(addrspec, "dynamic "IP_SCAN_FMT"%n",
> > +                 IP_SCAN_ARGS(&ip), &n) && addrspec[n] == '\0')) {
> > +        has_ipv4 = true;
> > +    } else if ((ovs_scan(addrspec, "dynamic "IPV6_SCAN_FMT"%n",
> > +                         ipv6_s, &n) && addrspec[n] == '\0')) {
> > +        has_ipv6 = true;
> > +    } else if ((ovs_scan(addrspec, "dynamic "IP_SCAN_FMT"
> "IPV6_SCAN_FMT"%n",
> > +                         IP_SCAN_ARGS(&ip), ipv6_s, &n)
> > +               && addrspec[n] == '\0')) {
> > +        has_ipv4 = has_ipv6 = true;
> > +    }
> > +
> > +    if (has_ipv4) {
> > +        update->ipv4 = STATIC;
> > +        update->static_ip = ip;
> > +    } else if (update->op->od->ipam_info.allocated_ipv4s) {
> > +        update->ipv4 = DYNAMIC;
> > +    } else {
> > +        update->ipv4 = NONE;
> > +    }
> > +
> > +    if (has_ipv6 && ipv6_parse(ipv6_s, &update->static_ipv6)) {
> > +        update->ipv6 = STATIC;
> > +    } else if (update->op->od->ipam_info.ipv6_prefix_set) {
> > +        update->ipv6 = DYNAMIC;
> > +    } else {
> > +        update->ipv6 = NONE;
> > +    }
> > +}
> > +
> > +void
> > +update_dynamic_addresses(struct dynamic_address_update *update)
> > +{
> > +    ovs_be32 ip4 = 0;
> > +    switch (update->ipv4) {
> > +    case NONE:
> > +        if (update->current_addresses.n_ipv4_addrs) {
> > +            ip4 = update->current_addresses.ipv4_addrs[0].addr;
> > +        }
> > +        break;
> > +    case REMOVE:
> > +        break;
> > +    case STATIC:
> > +        ip4 = update->static_ip;
> > +        break;
> > +    case DYNAMIC:
> > +        ip4 = htonl(ipam_get_unused_ip(&update->od->ipam_info));
> > +        VLOG_INFO("Assigned dynamic IPv4 address '"IP_FMT"' to port
> '%s'",
> > +                  IP_ARGS(ip4), update->op->nbsp->name);
> > +    }
> > +
> > +    struct eth_addr mac;
> > +    switch (update->mac) {
> > +    case NONE:
> > +        mac = update->current_addresses.ea;
> > +        break;
> > +    case REMOVE:
> > +        OVS_NOT_REACHED();
> > +    case STATIC:
> > +        mac = update->static_mac;
> > +        break;
> > +    case DYNAMIC:
> > +        eth_addr_from_uint64(ipam_get_unused_mac(ip4), &mac);
> > +        VLOG_INFO("Assigned dynamic MAC address '"ETH_ADDR_FMT"' to
> port '%s'",
> > +                  ETH_ADDR_ARGS(mac), update->op->nbsp->name);
> > +        break;
> > +    }
> > +
> > +    struct in6_addr ip6 = in6addr_any;
> > +    switch (update->ipv6) {
> > +    case NONE:
> > +        if (update->current_addresses.n_ipv6_addrs) {
> > +            ip6 = update->current_addresses.ipv6_addrs[0].addr;
> > +        }
> > +        break;
> > +    case REMOVE:
> > +        break;
> > +    case STATIC:
> > +        ip6 = update->static_ipv6;
> > +        break;
> > +    case DYNAMIC:
> > +        in6_generate_eui64(mac, &update->od->ipam_info.ipv6_prefix,
> &ip6);
> > +        struct ds ip6_ds = DS_EMPTY_INITIALIZER;
> > +        ipv6_format_addr(&ip6, &ip6_ds);
> > +        VLOG_INFO("Assigned dynamic IPv6 address '%s' to port '%s'",
> > +                  ip6_ds.string, update->op->nbsp->name);
> > +        ds_destroy(&ip6_ds);
> > +        break;
> > +    }
> > +
> > +    struct ds new_addr = DS_EMPTY_INITIALIZER;
> > +    ds_put_format(&new_addr, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
> > +    ipam_insert_mac(&mac, true);
> > +
> > +    if (ip4) {
> > +        ipam_insert_ip_for_datapath(update->od, ntohl(ip4), true);
> > +        ds_put_format(&new_addr, " "IP_FMT, IP_ARGS(ip4));
> > +    }
> > +    if (!IN6_ARE_ADDR_EQUAL(&ip6, &in6addr_any)) {
> > +        char ip6_s[INET6_ADDRSTRLEN + 1];
> > +        ipv6_string_mapped(ip6_s, &ip6);
> > +        ds_put_format(&new_addr, " %s", ip6_s);
> > +    }
> > +    nbrec_logical_switch_port_set_dynamic_addresses(update->op->nbsp,
> > +                                                    ds_cstr(&new_addr));
> > +    set_lsp_dynamic_addresses(ds_cstr(&new_addr), update->op);
> > +    ds_destroy(&new_addr);
> > +}
> > +
> > +
> > +void update_ipam_ls(struct ovn_datapath *od, struct hmap *ls_ports,
> > +                    struct ovs_list *updates, bool recompute)


nit: Missing newline after void.


> > +{
> > +    ovs_assert(od);
> > +    ovs_assert(od->nbs);
> > +    ovs_assert(updates);
> > +
> > +    for (size_t i = 0; i < od->nbs->n_ports; i++) {
> > +        const struct nbrec_logical_switch_port *nbsp =
> od->nbs->ports[i];
> > +
> > +        if (!od->ipam_info.allocated_ipv4s &&
> > +            !od->ipam_info.ipv6_prefix_set &&
> > +            !od->ipam_info.mac_only) {
> > +            if (nbsp->dynamic_addresses) {
> > +                nbrec_logical_switch_port_set_dynamic_addresses(nbsp,
> > +                                                                NULL);
> > +            }
> > +            continue;
> > +        }
> > +
> > +        struct ovn_port *op = ovn_port_find(ls_ports, nbsp->name);
> > +        if (!op || op->nbsp != nbsp || op->peer) {
> > +            /* Do not allocate addresses for logical switch ports that
> > +             * have a peer. */
> > +            continue;
> > +        }
> > +
> > +        int num_dynamic_addresses = 0;
>

We can use bool instead of int, looking at the code this will never
be larger than 1.

> +        for (size_t j = 0; j < nbsp->n_addresses; j++) {
> > +            if (!is_dynamic_lsp_address(nbsp->addresses[j])) {
> > +                continue;
> > +            }
> > +            if (num_dynamic_addresses) {
> > +                static struct vlog_rate_limit rl
> > +                    = VLOG_RATE_LIMIT_INIT(1, 1);
> > +                VLOG_WARN_RL(&rl, "More than one dynamic address "
> > +                             "configured for logical switch port '%s'",
> > +                             nbsp->name);
> > +                continue;
> > +            }
> > +            num_dynamic_addresses++;
> > +            struct dynamic_address_update *update
> > +                = xzalloc(sizeof *update);
>

Please use vector instead of list, this will avoid separate
allocations for each struct.


> > +            update->op = op;
> > +            update->od = od;
> > +            if (nbsp->dynamic_addresses) {
> > +                bool any_changed;
> > +                extract_lsp_addresses(nbsp->dynamic_addresses,
> > +                                      &update->current_addresses);
> > +                any_changed = dynamic_addresses_check_for_updates(
> > +                    nbsp->addresses[j], update);
> > +                update_unchanged_dynamic_addresses(update);
> > +                if (any_changed) {
> > +                    ovs_list_push_back(updates, &update->node);
> > +                } else {
> > +                    /* No changes to dynamic addresses */
> > +                    if (recompute) {
> > +
> set_lsp_dynamic_addresses(nbsp->dynamic_addresses, op);
> > +                    }
> > +                    destroy_lport_addresses(&update->current_addresses);
> > +                    free(update);
> > +                }
> > +            } else {
> > +                set_dynamic_updates(nbsp->addresses[j], update);
> > +                ovs_list_push_back(updates, &update->node);
> > +            }
> > +        }
> > +
> > +        if (!num_dynamic_addresses && nbsp->dynamic_addresses) {
> > +            nbrec_logical_switch_port_set_dynamic_addresses(nbsp, NULL);
> > +        }
> > +    }
> > +}
> > +
> > +void
> > +ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op)
> > +{
> > +    if (!od || !op) {
> > +        return;
> > +    }
> > +
> > +    if (op->n_lsp_non_router_addrs) {
> > +        /* Add all the port's addresses to address data structures. */
> > +        for (size_t i = 0; i < op->n_lsp_non_router_addrs; i++) {
> > +            ipam_insert_lsp_addresses(od, &op->lsp_addrs[i]);
> > +        }
> > +    } else if (op->lrp_networks.ea_s[0]) {
> > +        ipam_insert_mac(&op->lrp_networks.ea, true);
> > +
> > +        if (!op->peer || !op->peer->nbsp || !op->peer->od ||
> !op->peer->od->nbs
> > +            || !smap_get(&op->peer->od->nbs->other_config, "subnet")) {
> > +            return;
> > +        }
> > +
> > +        for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > +            uint32_t ip = ntohl(op->lrp_networks.ipv4_addrs[i].addr);
> > +            /* If the router has the first IP address of the subnet,
> don't add
> > +             * it to IPAM. We already added this when we initialized
> IPAM for
> > +             * the datapath. This will just result in an erroneous
> message
> > +             * about a duplicate IP address.
> > +             */
> > +            if (ip != op->peer->od->ipam_info.start_ipv4) {
> > +                ipam_insert_ip_for_datapath(op->peer->od, ip, false);
> > +            }
> > +        }
> > +    }
> > +}
> > +
> > +static void
> > +ipam_insert_lsp_addresses(struct ovn_datapath *od,
> > +                          struct lport_addresses *laddrs)
> > +{
> > +    ipam_insert_mac(&laddrs->ea, true);
> > +
> > +    /* IP is only added to IPAM if the switch's subnet option
> > +     * is set, whereas MAC is always added to MACAM. */
> > +    if (!od->ipam_info.allocated_ipv4s) {
> > +        return;
> > +    }
> > +
> > +    for (size_t j = 0; j < laddrs->n_ipv4_addrs; j++) {
> > +        uint32_t ip = ntohl(laddrs->ipv4_addrs[j].addr);
> > +        ipam_insert_ip_for_datapath(od, ip, false);
> > +    }
> > +}
> > diff --git a/northd/ipam.h b/northd/ipam.h
> > index 6240f9ab7..3c8a55a75 100644
> > --- a/northd/ipam.h
> > +++ b/northd/ipam.h
> > @@ -5,6 +5,9 @@
> >   #include <stdbool.h>
> >
> >   #include "openvswitch/types.h"
> > +#include "openvswitch/list.h"
> > +
> > +#include "lib/ovn-util.h"
> >
> >   struct ipam_info {
> >       uint32_t start_ipv4;
> > @@ -17,9 +20,39 @@ struct ipam_info {
> >   };
> >
> >   struct smap;
> > +struct hmap;
> > +struct ovn_datapath;
> > +struct ovn_port;
> > +
> > +
> > +enum dynamic_update_type {
> > +    NONE,    /* No change to the address */
> > +    REMOVE,  /* Address is no longer dynamic */
> > +    STATIC,  /* Use static address (MAC only) */
> > +    DYNAMIC, /* Assign a new dynamic address */
> > +};
> > +
> > +struct dynamic_address_update {
> > +    struct ovs_list node;       /* In build_ipam()'s list of updates. */
> > +
> > +    struct ovn_datapath *od;
> > +    struct ovn_port *op;
> > +
> > +    struct lport_addresses current_addresses;
> > +    struct eth_addr static_mac;
> > +    ovs_be32 static_ip;
> > +    struct in6_addr static_ipv6;
> > +    enum dynamic_update_type mac;
> > +    enum dynamic_update_type ipv4;
> > +    enum dynamic_update_type ipv6;
> > +};
> > +
> >   void init_ipam_info(struct ipam_info *info, const struct smap *config,
> >                       const char *id);
> >
> > +
> > +void init_ipam_info_for_datapath(struct ovn_datapath *od);
> > +
> >   void destroy_ipam_info(struct ipam_info *info);
> >
> >   bool ipam_insert_ip(struct ipam_info *info, uint32_t ip, bool dynamic);
> > @@ -36,4 +69,11 @@ struct eth_addr get_mac_prefix(void);
> >
> >   const char *set_mac_prefix(const char *hint);
> >
> > +void update_ipam_ls(struct ovn_datapath *, struct hmap *,
> > +                    struct ovs_list *, bool);
> > +
> > +void update_dynamic_addresses(struct dynamic_address_update *);
> > +
> > +void ipam_add_port_addresses(struct ovn_datapath *, struct ovn_port *);
> > +
> >   #endif /* NORTHD_IPAM_H */
> > diff --git a/northd/northd.c b/northd/northd.c
> > index 9b21786fa..66080777d 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -504,6 +504,7 @@ ovn_datapath_create(struct hmap *datapaths, const
> struct uuid *key,
> >       od->l3dgw_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
> >       od->localnet_ports = VECTOR_EMPTY_INITIALIZER(struct ovn_port *);
> >       od->lb_with_stateless_mode = false;
> > +    od->ipam_info_initilized = false;
> >       return od;
> >   }
> >
> > @@ -604,18 +605,6 @@ lrouter_is_enabled(const struct
> nbrec_logical_router *lrouter)
> >       return !lrouter->enabled || *lrouter->enabled;
> >   }
> >
> > -static void
> > -init_ipam_info_for_datapath(struct ovn_datapath *od)
> > -{
> > -    if (!od->nbs) {
> > -        return;
> > -    }
> > -
> > -    char uuid_s[UUID_LEN + 1];
> > -    sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
> > -    init_ipam_info(&od->ipam_info, &od->nbs->other_config, uuid_s);
> > -}
> > -
> >   static void
> >   init_mcast_info_for_router_datapath(struct ovn_datapath *od)
> >   {
> > @@ -930,6 +919,10 @@ join_datapaths(const struct
> nbrec_logical_switch_table *nbrec_ls_table,
> >               ovs_list_remove(&od->list);
> >               ovs_list_push_back(both, &od->list);
> >               ovn_datapath_update_external_ids(od);
> > +            if (od->ipam_info_initilized) {
> > +                destroy_ipam_info(&od->ipam_info);
> > +                od->ipam_info_initilized = false;
> > +            }
> >           } else {
> >               od = ovn_datapath_create(datapaths, &nbs->header_.uuid,
> >                                        nbs, NULL, NULL);
> > @@ -1336,40 +1329,6 @@ ovn_port_destroy(struct hmap *ports, struct
> ovn_port *port)
> >       }
> >   }
> >
> > -/* Returns the ovn_port that matches 'name'.  If 'prefer_bound' is true
> and
> > - * multiple ports share the same name, gives precendence to ports bound
> to
> > - * an ovn_datapath.
> > - */
> > -static struct ovn_port *
> > -ovn_port_find__(const struct hmap *ports, const char *name,
> > -                bool prefer_bound)
> > -{
> > -    struct ovn_port *matched_op = NULL;
> > -    struct ovn_port *op;
> > -
> > -    HMAP_FOR_EACH_WITH_HASH (op, key_node, hash_string(name, 0), ports)
> {
> > -        if (!strcmp(op->key, name)) {
> > -            matched_op = op;
> > -            if (!prefer_bound || op->od) {
> > -                return op;
> > -            }
> > -        }
> > -    }
> > -    return matched_op;
> > -}
> > -
> > -struct ovn_port *
> > -ovn_port_find(const struct hmap *ports, const char *name)
> > -{
> > -    return ovn_port_find__(ports, name, false);
> > -}
> > -
> > -static struct ovn_port *
> > -ovn_port_find_bound(const struct hmap *ports, const char *name)
> > -{
> > -    return ovn_port_find__(ports, name, true);
> > -}
> > -
> >   static bool
> >   lsp_is_clone_to_unknown(const struct nbrec_logical_switch_port *nbsp)
> >   {
> > @@ -1502,67 +1461,7 @@ ovn_port_get_peer(const struct hmap *lr_ports,
> struct ovn_port *op)
> >       return ovn_port_find(lr_ports, peer_name);
> >   }
> >
> > -static void
> > -ipam_insert_ip_for_datapath(struct ovn_datapath *od, uint32_t ip, bool
> dynamic)
> > -{
> > -    if (!od) {
> > -        return;
> > -    }
> > -
> > -    ipam_insert_ip(&od->ipam_info, ip, dynamic);
> > -}
> > -
> > -static void
> > -ipam_insert_lsp_addresses(struct ovn_datapath *od,
> > -                          struct lport_addresses *laddrs)
> > -{
> > -    ipam_insert_mac(&laddrs->ea, true);
> >
> > -    /* IP is only added to IPAM if the switch's subnet option
> > -     * is set, whereas MAC is always added to MACAM. */
> > -    if (!od->ipam_info.allocated_ipv4s) {
> > -        return;
> > -    }
> > -
> > -    for (size_t j = 0; j < laddrs->n_ipv4_addrs; j++) {
> > -        uint32_t ip = ntohl(laddrs->ipv4_addrs[j].addr);
> > -        ipam_insert_ip_for_datapath(od, ip, false);
> > -    }
> > -}
> > -
> > -static void
> > -ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op)
> > -{
> > -    if (!od || !op) {
> > -        return;
> > -    }
> > -
> > -    if (op->n_lsp_non_router_addrs) {
> > -        /* Add all the port's addresses to address data structures. */
> > -        for (size_t i = 0; i < op->n_lsp_non_router_addrs; i++) {
> > -            ipam_insert_lsp_addresses(od, &op->lsp_addrs[i]);
> > -        }
> > -    } else if (op->lrp_networks.ea_s[0]) {
> > -        ipam_insert_mac(&op->lrp_networks.ea, true);
> > -
> > -        if (!op->peer || !op->peer->nbsp || !op->peer->od ||
> !op->peer->od->nbs
> > -            || !smap_get(&op->peer->od->nbs->other_config, "subnet")) {
> > -            return;
> > -        }
> > -
> > -        for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
> > -            uint32_t ip = ntohl(op->lrp_networks.ipv4_addrs[i].addr);
> > -            /* If the router has the first IP address of the subnet,
> don't add
> > -             * it to IPAM. We already added this when we initialized
> IPAM for
> > -             * the datapath. This will just result in an erroneous
> message
> > -             * about a duplicate IP address.
> > -             */
> > -            if (ip != op->peer->od->ipam_info.start_ipv4) {
> > -                ipam_insert_ip_for_datapath(op->peer->od, ip, false);
> > -            }
> > -        }
> > -    }
> > -}
> >
> >   /* Returns true if the given router port 'op' (assumed to be a
> distributed
> >    * gateway port) is the relevant DGP where the NAT rule of the router
> needs to
> > @@ -1578,350 +1477,6 @@ is_nat_gateway_port(const struct nbrec_nat *nat,
> const struct ovn_port *op)
> >       return true;
> >   }
> >
> > -enum dynamic_update_type {
> > -    NONE,    /* No change to the address */
> > -    REMOVE,  /* Address is no longer dynamic */
> > -    STATIC,  /* Use static address (MAC only) */
> > -    DYNAMIC, /* Assign a new dynamic address */
> > -};
> > -
> > -struct dynamic_address_update {
> > -    struct ovs_list node;       /* In build_ipam()'s list of updates. */
> > -
> > -    struct ovn_datapath *od;
> > -    struct ovn_port *op;
> > -
> > -    struct lport_addresses current_addresses;
> > -    struct eth_addr static_mac;
> > -    ovs_be32 static_ip;
> > -    struct in6_addr static_ipv6;
> > -    enum dynamic_update_type mac;
> > -    enum dynamic_update_type ipv4;
> > -    enum dynamic_update_type ipv6;
> > -};
> > -
> > -static enum dynamic_update_type
> > -dynamic_mac_changed(const char *lsp_addresses,
> > -                    struct dynamic_address_update *update)
> > -{
> > -   struct eth_addr ea;
> > -
> > -   if (ovs_scan(lsp_addresses, ETH_ADDR_SCAN_FMT,
> ETH_ADDR_SCAN_ARGS(ea))) {
> > -       if (eth_addr_equals(ea, update->current_addresses.ea)) {
> > -           return NONE;
> > -       } else {
> > -           /* MAC is still static, but it has changed */
> > -           update->static_mac = ea;
> > -           return STATIC;
> > -       }
> > -   }
> > -
> > -   uint64_t mac64 = eth_addr_to_uint64(update->current_addresses.ea);
> > -   uint64_t prefix = eth_addr_to_uint64(get_mac_prefix());
> > -
> > -   if ((mac64 ^ prefix) >> 24) {
> > -       return DYNAMIC;
> > -   } else {
> > -       return NONE;
> > -   }
> > -}
> > -
> > -static enum dynamic_update_type
> > -dynamic_ip4_changed(const char *lsp_addrs,
> > -                    struct dynamic_address_update *update)
> > -{
> > -    const struct ipam_info *ipam = &update->op->od->ipam_info;
> > -    const struct lport_addresses *cur_addresses =
> &update->current_addresses;
> > -    bool dynamic_ip4 = ipam->allocated_ipv4s != NULL;
> > -
> > -    if (!dynamic_ip4) {
> > -        if (update->current_addresses.n_ipv4_addrs) {
> > -            return REMOVE;
> > -        } else {
> > -            return NONE;
> > -        }
> > -    }
> > -
> > -    if (!cur_addresses->n_ipv4_addrs) {
> > -        /* IPv4 was previously static but now is dynamic */
> > -        return DYNAMIC;
> > -    }
> > -
> > -    uint32_t ip4 = ntohl(cur_addresses->ipv4_addrs[0].addr);
> > -    if (ip4 < ipam->start_ipv4) {
> > -        return DYNAMIC;
> > -    }
> > -
> > -    uint32_t index = ip4 - ipam->start_ipv4;
> > -    if (index >= ipam->total_ipv4s - 1 ||
> > -        bitmap_is_set(ipam->allocated_ipv4s, index)) {
> > -        /* Previously assigned dynamic IPv4 address can no longer be
> used.
> > -         * It's either outside the subnet, conflicts with an excluded
> IP,
> > -         * or conflicts with a statically-assigned address on the switch
> > -         */
> > -        return DYNAMIC;
> > -    } else {
> > -        char ipv6_s[IPV6_SCAN_LEN + 1];
> > -        ovs_be32 new_ip;
> > -        int n = 0;
> > -
> > -        if ((ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT"%n",
> > -                     IP_SCAN_ARGS(&new_ip), &n)
> > -             && lsp_addrs[n] == '\0') ||
> > -            (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT"
> "IPV6_SCAN_FMT"%n",
> > -                      IP_SCAN_ARGS(&new_ip), ipv6_s, &n)
> > -             && lsp_addrs[n] == '\0')) {
> > -            index = ntohl(new_ip) - ipam->start_ipv4;
> > -            if (ntohl(new_ip) < ipam->start_ipv4 ||
> > -                index > ipam->total_ipv4s ||
> > -                bitmap_is_set(ipam->allocated_ipv4s, index)) {
> > -                /* new static ip is not valid */
> > -                return DYNAMIC;
> > -            } else if (cur_addresses->ipv4_addrs[0].addr != new_ip) {
> > -                update->ipv4 = STATIC;
> > -                update->static_ip = new_ip;
> > -                return STATIC;
> > -            }
> > -        }
> > -        return NONE;
> > -    }
> > -}
> > -
> > -static enum dynamic_update_type
> > -dynamic_ip6_changed(const char *lsp_addrs,
> > -                    struct dynamic_address_update *update)
> > -{
> > -    bool dynamic_ip6 = update->op->od->ipam_info.ipv6_prefix_set;
> > -    struct eth_addr ea;
> > -
> > -    if (!dynamic_ip6) {
> > -        if (update->current_addresses.n_ipv6_addrs) {
> > -            /* IPv6 was dynamic but now is not */
> > -            return REMOVE;
> > -        } else {
> > -            /* IPv6 has never been dynamic */
> > -            return NONE;
> > -        }
> > -    }
> > -
> > -    if (!update->current_addresses.n_ipv6_addrs ||
> > -        ovs_scan(lsp_addrs, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea)))
> {
> > -        /* IPv6 was previously static but now is dynamic */
> > -        return DYNAMIC;
> > -    }
> > -
> > -    const struct lport_addresses *cur_addresses;
> > -    char ipv6_s[IPV6_SCAN_LEN + 1];
> > -    ovs_be32 new_ip;
> > -    int n = 0;
> > -
> > -    if ((ovs_scan(lsp_addrs, "dynamic "IPV6_SCAN_FMT"%n",
> > -                  ipv6_s, &n) && lsp_addrs[n] == '\0') ||
> > -        (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT" "IPV6_SCAN_FMT"%n",
> > -                  IP_SCAN_ARGS(&new_ip), ipv6_s, &n)
> > -         && lsp_addrs[n] == '\0')) {
> > -        struct in6_addr ipv6;
> > -
> > -        if (!ipv6_parse(ipv6_s, &ipv6)) {
> > -            return DYNAMIC;
> > -        }
> > -
> > -        struct in6_addr masked = ipv6_addr_bitand(&ipv6,
> > -                &update->op->od->ipam_info.ipv6_prefix);
> > -        if (!IN6_ARE_ADDR_EQUAL(&masked,
> > -
> &update->op->od->ipam_info.ipv6_prefix)) {
> > -            return DYNAMIC;
> > -        }
> > -
> > -        cur_addresses = &update->current_addresses;
> > -
> > -        if (!IN6_ARE_ADDR_EQUAL(&cur_addresses->ipv6_addrs[0].addr,
> > -                                &ipv6)) {
> > -            update->static_ipv6 = ipv6;
> > -            return STATIC;
> > -        }
> > -    } else if (update->mac != NONE) {
> > -        return DYNAMIC;
> > -    }
> > -
> > -    return NONE;
> > -}
> > -
> > -/* Check previously assigned dynamic addresses for validity. This will
> > - * check if the assigned addresses need to change.
> > - *
> > - * Returns true if any changes to dynamic addresses are required
> > - */
> > -static bool
> > -dynamic_addresses_check_for_updates(const char *lsp_addrs,
> > -                                    struct dynamic_address_update
> *update)
> > -{
> > -    update->mac = dynamic_mac_changed(lsp_addrs, update);
> > -    update->ipv4 = dynamic_ip4_changed(lsp_addrs, update);
> > -    update->ipv6 = dynamic_ip6_changed(lsp_addrs, update);
> > -    if (update->mac == NONE &&
> > -        update->ipv4 == NONE &&
> > -        update->ipv6 == NONE) {
> > -        return false;
> > -    } else {
> > -        return true;
> > -    }
> > -}
> > -
> > -/* For addresses that do not need to be updated, go ahead and insert
> them
> > - * into IPAM. This way, their addresses will be claimed and cannot be
> assigned
> > - * elsewhere later.
> > - */
> > -static void
> > -update_unchanged_dynamic_addresses(struct dynamic_address_update
> *update)
> > -{
> > -    if (update->mac == NONE) {
> > -        ipam_insert_mac(&update->current_addresses.ea, false);
> > -    }
> > -    if (update->ipv4 == NONE && update->current_addresses.n_ipv4_addrs)
> {
> > -        ipam_insert_ip_for_datapath(update->op->od,
> > -
>  ntohl(update->current_addresses.ipv4_addrs[0].addr),
> > -                       true);
> > -    }
> > -}
> > -
> > -static void
> > -set_lsp_dynamic_addresses(const char *dynamic_addresses, struct
> ovn_port *op)
> > -{
> > -    extract_lsp_addresses(dynamic_addresses,
> &op->lsp_addrs[op->n_lsp_addrs]);
> > -    op->n_lsp_addrs++;
> > -}
> > -
> > -/* Determines which components (MAC, IPv4, and IPv6) of dynamic
> > - * addresses need to be assigned. This is used exclusively for
> > - * ports that do not have dynamic addresses already assigned.
> > - */
> > -static void
> > -set_dynamic_updates(const char *addrspec,
> > -                    struct dynamic_address_update *update)
> > -{
> > -    bool has_ipv4 = false, has_ipv6 = false;
> > -    char ipv6_s[IPV6_SCAN_LEN + 1];
> > -    struct eth_addr mac;
> > -    ovs_be32 ip;
> > -    int n = 0;
> > -    if (ovs_scan(addrspec, ETH_ADDR_SCAN_FMT" dynamic%n",
> > -                 ETH_ADDR_SCAN_ARGS(mac), &n)
> > -        && addrspec[n] == '\0') {
> > -        update->mac = STATIC;
> > -        update->static_mac = mac;
> > -    } else {
> > -        update->mac = DYNAMIC;
> > -    }
> > -
> > -    if ((ovs_scan(addrspec, "dynamic "IP_SCAN_FMT"%n",
> > -                 IP_SCAN_ARGS(&ip), &n) && addrspec[n] == '\0')) {
> > -        has_ipv4 = true;
> > -    } else if ((ovs_scan(addrspec, "dynamic "IPV6_SCAN_FMT"%n",
> > -                         ipv6_s, &n) && addrspec[n] == '\0')) {
> > -        has_ipv6 = true;
> > -    } else if ((ovs_scan(addrspec, "dynamic "IP_SCAN_FMT"
> "IPV6_SCAN_FMT"%n",
> > -                         IP_SCAN_ARGS(&ip), ipv6_s, &n)
> > -               && addrspec[n] == '\0')) {
> > -        has_ipv4 = has_ipv6 = true;
> > -    }
> > -
> > -    if (has_ipv4) {
> > -        update->ipv4 = STATIC;
> > -        update->static_ip = ip;
> > -    } else if (update->op->od->ipam_info.allocated_ipv4s) {
> > -        update->ipv4 = DYNAMIC;
> > -    } else {
> > -        update->ipv4 = NONE;
> > -    }
> > -
> > -    if (has_ipv6 && ipv6_parse(ipv6_s, &update->static_ipv6)) {
> > -        update->ipv6 = STATIC;
> > -    } else if (update->op->od->ipam_info.ipv6_prefix_set) {
> > -        update->ipv6 = DYNAMIC;
> > -    } else {
> > -        update->ipv6 = NONE;
> > -    }
> > -}
> > -
> > -static void
> > -update_dynamic_addresses(struct dynamic_address_update *update)
> > -{
> > -    ovs_be32 ip4 = 0;
> > -    switch (update->ipv4) {
> > -    case NONE:
> > -        if (update->current_addresses.n_ipv4_addrs) {
> > -            ip4 = update->current_addresses.ipv4_addrs[0].addr;
> > -        }
> > -        break;
> > -    case REMOVE:
> > -        break;
> > -    case STATIC:
> > -        ip4 = update->static_ip;
> > -        break;
> > -    case DYNAMIC:
> > -        ip4 = htonl(ipam_get_unused_ip(&update->od->ipam_info));
> > -        VLOG_INFO("Assigned dynamic IPv4 address '"IP_FMT"' to port
> '%s'",
> > -                  IP_ARGS(ip4), update->op->nbsp->name);
> > -    }
> > -
> > -    struct eth_addr mac;
> > -    switch (update->mac) {
> > -    case NONE:
> > -        mac = update->current_addresses.ea;
> > -        break;
> > -    case REMOVE:
> > -        OVS_NOT_REACHED();
> > -    case STATIC:
> > -        mac = update->static_mac;
> > -        break;
> > -    case DYNAMIC:
> > -        eth_addr_from_uint64(ipam_get_unused_mac(ip4), &mac);
> > -        VLOG_INFO("Assigned dynamic MAC address '"ETH_ADDR_FMT"' to
> port '%s'",
> > -                  ETH_ADDR_ARGS(mac), update->op->nbsp->name);
> > -        break;
> > -    }
> > -
> > -    struct in6_addr ip6 = in6addr_any;
> > -    switch (update->ipv6) {
> > -    case NONE:
> > -        if (update->current_addresses.n_ipv6_addrs) {
> > -            ip6 = update->current_addresses.ipv6_addrs[0].addr;
> > -        }
> > -        break;
> > -    case REMOVE:
> > -        break;
> > -    case STATIC:
> > -        ip6 = update->static_ipv6;
> > -        break;
> > -    case DYNAMIC:
> > -        in6_generate_eui64(mac, &update->od->ipam_info.ipv6_prefix,
> &ip6);
> > -        struct ds ip6_ds = DS_EMPTY_INITIALIZER;
> > -        ipv6_format_addr(&ip6, &ip6_ds);
> > -        VLOG_INFO("Assigned dynamic IPv6 address '%s' to port '%s'",
> > -                  ip6_ds.string, update->op->nbsp->name);
> > -        ds_destroy(&ip6_ds);
> > -        break;
> > -    }
> > -
> > -    struct ds new_addr = DS_EMPTY_INITIALIZER;
> > -    ds_put_format(&new_addr, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
> > -    ipam_insert_mac(&mac, true);
> > -
> > -    if (ip4) {
> > -        ipam_insert_ip_for_datapath(update->od, ntohl(ip4), true);
> > -        ds_put_format(&new_addr, " "IP_FMT, IP_ARGS(ip4));
> > -    }
> > -    if (!IN6_ARE_ADDR_EQUAL(&ip6, &in6addr_any)) {
> > -        char ip6_s[INET6_ADDRSTRLEN + 1];
> > -        ipv6_string_mapped(ip6_s, &ip6);
> > -        ds_put_format(&new_addr, " %s", ip6_s);
> > -    }
> > -    nbrec_logical_switch_port_set_dynamic_addresses(update->op->nbsp,
> > -                                                    ds_cstr(&new_addr));
> > -    set_lsp_dynamic_addresses(ds_cstr(&new_addr), update->op);
> > -    ds_destroy(&new_addr);
> > -}
> >
> >   static void
> >   build_ipam(struct hmap *ls_datapaths, struct hmap *ls_ports)
> > @@ -1931,82 +1486,15 @@ build_ipam(struct hmap *ls_datapaths, struct
> hmap *ls_ports)
> >        * workloads, they need to be assigned and managed.  This function
> >        * does both IP address management (ipam) and MAC address
> management
> >        * (macam). */
> > +    struct ovs_list updates;
> >
> > +    ovs_list_init(&updates);
> >       /* If the switch's other_config:subnet is set, allocate new
> addresses for
> >        * ports that have the "dynamic" keyword in their addresses
> column. */
> >       struct ovn_datapath *od;
> > -    struct ovs_list updates;
> > -
> > -    ovs_list_init(&updates);
> >       HMAP_FOR_EACH (od, key_node, ls_datapaths) {
> > -        ovs_assert(od->nbs);
> > -
> > -        for (size_t i = 0; i < od->nbs->n_ports; i++) {
> > -            const struct nbrec_logical_switch_port *nbsp =
> od->nbs->ports[i];
> > -
> > -            if (!od->ipam_info.allocated_ipv4s &&
> > -                !od->ipam_info.ipv6_prefix_set &&
> > -                !od->ipam_info.mac_only) {
> > -                if (nbsp->dynamic_addresses) {
> > -
> nbrec_logical_switch_port_set_dynamic_addresses(nbsp,
> > -
> NULL);
> > -                }
> > -                continue;
> > -            }
> > -
> > -            struct ovn_port *op = ovn_port_find(ls_ports, nbsp->name);
> > -            if (!op || op->nbsp != nbsp || op->peer) {
> > -                /* Do not allocate addresses for logical switch ports
> that
> > -                 * have a peer. */
> > -                continue;
> > -            }
> > -
> > -            int num_dynamic_addresses = 0;
> > -            for (size_t j = 0; j < nbsp->n_addresses; j++) {
> > -                if (!is_dynamic_lsp_address(nbsp->addresses[j])) {
> > -                    continue;
> > -                }
> > -                if (num_dynamic_addresses) {
> > -                    static struct vlog_rate_limit rl
> > -                        = VLOG_RATE_LIMIT_INIT(1, 1);
> > -                    VLOG_WARN_RL(&rl, "More than one dynamic address "
> > -                                 "configured for logical switch port
> '%s'",
> > -                                 nbsp->name);
> > -                    continue;
> > -                }
> > -                num_dynamic_addresses++;
> > -                struct dynamic_address_update *update
> > -                    = xzalloc(sizeof *update);
> > -                update->op = op;
> > -                update->od = od;
> > -                if (nbsp->dynamic_addresses) {
> > -                    bool any_changed;
> > -                    extract_lsp_addresses(nbsp->dynamic_addresses,
> > -                                          &update->current_addresses);
> > -                    any_changed = dynamic_addresses_check_for_updates(
> > -                        nbsp->addresses[j], update);
> > -                    update_unchanged_dynamic_addresses(update);
> > -                    if (any_changed) {
> > -                        ovs_list_push_back(&updates, &update->node);
> > -                    } else {
> > -                        /* No changes to dynamic addresses */
> > -
> set_lsp_dynamic_addresses(nbsp->dynamic_addresses, op);
> > -
> destroy_lport_addresses(&update->current_addresses);
> > -                        free(update);
> > -                    }
> > -                } else {
> > -                    set_dynamic_updates(nbsp->addresses[j], update);
> > -                    ovs_list_push_back(&updates, &update->node);
> > -                }
> > -            }
> > -
> > -            if (!num_dynamic_addresses && nbsp->dynamic_addresses) {
> > -                nbrec_logical_switch_port_set_dynamic_addresses(nbsp,
> NULL);
> > -            }
> > -        }
> > -
> > +        update_ipam_ls(od, ls_ports, &updates, true);
> >       }
> > -
> >       /* After retaining all unchanged dynamic addresses, now assign
> >        * new ones.
> >        */
> > @@ -4650,6 +4138,7 @@ destroy_northd_data_tracked_changes(struct
> northd_data *nd)
> >       hmapx_clear(&trk_changes->trk_nat_lrs);
> >       hmapx_clear(&trk_changes->ls_with_changed_lbs);
> >       hmapx_clear(&trk_changes->ls_with_changed_acls);
> > +    hmapx_clear(&trk_changes->ls_with_changed_ipam);
> >       trk_changes->type = NORTHD_TRACKED_NONE;
> >   }
> >
> > @@ -4666,6 +4155,7 @@ init_northd_tracked_data(struct northd_data *nd)
> >       hmapx_init(&trk_data->trk_nat_lrs);
> >       hmapx_init(&trk_data->ls_with_changed_lbs);
> >       hmapx_init(&trk_data->ls_with_changed_acls);
> > +    hmapx_init(&trk_data->ls_with_changed_ipam);
> >   }
> >
> >   static void
> > @@ -4681,6 +4171,7 @@ destroy_northd_tracked_data(struct northd_data *nd)
> >       hmapx_destroy(&trk_data->trk_nat_lrs);
> >       hmapx_destroy(&trk_data->ls_with_changed_lbs);
> >       hmapx_destroy(&trk_data->ls_with_changed_acls);
> > +    hmapx_destroy(&trk_data->ls_with_changed_ipam);
> >   }
> >
> >   /* Check if a changed LSP can be handled incrementally within the I-P
> engine
> > @@ -4707,8 +4198,9 @@ lsp_can_be_inc_processed(const struct
> nbrec_logical_switch_port *nbsp)
> >       }
> >
> >       for (size_t j = 0; j < nbsp->n_addresses; j++) {
> > -        /* Dynamic address handling is not supported for now. */
> > -        if (is_dynamic_lsp_address(nbsp->addresses[j])) {
> > +        /* Dynamic address was assigned in the last iteration. */
> > +        if (is_dynamic_lsp_address(nbsp->addresses[j]) &&
> > +            nbsp->dynamic_addresses) {
> >               return false;
> >           }
> >           /* "unknown" address handling is not supported for now.  XXX:
> Need to
> > @@ -5131,6 +4623,13 @@ is_ls_acls_changed(const struct
> nbrec_logical_switch *nbs) {
> >               || is_acls_seqno_changed(nbs->acls, nbs->n_acls));
> >   }
> >
> > +static bool
> > +is_ls_ipam_changed(const struct nbrec_logical_switch *nbs) {
> > +    return
> > +    nbrec_logical_switch_is_updated(nbs,
> > +
> NBREC_LOGICAL_SWITCH_COL_OTHER_CONFIG);
> > +}
> > +
> >   /* Return true if changes are handled incrementally, false otherwise.
> >    * When there are any changes, try to track what's exactly changed and
> set
> >    * northd_data->trk_data accordingly.
> > @@ -5175,6 +4674,14 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> >
> >           if (is_ls_acls_changed(changed_ls)) {
> >               hmapx_add(&trk_data->ls_with_changed_acls, od);
> > +        } else {
>


This else would mean that the ACL and IPAM change cannot happen at
the same time, that doesn't seem right.


> > +            init_ipam_info_for_datapath(od);
> > +            bool ls_has_ipam = od->ipam_info.allocated_ipv4s ||
> > +                               od->ipam_info.ipv6_prefix_set ||
> > +                               od->ipam_info.mac_only;
> > +            if (ls_has_ipam || is_ls_ipam_changed(changed_ls)) {
> > +                hmapx_add(&trk_data->ls_with_changed_ipam, od);
> > +            }
> >           }
> >       }
> >
> > @@ -5184,6 +4691,9 @@ northd_handle_ls_changes(struct ovsdb_idl_txn
> *ovnsb_idl_txn,
> >           trk_data->type |= NORTHD_TRACKED_PORTS;
> >       }
> >
> > +    if (!hmapx_is_empty(&trk_data->ls_with_changed_ipam)) {
> > +        trk_data->type |= NORTHD_TRACKED_LS_IPAM;
> > +    }
>

This isn't needed, the hmapx is handled within the same node.


> >       if (!hmapx_is_empty(&trk_data->ls_with_changed_acls)) {
> >           trk_data->type |= NORTHD_TRACKED_LS_ACLS;
> >       }
> > @@ -5257,6 +4767,53 @@ fail:
> >       return false;
> >   }
> >
> > +bool northd_handle_ipam_changes(struct northd_data *nd)
> > +{
> > +    struct northd_tracked_data *nd_changes = &nd->trk_data;
> > +    struct ovs_list updates;
> > +    ovs_list_init(&updates);
> > +    struct hmapx_node *hmapx_node;
> > +    HMAPX_FOR_EACH (hmapx_node, &nd_changes->ls_with_changed_ipam) {
> > +        struct ovn_datapath *od = hmapx_node->data;
> > +        if (od->ipam_info_initilized) {
> > +            destroy_ipam_info(&od->ipam_info);
> > +            od->ipam_info_initilized = false;
> > +        }
> > +        init_ipam_info_for_datapath(od);
> > +        update_ipam_ls(od, &nd->ls_ports, &updates, false);
> > +    }
> > +    struct dynamic_address_update *update;
> > +    size_t lsps_changed = 0;
> > +    LIST_FOR_EACH_POP (update, node, &updates) {
> > +        bool find = false;
> > +        struct hmapx_node *hmapx_node_op;
> > +        HMAPX_FOR_EACH (hmapx_node_op, &nd_changes->trk_lsps.updated) {
> > +            struct ovn_port *op = hmapx_node_op->data;
> > +            if (!find && op == update->op) {
> > +                find = true;
> > +                break;
> > +            }
> > +        }
> > +        HMAPX_FOR_EACH (hmapx_node_op, &nd_changes->trk_lsps.created) {
> > +            struct ovn_port *op = hmapx_node_op->data;
> > +            if (!find && op == update->op) {
> > +                find = true;
> > +                break;
> > +            }
> > +        }
> > +        if (find) {
>
> +            update_dynamic_addresses(update);
> > +            lsps_changed++;
> > +        }
>

Everything below "LIST_FOR_EACH_POP()" can be simplified into the following:

if (hmapx_find(&nd_changes->trk_lsps.updated, update->op) ||
    hmapx_find(&nd_changes->trk_lsps.created, update->op)) {
    update_dynamic_addresses(update);
    lsps_changed++;
}


> > +        destroy_lport_addresses(&update->current_addresses);
> > +        free(update);
> > +    }
> > +
> > +    if (!lsps_changed) {
> > +        return false;
> > +    }
> > +    return true;
> > +}
> >   /* Returns true if the logical router has changes which can be
> >    * incrementally handled.
> >    * Presently supports i-p for the below changes:
> > diff --git a/northd/northd.h b/northd/northd.h
> > index 084634328..862df31d5 100644
> > --- a/northd/northd.h
> > +++ b/northd/northd.h
> > @@ -139,6 +139,7 @@ enum northd_tracked_data_type {
> >       NORTHD_TRACKED_LR_NATS  = (1 << 2),
> >       NORTHD_TRACKED_LS_LBS   = (1 << 3),
> >       NORTHD_TRACKED_LS_ACLS  = (1 << 4),
> > +    NORTHD_TRACKED_LS_IPAM  = (1 << 5),
> >   };
> >
> >   /* Track what's changed in the northd engine node.
> > @@ -161,6 +162,10 @@ struct northd_tracked_data {
> >       /* Tracked logical switches whose ACLs have changed.
> >        * hmapx node is 'struct ovn_datapath *'. */
> >       struct hmapx ls_with_changed_acls;
> > +
> > +    /* Tracked logical switches whose IPAM or LSPs have changed.
> > +     * hmapx node is 'struct ovn_datapath *'. */
> > +    struct hmapx ls_with_changed_ipam;
> >   };
> >
> >   struct northd_data {
> > @@ -386,6 +391,7 @@ struct ovn_datapath {
> >
> >       /* IPAM data. */
> >       struct ipam_info ipam_info;
> > +    bool ipam_info_initilized;
> >
> >       /* Multicast data. */
> >       struct mcast_info mcast_info;
> > @@ -838,6 +844,8 @@ bool northd_handle_lr_changes(const struct
> northd_input *,
> >                                 struct northd_data *);
> >   bool northd_handle_pgs_acl_changes(const struct northd_input *ni,
> >                                      struct northd_data *nd);
> > +bool northd_handle_ipam_changes(struct northd_data *nd);
> > +
> >   void destroy_northd_data_tracked_changes(struct northd_data *);
> >   void northd_destroy(struct northd_data *data);
> >   void northd_init(struct northd_data *data);
> > @@ -959,6 +967,12 @@ northd_has_ls_acls_in_tracked_data(struct
> northd_tracked_data *trk_nd_changes)
> >       return trk_nd_changes->type & NORTHD_TRACKED_LS_ACLS;
> >   }
> >
> > +static inline bool
> > +northd_has_ls_ipam_in_tracked_data(struct northd_tracked_data
> *trk_nd_changes)
> > +{
> > +    return trk_nd_changes->type & NORTHD_TRACKED_LS_IPAM;
> > +}
> > +
> >   /* Returns 'true' if the IPv4 'addr' is on the same subnet with one of
> the
> >    * IPs configured on the router port.
> >    */
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index 108de260e..264dfebbc 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -9218,7 +9218,7 @@ done
> >   # and addresses set by the user.
> >   check ovn-nbctl lsp-set-addresses p0 "0a:00:00:a8:01:17 192.168.1.2
> 192.168.1.12 192.168.1.14"
> >   check ovn-nbctl --wait=sb lsp-add sw0 p20 -- lsp-set-addresses p20
> dynamic
> > -check_dynamic_addresses p20 "0a:00:00:a8:01:18 192.168.1.13"
> > +check_dynamic_addresses p20 "0a:00:00:a8:01:03 192.168.1.13"
> >
> >   # Test for logical router port address management.
> >   check_uuid ovn-nbctl create Logical_Router name=R1
> > @@ -9227,12 +9227,12 @@ network="192.168.1.1/24"
> mac=\"0a:00:00:a8:01:19\" \
> >   -- add Logical_Router R1 ports @lrp -- lsp-add sw0 rp-sw0 \
> >   -- set Logical_Switch_Port rp-sw0 type=router options:router-port=sw0
> >   check ovn-nbctl --wait=sb lsp-add sw0 p21 -- lsp-set-addresses p21
> dynamic
> > -check_dynamic_addresses p21 "0a:00:00:a8:01:1a 192.168.1.15"
> > +check_dynamic_addresses p21 "0a:00:00:a8:01:18 192.168.1.15"
> >
> >   # Test for address reuse after logical port is deleted.
> >   check ovn-nbctl lsp-del p0
> >   check ovn-nbctl --wait=sb lsp-add sw0 p23 -- lsp-set-addresses p23
> dynamic
> > -check_dynamic_addresses p23 "0a:00:00:a8:01:03 192.168.1.2"
> > +check_dynamic_addresses p23 "0a:00:00:a8:01:1a 192.168.1.2"
> >
> >   # Test for multiple addresses to one logical port.
> >   check ovn-nbctl lsp-add sw0 p25 -- lsp-set-addresses p25 \
> > @@ -9292,19 +9292,19 @@ check_dynamic_addresses p33 "0a:00:00:a8:01:1f
> 192.168.1.22"
> >   check ovn-nbctl --wait=sb lsp-add sw0 p34 -- lsp-set-addresses p34 \
> >   "dynamic"
> >   # 192.168.1.51 should be assigned as 192.168.1.23-192.168.1.50 is
> excluded.
> > -check_dynamic_addresses p34 "0a:00:00:a8:01:34 192.168.1.51"
> > +check_dynamic_addresses p34 "0a:00:00:a8:01:20 192.168.1.51"
> >
> >   # Now clear the exclude_ips list. 192.168.1.19 should be assigned.
> >   check ovn-nbctl --wait=sb set Logical-switch sw0
> other_config:exclude_ips="invalid"
> >   check ovn-nbctl --wait=sb lsp-add sw0 p35 -- lsp-set-addresses p35
> "dynamic"
> > -check_dynamic_addresses p35 "0a:00:00:a8:01:20 192.168.1.19"
> > +check_dynamic_addresses p35 "0a:00:00:a8:01:21 192.168.1.19"
> >
> >   # Set invalid data in exclude_ips list. It should be ignored.
> >   check ovn-nbctl --wait=sb set Logical-switch sw0
> other_config:exclude_ips="182.168.1.30"
> >   check ovn-nbctl --wait=sb lsp-add sw0 p36 -- lsp-set-addresses p36 \
> >   "dynamic"
> >   # 192.168.1.21 should be assigned as that's the next free one.
> > -check_dynamic_addresses p36 "0a:00:00:a8:01:21 192.168.1.21"
> > +check_dynamic_addresses p36 "0a:00:00:a8:01:22 192.168.1.21"
> >
> >   # Clear the dynamic addresses assignment request.
> >   check ovn-nbctl --wait=sb clear logical_switch_port p36 addresses
> > @@ -9316,7 +9316,7 @@ check ovn-nbctl --wait=sb lsp-add sw0 p37 --
> lsp-set-addresses p37 "dynamic"
> >
> >   # With prefix aef0 and mac 0a:00:00:00:00:26, the dynamic IPv6 should
> be
> >   # - aef0::800:ff:fe00:26 (EUI64)
> > -check_dynamic_addresses p37 "0a:00:00:a8:01:21 192.168.1.21
> aef0::800:ff:fea8:121"
> > +check_dynamic_addresses p37 "0a:00:00:a8:01:22 192.168.1.21
> aef0::800:ff:fea8:122"
> >
> >   check ovn-nbctl --wait=sb ls-add sw4
> >   check ovn-nbctl --wait=sb set Logical-switch sw4
> other_config:ipv6_prefix="bef0::" \
> > @@ -9346,7 +9346,7 @@ check_dynamic_addresses p41 ''
> >
> >   # Set a subnet. Now p41 should have an ipv4 address, too
> >   check ovn-nbctl --wait=sb add Logical-Switch sw5 other_config subnet=
> 192.168.1.0/24
> > -check_dynamic_addresses p41 "0a:00:00:a8:01:22 192.168.1.2"
> > +check_dynamic_addresses p41 "0a:00:00:a8:01:23 192.168.1.2"
> >
> >   # Clear the other_config. The IPv4 address should be gone
> >   check ovn-nbctl --wait=sb clear Logical-Switch sw5 other_config
>
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Thanks,
Ales
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to