On 12/6/23 04:03, Numan Siddique wrote:
> On Thu, Nov 23, 2023 at 9:04 AM Dumitru Ceara <[email protected]> wrote:
>>
>> On 11/17/23 23:05, Numan Siddique wrote:
>>> On Wed, Nov 15, 2023 at 1:27 AM Han Zhou <[email protected]> wrote:
>>>>
>>>> On Thu, Oct 26, 2023 at 11:15 AM <[email protected]> wrote:
>>>>>
>>>>> From: Numan Siddique <[email protected]>
>>>>>
>>>>> This new engine now maintains the NAT related data for each
>>>>> logical router which was earlier maintained by the northd
>>>>> engine node in the 'struct ovn_datapath'.  Main inputs to
>>>>> this engine node are:
>>>>>    - northd
>>>>>    - NB logical router
>>>>>
>>>>> A record for each logical router is maintained in the 'lr_nats'
>>>>> hmap table and this record
>>>>>   - stores the ovn_nat's
>>>>
>>>> It seems the sentence is incomplete.
>>>>
>>>>>
>>>>> Handlers are also added to handle the changes to both these
>>>>> inputs.  This engine node becomes an input to 'lflow' node.
>>>>> This essentially decouples the lr NAT data from the northd
>>>>> engine node.
>>>>>
>>>>> Signed-off-by: Numan Siddique <[email protected]>
>>>>> ---
>>>>>  lib/ovn-util.c           |   6 +-
>>>>>  lib/ovn-util.h           |   2 +-
>>>>>  lib/stopwatch-names.h    |   1 +
>>>>>  northd/automake.mk       |   2 +
>>>>>  northd/en-lflow.c        |   5 +
>>>>>  northd/en-lr-nat.c       | 498 +++++++++++++++++++++++++++++++++++++
>>>>>  northd/en-lr-nat.h       | 134 ++++++++++
>>>>>  northd/en-sync-sb.c      |  11 +-
>>>>>  northd/inc-proc-northd.c |   9 +
>>>>>  northd/northd.c          | 514 ++++++++++++++-------------------------
>>>>>  northd/northd.h          |  32 ++-
>>>>>  tests/ovn-northd.at      |  18 ++
>>>>>  12 files changed, 877 insertions(+), 355 deletions(-)
>>>>>  create mode 100644 northd/en-lr-nat.c
>>>>>  create mode 100644 northd/en-lr-nat.h
>>
>> I would call these en-nat.{hc}, I think.  There's no NAT for switches.
> 
> 
> In v3 I haven't renamed it.  I can rename it in the next version once
> most of the review comments
> are addressed  (it's a bit of a pain to rename and fix all the compiler 
> errors).
> 
> But in this case I still prefer lr-nat (and the structures -
> lr_nat_record, ... ) because,
>      -  This engine node maintains the data for the NATs associated
> with a logical router.  A router
>          can have 0 or more NATs
>      - We have a NB NAT table which is for each NAT entry, whereas
> this engine node is for NATs associated with a router
>         and it could confuse and not convey the relationship between
> router and NATs.
>      -  This patch creates a 'lr_nat_record' entry for every logical
> router even if it doesn't have any NATs.
> 
> 

OK, this seems reasonable.

>>
>>>>>
>>>>> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
>>>>> index 33105202f2..05e635a6b4 100644
>>>>> --- a/lib/ovn-util.c
>>>>> +++ b/lib/ovn-util.c
>>>>> @@ -395,7 +395,7 @@ extract_sbrec_binding_first_mac(const struct
>>>> sbrec_port_binding *binding,
>>>>>  }
>>>>>
>>>>>  bool
>>>>> -lport_addresses_is_empty(struct lport_addresses *laddrs)
>>>>> +lport_addresses_is_empty(const struct lport_addresses *laddrs)
>>>>>  {
>>>>>      return !laddrs->n_ipv4_addrs && !laddrs->n_ipv6_addrs;
>>>>>  }
>>>>> @@ -405,6 +405,10 @@ destroy_lport_addresses(struct lport_addresses
>>>> *laddrs)
>>>>>  {
>>>>>      free(laddrs->ipv4_addrs);
>>>>>      free(laddrs->ipv6_addrs);
>>>>> +    laddrs->ipv4_addrs = NULL;
>>>>> +    laddrs->ipv6_addrs = NULL;
>>>>> +    laddrs->n_ipv4_addrs = 0;
>>>>> +    laddrs->n_ipv6_addrs = 0;
>>>>>  }
>>>>>
>>>>>  /* Returns a string of the IP address of 'laddrs' that overlaps with
>>>> 'ip_s'.
>>>>> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
>>>>> index bff50dbde9..5805415885 100644
>>>>> --- a/lib/ovn-util.h
>>>>> +++ b/lib/ovn-util.h
>>>>> @@ -112,7 +112,7 @@ bool extract_sbrec_binding_first_mac(const struct
>>>> sbrec_port_binding *binding,
>>>>>  bool extract_lrp_networks__(char *mac, char **networks, size_t
>>>> n_networks,
>>>>>                              struct lport_addresses *laddrs);
>>>>>
>>>>> -bool lport_addresses_is_empty(struct lport_addresses *);
>>>>> +bool lport_addresses_is_empty(const struct lport_addresses *);
>>>>>  void destroy_lport_addresses(struct lport_addresses *);
>>>>>  const char *find_lport_address(const struct lport_addresses *laddrs,
>>>>>                                 const char *ip_s);
>>>>> diff --git a/lib/stopwatch-names.h b/lib/stopwatch-names.h
>>>>> index 3452cc71cf..0a16da211e 100644
>>>>> --- a/lib/stopwatch-names.h
>>>>> +++ b/lib/stopwatch-names.h
>>>>> @@ -32,5 +32,6 @@
>>>>>  #define LFLOWS_TO_SB_STOPWATCH_NAME "lflows_to_sb"
>>>>>  #define PORT_GROUP_RUN_STOPWATCH_NAME "port_group_run"
>>>>>  #define SYNC_METERS_RUN_STOPWATCH_NAME "sync_meters_run"
>>>>> +#define LR_NAT_RUN_STOPWATCH_NAME "lr_nat_run"
>>>>>
>>>>>  #endif
>>>>> diff --git a/northd/automake.mk b/northd/automake.mk
>>>>> index cf622fc3c9..ae367a2a8b 100644
>>>>> --- a/northd/automake.mk
>>>>> +++ b/northd/automake.mk
>>>>> @@ -24,6 +24,8 @@ northd_ovn_northd_SOURCES = \
>>>>>         northd/en-sync-from-sb.h \
>>>>>         northd/en-lb-data.c \
>>>>>         northd/en-lb-data.h \
>>>>> +       northd/en-lr-nat.c \
>>>>> +       northd/en-lr-nat.h \
>>>>>         northd/inc-proc-northd.c \
>>>>>         northd/inc-proc-northd.h \
>>>>>         northd/ipam.c \
>>>>> diff --git a/northd/en-lflow.c b/northd/en-lflow.c
>>>>> index 96d03b7ada..22f398d419 100644
>>>>> --- a/northd/en-lflow.c
>>>>> +++ b/northd/en-lflow.c
>>>>> @@ -19,6 +19,7 @@
>>>>>  #include <stdio.h>
>>>>>
>>>>>  #include "en-lflow.h"
>>>>> +#include "en-lr-nat.h"
>>>>>  #include "en-northd.h"
>>>>>  #include "en-meters.h"
>>>>>
>>>>> @@ -40,6 +41,9 @@ lflow_get_input_data(struct engine_node *node,
>>>>>          engine_get_input_data("port_group", node);
>>>>>      struct sync_meters_data *sync_meters_data =
>>>>>          engine_get_input_data("sync_meters", node);
>>>>> +    struct ed_type_lr_nat_data *lr_nat_data =
>>>>> +        engine_get_input_data("lr_nat", node);
>>>>> +
>>>>>      lflow_input->nbrec_bfd_table =
>>>>>          EN_OVSDB_GET(engine_get_input("NB_bfd", node));
>>>>>      lflow_input->sbrec_bfd_table =
>>>>> @@ -61,6 +65,7 @@ lflow_get_input_data(struct engine_node *node,
>>>>>      lflow_input->ls_ports = &northd_data->ls_ports;
>>>>>      lflow_input->lr_ports = &northd_data->lr_ports;
>>>>>      lflow_input->ls_port_groups = &pg_data->ls_port_groups;
>>>>> +    lflow_input->lr_nats = &lr_nat_data->lr_nats;
>>>>>      lflow_input->meter_groups = &sync_meters_data->meter_groups;
>>>>>      lflow_input->lb_datapaths_map = &northd_data->lb_datapaths_map;
>>>>>      lflow_input->svc_monitor_map = &northd_data->svc_monitor_map;
>>>>> diff --git a/northd/en-lr-nat.c b/northd/en-lr-nat.c
>>>>> new file mode 100644
>>>>> index 0000000000..0d332d300b
>>>>> --- /dev/null
>>>>> +++ b/northd/en-lr-nat.c
>>>>> @@ -0,0 +1,498 @@
>>>>> +/*
>>>>> + * Copyright (c) 2023, Red Hat, Inc.
>>>>> + *
>>>>> + * Licensed under the Apache License, Version 2.0 (the "License");
>>>>> + * you may not use this file except in compliance with the License.
>>>>> + * You may obtain a copy of the License at:
>>>>> + *
>>>>> + *     http://www.apache.org/licenses/LICENSE-2.0
>>>>> + *
>>>>> + * Unless required by applicable law or agreed to in writing, software
>>>>> + * distributed under the License is distributed on an "AS IS" BASIS,
>>>>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
>>>> implied.
>>>>> + * See the License for the specific language governing permissions and
>>>>> + * limitations under the License.
>>>>> + */
>>>>> +
>>>>> +#include <config.h>
>>>>> +
>>>>> +#include <getopt.h>
>>>>> +#include <stdlib.h>
>>>>> +#include <stdio.h>
>>>>> +
>>>>> +/* OVS includes */
>>>>> +#include "include/openvswitch/hmap.h"
>>>>> +#include "openvswitch/util.h"
>>>>> +#include "openvswitch/vlog.h"
>>>>> +#include "stopwatch.h"
>>>>> +
>>>>> +/* OVN includes */
>>>>> +#include "en-lr-nat.h"
>>>>> +#include "lib/inc-proc-eng.h"
>>>>> +#include "lib/lb.h"
>>>>> +#include "lib/ovn-nb-idl.h"
>>>>> +#include "lib/ovn-sb-idl.h"
>>>>> +#include "lib/ovn-util.h"
>>>>> +#include "lib/stopwatch-names.h"
>>>>> +#include "northd.h"
>>>>> +
>>>>> +VLOG_DEFINE_THIS_MODULE(en_lr_nat);
>>
>> Globally, throughout the whole patch, I think we should replace
>> "lr_nat_*" with "nat_*".  There's no NAT on switches.
>>
>>>>> +
>>>>> +/* Static function declarations. */
>>>>> +static void lr_nat_table_init(struct lr_nat_table *);
>>>>> +static void lr_nat_table_clear(struct lr_nat_table *);
>>>>> +static void lr_nat_table_destroy(struct lr_nat_table *);
>>>>> +static void lr_nat_table_build(struct lr_nat_table *,
>>>>> +                               const struct ovn_datapaths *lr_datapaths);
>>>>> +static struct lr_nat_record *lr_nat_table_find_(const struct
>>>> lr_nat_table *,
>>>>> +                                         const struct
>>>> nbrec_logical_router *);
>>>>> +static struct lr_nat_record *lr_nat_table_find_by_index_(
>>>>> +    const struct lr_nat_table *, size_t od_index);
>>>>> +
>>>>> +static struct lr_nat_record *lr_nat_record_create(
>>>>> +    struct lr_nat_table *, const struct ovn_datapath *);
>>>>> +static void lr_nat_record_init(struct lr_nat_record *);
>>>>> +static void lr_nat_record_reinit(struct lr_nat_record *);
>>>>> +static void lr_nat_record_destroy(struct lr_nat_record *);
>>>>> +
>>>>> +static void lr_nat_entries_init(struct lr_nat_record *);
>>>>> +static void lr_nat_entries_destroy(struct lr_nat_record *);
>>>>> +static void lr_nat_external_ips_init(struct lr_nat_record *);
>>>>> +static void lr_nat_external_ips_destroy(struct lr_nat_record *);
>>>>> +static bool get_force_snat_ip(struct lr_nat_record *, const char
>>>> *key_type,
>>>>> +                              struct lport_addresses *);
>>>>> +static struct lr_nat_input lr_nat_get_input_data(struct engine_node *);
>>>>> +static bool is_lr_nats_changed(const struct nbrec_logical_router *);
>>>>> +static bool is_lr_nats_seqno_changed(const struct nbrec_logical_router
>>>> *nbr);
>>>>> +
>>>>> +
>>>>> +const struct lr_nat_record *
>>>>> +lr_nat_table_find_by_index(const struct lr_nat_table *table,
>>>>> +                           size_t od_index)
>>>>> +{
>>>>> +    return lr_nat_table_find_by_index_(table, od_index);
>>>>> +}
>>>>> +
>>>>> +/* 'lr_nat' engine node manages the NB logical router NAT data.
>>>>> + */
>>>>> +void *
>>>>> +en_lr_nat_init(struct engine_node *node OVS_UNUSED,
>>>>> +               struct engine_arg *arg OVS_UNUSED)
>>>>> +{
>>>>> +    struct ed_type_lr_nat_data *data = xzalloc(sizeof *data);
>>>>> +    lr_nat_table_init(&data->lr_nats);
>>>>> +    hmapx_init(&data->tracked_data.crupdated);
>>>>> +    hmapx_init(&data->tracked_data.deleted);
>>>>> +    return data;
>>>>> +}
>>>>> +
>>>>> +void
>>>>> +en_lr_nat_cleanup(void *data_)
>>>>> +{
>>>>> +    struct ed_type_lr_nat_data *data = (struct ed_type_lr_nat_data *)
>>>> data_;
>>>>> +    lr_nat_table_destroy(&data->lr_nats);
>>>>> +    hmapx_destroy(&data->tracked_data.crupdated);
>>>>> +    hmapx_destroy(&data->tracked_data.deleted);
>>>>> +}
>>>>> +
>>>>> +void
>>>>> +en_lr_nat_clear_tracked_data(void *data_)
>>>>> +{
>>>>> +    struct ed_type_lr_nat_data *data = (struct ed_type_lr_nat_data *)
>>>> data_;
>>>>> +
>>>>> +    struct hmapx_node *hmapx_node;
>>>>> +    HMAPX_FOR_EACH_SAFE (hmapx_node, &data->tracked_data.deleted) {
>>>>> +        lr_nat_record_destroy(hmapx_node->data);
>>>>> +        hmapx_delete(&data->tracked_data.deleted, hmapx_node);
>>>>> +    }
>>>>> +
>>>>> +    hmapx_clear(&data->tracked_data.crupdated);
>>>>> +    data->tracked = false;
>>>>> +}
>>>>> +
>>>>> +void
>>>>> +en_lr_nat_run(struct engine_node *node, void *data_)
>>>>> +{
>>>>> +    struct lr_nat_input input_data = lr_nat_get_input_data(node);
>>>>> +    struct ed_type_lr_nat_data *data = data_;
>>>>> +
>>>>> +    stopwatch_start(LR_NAT_RUN_STOPWATCH_NAME, time_msec());
>>>>> +    data->tracked = false;
>>>>> +    lr_nat_table_clear(&data->lr_nats);
>>>>> +    lr_nat_table_build(&data->lr_nats, input_data.lr_datapaths);
>>>>> +
>>>>> +    stopwatch_stop(LR_NAT_RUN_STOPWATCH_NAME, time_msec());
>>>>> +    engine_set_node_state(node, EN_UPDATED);
>>>>> +}
>>>>> +
>>>>> +/* Handler functions. */
>>>>> +bool
>>>>> +lr_nat_northd_handler(struct engine_node *node, void *data OVS_UNUSED)
>>>>> +{
>>>>> +    struct northd_data *northd_data = engine_get_input_data("northd",
>>>> node);
>>>>> +    if (!northd_data->change_tracked) {
>>>>> +        return false;
>>>>> +    }
>>>>> +
>>>>
>>>> I wonder how can we return true here without checking the actual changes in
>>>> north? We should check if there is any change in northd that would affect
>>>> the data of this node, right?
>>>>
>>>
>>> This (lr_nat) node is interested only in the logical router changes
>>> related to NAT. And any changes related to NAT are handled below
>>> in the lr_nat_logical_router_handler() function. So we don't have to
>>> do anything for northd node changes.   Actually we don't need to have
>>> northd node as input.  I only added it so that lr_nat_record can store
>>> 'struct ovn_datapath *'.
>>
>> We're making quite an assumption here, we're essentially saying: "if the
>> northd_data was 'change tracked' then all 'ovn_datapath *' we stored in
>> lr_nat_records are still valid".  That seems very fragile to me.  What
>> happens when in the future router deletion is handled incrementally in
>> northd?
> 
> 
> This is very similar to having 'struct nbrec_logical_switch' or
> 'struct nbrec_logical_router'
> referenced in the 'struct ovn_datapath'.   In the future if we add I-P
> for logical switch/router
> deletion we will include that in the tracking data and the 'en_lr_nat'
> engine node will
> delete the entry in its lr_nat table.
> 
> I don't think its fragile to me.
> 
> However,  in v3 I changed the inputs to lr_nat engine node.  It's only
> input is now 'northd' engine node
> and the handler 'lr_nat_northd_handler' handles the changes from the
> northd_tracked_data ->lr_with_changed_nats.
> 
> Request to take a look at v3.
> 
> 

OK, will do.

> 
>>
>>> This becomes easier for lflow engine node to easily access
>>> lr_nat_record from lr_nats array using
>>> the od->index.
>>> If we don't want this,  then we need to do a lookup for the lr_nat
>>> record in the lr_nats hmap.
>>> I wanted to avoid this lookup.
>>>
>>
>> Do we know if this lookup is expensive on large scale realistic topologies?
> 
> I haven't measured it,  but it can be expensive if there are many hash
> collisions in the hmap.
> 
> 

I agree.

>>
>>> Thanks
>>> Numan
>>>
>>>> Thanks,
>>>> Han
>>>>
>>>>> +    return true;
>>>>> +}
>>>>> +
>>>>> +bool
>>>>> +lr_nat_logical_router_handler(struct engine_node *node, void *data_)
>>>>> +{
>>>>> +    struct lr_nat_input input_data = lr_nat_get_input_data(node);
>>>>> +    struct ed_type_lr_nat_data *data = data_;
>>>>> +    const struct nbrec_logical_router *nbr;
>>>>> +
>>>>> +    NBREC_LOGICAL_ROUTER_TABLE_FOR_EACH_TRACKED (
>>>>> +            nbr, input_data.nbrec_logical_router_table) {
>>>>> +        if (!is_lr_nats_changed(nbr)) {
>>>>> +            continue;
>>>>> +        }
>>>>> +
>>>>> +        struct lr_nat_record *lrnat_rec =
>>>> lr_nat_table_find_(&data->lr_nats,
>>>>> +                                                             nbr);
>>>>> +
>>>>> +        if (nbrec_logical_router_is_deleted(nbr)) {
>>>>> +            if (lrnat_rec) {
>>>>> +                /* Remove the record from the entries. */
>>>>> +                hmap_remove(&data->lr_nats.entries,
>>>> &lrnat_rec->key_node);
>>>>> +
>>>>> +                /* Add the lrnet rec to the tracking data. */
>>>>> +                hmapx_add(&data->tracked_data.deleted, lrnat_rec);
>>>>> +            }
>>>>> +        } else {
>>>>> +            if (!lrnat_rec) {
>>>>> +                const struct ovn_datapath *od;
>>>>> +                od =
>>>> ovn_datapath_find(&input_data.lr_datapaths->datapaths,
>>>>> +                                       &nbr->header_.uuid);
>>>>> +                ovs_assert(od);
>>>>> +                lrnat_rec = lr_nat_record_create(&data->lr_nats, od);
>>>>> +            } else {
>>>>> +                lr_nat_record_reinit(lrnat_rec);
>>>>> +            }
>>>>> +
>>>>> +            /* Add the lrnet rec to the tracking data. */
>>>>> +            hmapx_add(&data->tracked_data.crupdated, lrnat_rec);
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    if (!hmapx_is_empty(&data->tracked_data.deleted)
>>>>> +            || !hmapx_is_empty(&data->tracked_data.crupdated)) {
>>>>> +        data->tracked = true;
>>>>> +        engine_set_node_state(node, EN_UPDATED);
>>>>> +    }
>>>>> +    return true;
>>>>> +}
>>>>> +
>>>>> +/* static functions. */
>>>>> +static void
>>>>> +lr_nat_table_init(struct lr_nat_table *table)
>>>>> +{
>>>>> +    *table = (struct lr_nat_table) {
>>>>> +        .entries = HMAP_INITIALIZER(&table->entries),
>>>>> +    };
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_table_clear(struct lr_nat_table *table)
>>>>> +{
>>>>> +    struct lr_nat_record *lrnat_rec;
>>>>> +    HMAP_FOR_EACH_POP (lrnat_rec, key_node, &table->entries) {
>>>>> +        lr_nat_record_destroy(lrnat_rec);
>>>>> +    }
>>>>> +
>>>>> +    free(table->array);
>>>>> +    table->array = NULL;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_table_build(struct lr_nat_table *table,
>>>>> +                   const struct ovn_datapaths *lr_datapaths)
>>>>> +{
>>>>> +    table->array = xrealloc(table->array,
>>>>> +                            ods_size(lr_datapaths) * sizeof
>>>> *table->array);
>>>>> +
>>>>> +    const struct ovn_datapath *od;
>>>>> +    HMAP_FOR_EACH (od, key_node, &lr_datapaths->datapaths) {
>>>>> +        lr_nat_record_create(table, od);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_table_destroy(struct lr_nat_table *table)
>>>>> +{
>>>>> +    lr_nat_table_clear(table);
>>>>> +    hmap_destroy(&table->entries);
>>>>> +}
>>>>> +
>>>>> +struct lr_nat_record *
>>>>> +lr_nat_table_find_(const struct lr_nat_table *table,
>>>>> +                  const struct nbrec_logical_router *nbr)
>>>>> +{
>>>>> +    struct lr_nat_record *lrnat_rec;
>>>>> +
>>>>> +    HMAP_FOR_EACH_WITH_HASH (lrnat_rec, key_node,
>>>>> +                             uuid_hash(&nbr->header_.uuid),
>>>> &table->entries) {
>>>>> +        if (nbr == lrnat_rec->od->nbr) {
>>>>> +            return lrnat_rec;
>>>>> +        }
>>>>> +    }
>>>>> +    return NULL;
>>>>> +}
>>>>> +
>>>>> +
>>>>> +struct lr_nat_record *
>>>>> +lr_nat_table_find_by_index_(const struct lr_nat_table *table,
>>>>> +                            size_t od_index)
>>>>> +{
>>>>> +    ovs_assert(od_index <= hmap_count(&table->entries));
>>>>> +
>>>>> +    return table->array[od_index];
>>>>> +}
>>>>> +
>>>>> +static struct lr_nat_record *
>>>>> +lr_nat_record_create(struct lr_nat_table *table,
>>>>> +                     const struct ovn_datapath *od)
>>>>> +{
>>>>> +    ovs_assert(od->nbr);
>>>>> +
>>>>> +    struct lr_nat_record *lrnat_rec = xzalloc(sizeof *lrnat_rec);
>>>>> +    lrnat_rec->od = od;
>>>>> +    lr_nat_record_init(lrnat_rec);
>>>>> +
>>>>> +    hmap_insert(&table->entries, &lrnat_rec->key_node,
>>>>> +                uuid_hash(&od->nbr->header_.uuid));
>>>>> +    table->array[od->index] = lrnat_rec;
>>>>> +    return lrnat_rec;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_record_init(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    lr_nat_entries_init(lrnat_rec);
>>>>> +    lr_nat_external_ips_init(lrnat_rec);
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_record_reinit(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    lr_nat_entries_destroy(lrnat_rec);
>>>>> +    lr_nat_external_ips_destroy(lrnat_rec);
>>>>> +    lr_nat_record_init(lrnat_rec);
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_record_destroy(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    lr_nat_entries_destroy(lrnat_rec);
>>>>> +    lr_nat_external_ips_destroy(lrnat_rec);
>>>>> +    free(lrnat_rec);
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_external_ips_init(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    sset_init(&lrnat_rec->external_ips);
>>>>> +    for (size_t i = 0; i < lrnat_rec->od->nbr->n_nat; i++) {
>>>>> +        sset_add(&lrnat_rec->external_ips,
>>>>> +                 lrnat_rec->od->nbr->nat[i]->external_ip);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_external_ips_destroy(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    sset_destroy(&lrnat_rec->external_ips);
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +snat_ip_add(struct lr_nat_record *lrnat_rec, const char *ip,
>>>>> +            struct ovn_nat *nat_entry)
>>>>> +{
>>>>> +    struct ovn_snat_ip *snat_ip = shash_find_data(&lrnat_rec->snat_ips,
>>>> ip);
>>>>> +
>>>>> +    if (!snat_ip) {
>>>>> +        snat_ip = xzalloc(sizeof *snat_ip);
>>>>> +        ovs_list_init(&snat_ip->snat_entries);
>>>>> +        shash_add(&lrnat_rec->snat_ips, ip, snat_ip);
>>>>> +    }
>>>>> +
>>>>> +    if (nat_entry) {
>>>>> +        ovs_list_push_back(&snat_ip->snat_entries,
>>>>> +                           &nat_entry->ext_addr_list_node);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_entries_init(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    shash_init(&lrnat_rec->snat_ips);
>>>>> +    sset_init(&lrnat_rec->external_macs);
>>>>> +    lrnat_rec->has_distributed_nat = false;
>>>>> +
>>>>> +    if (get_force_snat_ip(lrnat_rec, "dnat",
>>>>> +                          &lrnat_rec->dnat_force_snat_addrs)) {
>>>>> +        if (lrnat_rec->dnat_force_snat_addrs.n_ipv4_addrs) {
>>>>> +            snat_ip_add(lrnat_rec,
>>>>> +
>>>>  lrnat_rec->dnat_force_snat_addrs.ipv4_addrs[0].addr_s,
>>>>> +                        NULL);
>>>>> +        }
>>>>> +        if (lrnat_rec->dnat_force_snat_addrs.n_ipv6_addrs) {
>>>>> +            snat_ip_add(lrnat_rec,
>>>>> +
>>>>  lrnat_rec->dnat_force_snat_addrs.ipv6_addrs[0].addr_s,
>>>>> +                        NULL);
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    /* Check if 'lb_force_snat_ip' is configured with 'router_ip'. */
>>>>> +    const char *lb_force_snat =
>>>>> +        smap_get(&lrnat_rec->od->nbr->options, "lb_force_snat_ip");
>>>>> +    if (lb_force_snat && !strcmp(lb_force_snat, "router_ip")
>>>>> +            && smap_get(&lrnat_rec->od->nbr->options, "chassis")) {
>>>>> +
>>>>> +        /* Set it to true only if its gateway router and
>>>>> +         * options:lb_force_snat_ip=router_ip. */
>>>>> +        lrnat_rec->lb_force_snat_router_ip = true;
>>>>> +    } else {
>>>>> +        lrnat_rec->lb_force_snat_router_ip = false;
>>>>> +
>>>>> +        /* Check if 'lb_force_snat_ip' is configured with a set of
>>>>> +         * IP address(es). */
>>>>> +        if (get_force_snat_ip(lrnat_rec, "lb",
>>>>> +                              &lrnat_rec->lb_force_snat_addrs)) {
>>>>> +            if (lrnat_rec->lb_force_snat_addrs.n_ipv4_addrs) {
>>>>> +                snat_ip_add(lrnat_rec,
>>>>> +
>>>>  lrnat_rec->lb_force_snat_addrs.ipv4_addrs[0].addr_s,
>>>>> +                        NULL);
>>>>> +            }
>>>>> +            if (lrnat_rec->lb_force_snat_addrs.n_ipv6_addrs) {
>>>>> +                snat_ip_add(lrnat_rec,
>>>>> +
>>>>  lrnat_rec->lb_force_snat_addrs.ipv6_addrs[0].addr_s,
>>>>> +                        NULL);
>>>>> +            }
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    if (!lrnat_rec->od->nbr->n_nat) {
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    lrnat_rec->nat_entries =
>>>>> +        xmalloc(lrnat_rec->od->nbr->n_nat * sizeof
>>>> *lrnat_rec->nat_entries);
>>>>> +
>>>>> +    for (size_t i = 0; i < lrnat_rec->od->nbr->n_nat; i++) {
>>>>> +        const struct nbrec_nat *nat = lrnat_rec->od->nbr->nat[i];
>>>>> +        struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>>>>> +
>>>>> +        nat_entry->nb = nat;
>>>>> +        if (!extract_ip_addresses(nat->external_ip,
>>>>> +                                  &nat_entry->ext_addrs) ||
>>>>> +                !nat_entry_is_valid(nat_entry)) {
>>>>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
>>>> 1);
>>>>> +
>>>>> +            VLOG_WARN_RL(&rl,
>>>>> +                         "Bad ip address %s in nat configuration "
>>>>> +                         "for router %s", nat->external_ip,
>>>>> +                         lrnat_rec->od->nbr->name);
>>>>> +            continue;
>>>>> +        }
>>>>> +
>>>>> +        /* If this is a SNAT rule add the IP to the set of unique SNAT
>>>> IPs. */
>>>>> +        if (!strcmp(nat->type, "snat")) {
>>>>> +            if (!nat_entry_is_v6(nat_entry)) {
>>>>> +                snat_ip_add(lrnat_rec,
>>>>> +                            nat_entry->ext_addrs.ipv4_addrs[0].addr_s,
>>>>> +                            nat_entry);
>>>>> +            } else {
>>>>> +                snat_ip_add(lrnat_rec,
>>>>> +                            nat_entry->ext_addrs.ipv6_addrs[0].addr_s,
>>>>> +                            nat_entry);
>>>>> +            }
>>>>> +        } else {
>>>>> +            if (!strcmp(nat->type, "dnat_and_snat")
>>>>> +                    && nat->logical_port && nat->external_mac) {
>>>>> +                lrnat_rec->has_distributed_nat = true;
>>>>> +            }
>>>>> +
>>>>> +            if (nat->external_mac) {
>>>>> +                sset_add(&lrnat_rec->external_macs, nat->external_mac);
>>>>> +            }
>>>>> +        }
>>>>> +    }
>>>>> +    lrnat_rec->n_nat_entries = lrnat_rec->od->nbr->n_nat;
>>>>> +}
>>>>> +
>>>>> +static bool
>>>>> +get_force_snat_ip(struct lr_nat_record *lrnat_rec, const char *key_type,
>>>>> +                  struct lport_addresses *laddrs)
>>>>> +{
>>>>> +    char *key = xasprintf("%s_force_snat_ip", key_type);
>>>>> +    const char *addresses = smap_get(&lrnat_rec->od->nbr->options, key);
>>>>> +    free(key);
>>>>> +
>>>>> +    if (!addresses) {
>>>>> +        return false;
>>>>> +    }
>>>>> +
>>>>> +    if (!extract_ip_address(addresses, laddrs)) {
>>>>> +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>>>>> +        VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
>>>>> +                     addresses,
>>>> UUID_ARGS(&lrnat_rec->od->nbr->header_.uuid));
>>>>> +        return false;
>>>>> +    }
>>>>> +
>>>>> +    return true;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lr_nat_entries_destroy(struct lr_nat_record *lrnat_rec)
>>>>> +{
>>>>> +    shash_destroy_free_data(&lrnat_rec->snat_ips);
>>>>> +    destroy_lport_addresses(&lrnat_rec->dnat_force_snat_addrs);
>>>>> +    destroy_lport_addresses(&lrnat_rec->lb_force_snat_addrs);
>>>>> +
>>>>> +    for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>>>>> +        destroy_lport_addresses(&lrnat_rec->nat_entries[i].ext_addrs);
>>>>> +    }
>>>>> +
>>>>> +    free(lrnat_rec->nat_entries);
>>>>> +    lrnat_rec->nat_entries = NULL;
>>>>> +    lrnat_rec->n_nat_entries = 0;
>>>>> +    sset_destroy(&lrnat_rec->external_macs);
>>>>> +}
>>>>> +
>>>>> +static struct lr_nat_input
>>>>> +lr_nat_get_input_data(struct engine_node *node)
>>>>> +{
>>>>> +    struct northd_data *northd_data = engine_get_input_data("northd",
>>>> node);
>>>>> +    return (struct lr_nat_input) {
>>>>> +        .nbrec_logical_router_table =
>>>>> +            EN_OVSDB_GET(engine_get_input("NB_logical_router", node)),
>>>>> +        .lr_datapaths = &northd_data->lr_datapaths,
>>>>> +    };
>>>>> +}
>>>>> +
>>>>> +static bool
>>>>> +is_lr_nats_seqno_changed(const struct nbrec_logical_router *nbr)
>>>>> +{
>>>>> +    for (size_t i = 0; i < nbr->n_nat; i++) {
>>>>> +        if (nbrec_nat_row_get_seqno(nbr->nat[i],
>>>>> +                                    OVSDB_IDL_CHANGE_MODIFY) > 0) {
>>>>> +            return true;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    return false;
>>>>> +}
>>>>> +
>>>>> +static bool
>>>>> +is_lr_nats_changed(const struct nbrec_logical_router *nbr) {
>>>>> +    return (nbrec_logical_router_is_new(nbr)
>>>>> +            || nbrec_logical_router_is_deleted(nbr)
>>>>> +            || nbrec_logical_router_is_updated(nbr,
>>>>> +
>>>> NBREC_LOGICAL_ROUTER_COL_NAT)
>>>>> +            || nbrec_logical_router_is_updated(
>>>>> +                nbr, NBREC_LOGICAL_ROUTER_COL_OPTIONS)
>>>>> +            || is_lr_nats_seqno_changed(nbr));
>>>>> +}
>>>>> diff --git a/northd/en-lr-nat.h b/northd/en-lr-nat.h
>>>>> new file mode 100644
>>>>> index 0000000000..01a16a21aa
>>>>> --- /dev/null
>>>>> +++ b/northd/en-lr-nat.h
>>>>> @@ -0,0 +1,134 @@
>>>>> +/*
>>>>> + * Copyright (c) 2023, Red Hat, Inc.
>>>>> + *
>>>>> + * Licensed under the Apache License, Version 2.0 (the "License");
>>>>> + * you may not use this file except in compliance with the License.
>>>>> + * You may obtain a copy of the License at:
>>>>> + *
>>>>> + *     http://www.apache.org/licenses/LICENSE-2.0
>>>>> + *
>>>>> + * Unless required by applicable law or agreed to in writing, software
>>>>> + * distributed under the License is distributed on an "AS IS" BASIS,
>>>>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
>>>> implied.
>>>>> + * See the License for the specific language governing permissions and
>>>>> + * limitations under the License.
>>>>> + */
>>>>> +#ifndef EN_LR_NAT_H
>>>>> +#define EN_LR_NAT_H 1
>>>>> +
>>>>> +#include <stdint.h>
>>>>> +
>>>>> +/* OVS includes. */
>>>>> +#include "lib/hmapx.h"
>>>>> +#include "openvswitch/hmap.h"
>>>>> +#include "sset.h"
>>>>> +
>>>>> +/* OVN includes. */
>>>>> +#include "lib/inc-proc-eng.h"
>>>>> +#include "lib/ovn-nb-idl.h"
>>>>> +#include "lib/ovn-sb-idl.h"
>>>>> +#include "lib/ovn-util.h"
>>>>> +
>>>>> +/* Contains a NAT entry with the external addresses pre-parsed. */
>>>>> +struct ovn_nat {
>>>>> +    const struct nbrec_nat *nb;
>>>>> +    struct lport_addresses ext_addrs;
>>>>> +    struct ovs_list ext_addr_list_node; /* Linkage in the per-external IP
>>>>> +                                         * list of nat entries. Currently
>>>>> +                                         * only used for SNAT.
>>>>> +                                         */
>>>>> +};
>>>>> +
>>>>> +/* Stores the list of SNAT entries referencing a unique SNAT IP address.
>>>>> + * The 'snat_entries' list will be empty if the SNAT IP is used only for
>>>>> + * dnat_force_snat_ip or lb_force_snat_ip.
>>>>> + */
>>>>> +struct ovn_snat_ip {
>>>>> +    struct ovs_list snat_entries;
>>>>> +};
>>>>> +
>>>>> +struct lr_nat_record {
>>>>> +    struct hmap_node key_node;  /* Index on 'nbr->header_.uuid'. */
>>>>> +
>>>>> +    const struct ovn_datapath *od;
>>>>> +
>>>>> +    struct ovn_nat *nat_entries;
>>>>> +    size_t n_nat_entries;
>>>>> +
>>>>> +    bool has_distributed_nat;
>>>>> +
>>>>> +    /* Set of nat external ips on the router. */
>>>>> +    struct sset external_ips;
>>>>> +
>>>>> +    /* Set of nat external macs on the router. */
>>>>> +    struct sset external_macs;
>>>>> +
>>>>> +    /* SNAT IPs owned by the router (shash of 'struct ovn_snat_ip'). */
>>>>> +    struct shash snat_ips;
>>>>> +
>>>>> +    struct lport_addresses dnat_force_snat_addrs;
>>>>> +    struct lport_addresses lb_force_snat_addrs;
>>>>> +    bool lb_force_snat_router_ip;
>>>>> +};
>>>>> +
>>>>> +struct lr_nat_tracked_data {
>>>>> +    /* Created or updated logical router with NAT data. */
>>>>> +    struct hmapx crupdated;
>>>>> +
>>>>> +    /* Deleted logical router with NAT data. */
>>>>> +    struct hmapx deleted; /* Stores 'struct lr_nat_record'. */
>>>>> +};
>>>>> +
>>>>> +struct lr_nat_table {
>>>>> +    struct hmap entries; /* Stores struct lr_nat_record. */
>>>>> +
>>>>> +    /* The array index of each element in 'entries'. */
>>>>> +    struct lr_nat_record **array;
>>>>> +};
>>>>> +
>>>>> +const struct lr_nat_record * lr_nat_table_find_by_index(
>>>>> +    const struct lr_nat_table *, size_t od_index);
>>>>> +
>>>>> +/* Incremental processing implementation. */
>>>>> +struct lr_nat_input {
>>>>> +    /* Northbound table references. */
>>>>> +    const struct nbrec_logical_router_table *nbrec_logical_router_table;
>>>>> +
>>>>> +    const struct ovn_datapaths *lr_datapaths;
>>>>> +};
>>>>> +
>>>>> +struct ed_type_lr_nat_data {
>>>>> +    struct lr_nat_table lr_nats;
>>>>> +
>>>>> +    bool tracked;
>>>>> +    struct lr_nat_tracked_data tracked_data;
>>
>> We don't really need the "bool tracked".  We can quite easily have an
>> inline function along the lines of:
>>
>> static inline bool
>> lr_nat_is_tracked_data(const struct lr_nat_tracked_data *td)
>> {
>>     return !hmapx_is_empty(td->deleted)
>>            || !hmapx_is_empty(td->crupdated);
>> }
> 
> Ack.  Addressed in v3.
> 
>>
>> This is actually true for struct ed_type_lb_data->tracked as well but
>> that must be handled in a different patch.
> 
> I haven't included this change in this series.  Do you want me to
> include it in v4 as a separate patch ?
> 

That would be great; it would make the code a bit more consistent I think.

Thanks for your replies!

Regards,
Dumitru


_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to