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