Hi, thank you Mark and Ales for your reviews.

Em ter., 22 de jul. de 2025 às 08:25, Ales Musil <amu...@redhat.com>
escreveu:

>
>
> 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.
>
>
I agree, I will adjust it for the next patches.


>
>
>> >   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.
>
>

It's a problem of circular dependency, ipam.c includes northd.h, northd.h
includes ipam.h,
ipam.c implements ipam.h but includes northd.h. I think it was more
appropriated change the
functions to util. I agree about ovn_port_find_bound, it could be kept in
northd.c



> > 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?
>

Actually, we can, I will adjust it.

>
>
>> >           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.
>
> I agree.


>
>
>> > +
>> >   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.
>
>
I agree.


> > +{
>> > +    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.
>
> I agree.


> > +        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.
>
>
I agree.



> > +            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.
>

You're right, I will adjust it.


>
>> > +            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++;
> }
>
>
Good, I will adjust it.


> > +        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
>


Thanks,
Lucas

-- 




_‘Esta mensagem é direcionada apenas para os endereços constantes no 
cabeçalho inicial. Se você não está listado nos endereços constantes no 
cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa 
mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão 
imediatamente anuladas e proibidas’._


* **‘Apesar do Magazine Luiza tomar 
todas as precauções razoáveis para assegurar que nenhum vírus esteja 
presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por 
quaisquer perdas ou danos causados por esse e-mail ou por seus anexos’.*



_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to