On 2/14/25 10:03 AM, Ales Musil wrote:
> On Fri, Feb 14, 2025 at 7:34 AM Martin Kalcok <[email protected]>
> wrote:
> 
>> From: Dumitru Ceara <[email protected]>
>>
>> We can do this early, in the en_lr_nat node.  That allows future commits
>> to reuse the parsed information later in the I-P run without having to,
>> reprocess NB contents.
>>
>> While at it clean up a bit the nat entry parsing and checking.  We were
>> re-parsing external_ips, for example, twice even though we already had
>> the parsed IPs available in the internal record.  Also the code now
>> tries to separate parsing and checking a bit as they were inter-twined
>> and very hard to understand.
>>
>> Signed-off-by: Dumitru Ceara <[email protected]>
>> ---
>>
> 
> Hi Martin, Dumitru,
> 

Hi Ales,

> we have talked about this offline but we should really remove invalid
> NAT entries, I'm fine with this approach but it would be a nice thing
> to do as a follow up.
> 

Yes, I agree, but I didn't want to make even more "unrelated" changes so
close to branching for 25.03.  I'll follow up after branching.  This
code definitely needs more refactor than what I tried to do in this patch.

> 
>  northd/en-lr-nat.c | 234 +++++++++++++++++++++++++++++++++++----
>>  northd/en-lr-nat.h |  24 ++--
>>  northd/northd.c    | 270 ++++++++++++---------------------------------
>>  northd/northd.h    |   3 +
>>  4 files changed, 292 insertions(+), 239 deletions(-)
>>
>> diff --git a/northd/en-lr-nat.c b/northd/en-lr-nat.c
>> index bdbb2c860..44009ca6f 100644
>> --- a/northd/en-lr-nat.c
>> +++ b/northd/en-lr-nat.c
>> @@ -43,17 +43,21 @@ 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);
>> +                               const struct ovn_datapaths *lr_datapaths,
>> +                               const struct hmap *lr_ports);
>>  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 struct lr_nat_record *lr_nat_record_create(struct lr_nat_table *,
>> +                                                  const struct
>> ovn_datapath *,
>> +                                                  const struct hmap
>> *lr_ports);
>>  static void lr_nat_record_init(struct lr_nat_record *,
>> -                               const struct ovn_datapath *);
>> +                               const struct ovn_datapath *,
>> +                               const struct hmap *lr_ports);
>>  static void lr_nat_record_clear(struct lr_nat_record *);
>>  static void lr_nat_record_reinit(struct lr_nat_record *,
>> -                                 const struct ovn_datapath *);
>> +                                 const struct ovn_datapath *,
>> +                                 const struct hmap *lr_ports);
>>  static void lr_nat_record_destroy(struct lr_nat_record *);
>>
>>  static bool get_force_snat_ip(const struct ovn_datapath *,
>> @@ -105,7 +109,8 @@ en_lr_nat_run(struct engine_node *node, void *data_)
>>
>>      stopwatch_start(LR_NAT_RUN_STOPWATCH_NAME, time_msec());
>>      lr_nat_table_clear(&data->lr_nats);
>> -    lr_nat_table_build(&data->lr_nats, &northd_data->lr_datapaths);
>> +    lr_nat_table_build(&data->lr_nats, &northd_data->lr_datapaths,
>> +                       &northd_data->lr_ports);
>>
>>      stopwatch_stop(LR_NAT_RUN_STOPWATCH_NAME, time_msec());
>>      engine_set_node_state(node, EN_UPDATED);
>> @@ -133,7 +138,7 @@ lr_nat_northd_handler(struct engine_node *node, void
>> *data_)
>>          od = hmapx_node->data;
>>          lrnat_rec = lr_nat_table_find_by_index_(&data->lr_nats,
>> od->index);
>>          ovs_assert(lrnat_rec);
>> -        lr_nat_record_reinit(lrnat_rec, od);
>> +        lr_nat_record_reinit(lrnat_rec, od, &northd_data->lr_ports);
>>
>>          /* Add the lrnet rec to the tracking data. */
>>          hmapx_add(&data->trk_data.crupdated, lrnat_rec);
>> @@ -169,14 +174,15 @@ lr_nat_table_clear(struct lr_nat_table *table)
>>
>>  static void
>>  lr_nat_table_build(struct lr_nat_table *table,
>> -                   const struct ovn_datapaths *lr_datapaths)
>> +                   const struct ovn_datapaths *lr_datapaths,
>> +                   const struct hmap *lr_ports)
>>  {
>>      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);
>> +        lr_nat_record_create(table, od, lr_ports);
>>      }
>>  }
>>
>> @@ -198,12 +204,13 @@ lr_nat_table_find_by_index_(const struct
>> lr_nat_table *table,
>>
>>  static struct lr_nat_record *
>>  lr_nat_record_create(struct lr_nat_table *table,
>> -                     const struct ovn_datapath *od)
>> +                     const struct ovn_datapath *od,
>> +                     const struct hmap *lr_ports)
>>  {
>>      ovs_assert(od->nbr);
>>
>>      struct lr_nat_record *lrnat_rec = xzalloc(sizeof *lrnat_rec);
>> -    lr_nat_record_init(lrnat_rec, od);
>> +    lr_nat_record_init(lrnat_rec, od, lr_ports);
>>
>>      hmap_insert(&table->entries, &lrnat_rec->key_node,
>>                  uuid_hash(&od->nbr->header_.uuid));
>> @@ -211,9 +218,142 @@ lr_nat_record_create(struct lr_nat_table *table,
>>      return lrnat_rec;
>>  }
>>
>> +/* Returns true if a 'nat_entry' is valid, i.e.:
>> + * - parsing was successful.
>> + * - the string yielded exactly one IPv4 address or exactly one IPv6
>> address.
>> + */
>> +static bool
>> +lr_nat_entry_ext_addrs_valid(const struct ovn_nat *nat_entry)
>> +{
>> +    const struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
>> +
>> +    return (ext_addrs->n_ipv4_addrs == 1 && ext_addrs->n_ipv6_addrs == 0)
>> ||
>> +        (ext_addrs->n_ipv4_addrs == 0 && ext_addrs->n_ipv6_addrs == 1);
>> +}
>> +
>> +/* Populates 'nat_entry->logical_ip_cidr_bits'.  SNAT rules can have
>> + * subnets as logical IPs.  Their prefix length is used to generate
>> + * NAT priority (LPM). */
>> +static bool
>> +lr_nat_entry_set_logical_ip_cidr_bits(const struct ovn_datapath *od,
>> +                                      struct ovn_nat *nat_entry)
>> +{
>> +    struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
>> +    const struct nbrec_nat *nat = nat_entry->nb;
>> +    bool is_v6 = nat_entry_is_v6(nat_entry);
>> +    ovs_be32 ip, mask;
>> +    char *error = NULL;
>> +    if (is_v6) {
>> +        error = ipv6_parse_masked(nat->logical_ip, &ipv6, &mask_v6);
>> +        nat_entry->logical_ip_cidr_bits = ipv6_count_cidr_bits(&mask_v6);
>> +    } else {
>> +        error = ip_parse_masked(nat->logical_ip, &ip, &mask);
>> +        nat_entry->logical_ip_cidr_bits = ip_count_cidr_bits(mask);
>> +    }
>> +    if (nat_entry->type == SNAT) {
>> +        if (error) {
>> +            /* Invalid for both IPv4 and IPv6 */
>> +            static struct vlog_rate_limit rl =
>> +                VLOG_RATE_LIMIT_INIT(5, 1);
>> +            VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
>> +                              "in router " UUID_FMT,
>> +                        nat->logical_ip, UUID_ARGS(&od->key));
>> +            free(error);
>> +            return false;
>> +        }
>> +    } else {
>> +        if (error || (!is_v6 && mask != OVS_BE32_MAX)
>> +            || (is_v6 && memcmp(&mask_v6, &v6_exact, sizeof mask_v6))) {
>> +            /* Invalid for both IPv4 and IPv6 */
>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>> +            VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
>> +                              UUID_FMT,
>> +                         nat->logical_ip, UUID_ARGS(&od->key));
>> +            free(error);
>> +            return false;
>> +        }
>> +    }
>> +    return true;
>> +}
>> +
>> +/* Populates 'nat_entry->l3dgw_port' with the corresponding DGW port
>> + * (distributed gateway port) for the NAT entry, if any.  Sets
>> + * 'nat_entry->is_distributed' to true if the NAT entry is fully
>> + * distributed, that is, dnat_and_snat with valid DGW port and mac set.
>> + * It sets 'nat_entry->is_distributed' to false otherwise.
>> + *
>> + * Returns false on failure to find an adequate DGW port.
>> + * Returns true otherwise.
>> + *  */
>> +static bool
>> +lr_nat_entry_set_dgw_port(const struct ovn_datapath *od,
>> +                          struct ovn_nat *nat_entry,
>> +                          const struct hmap *lr_ports)
>> +{
>> +    const struct nbrec_nat *nat = nat_entry->nb;
>> +
>> +    /* Validate gateway_port of NAT rule. */
>> +    nat_entry->l3dgw_port = NULL;
>> +    if (nat->gateway_port == NULL) {
>> +        if (od->n_l3dgw_ports == 1) {
>> +            nat_entry->l3dgw_port = od->l3dgw_ports[0];
>> +        } else if (od->n_l3dgw_ports > 1) {
>> +            /* Find the DGP reachable for the NAT external IP. */
>> +            for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
>> +               if (lrp_find_member_ip(od->l3dgw_ports[i],
>> nat->external_ip)) {
>> +                   nat_entry->l3dgw_port = od->l3dgw_ports[i];
>> +                   break;
>> +               }
>> +            }
>> +            if (nat_entry->l3dgw_port == NULL) {
>> +                static struct vlog_rate_limit rl =
>> VLOG_RATE_LIMIT_INIT(5, 1);
>> +                VLOG_WARN_RL(&rl, "Unable to determine gateway_port for
>> NAT "
>> +                             "with external_ip: %s configured on logical "
>> +                             "router: %s with multiple distributed
>> gateway "
>> +                             "ports", nat->external_ip, od->nbr->name);
>> +                return false;
>> +            }
>> +        }
>> +    } else {
>> +        nat_entry->l3dgw_port =
>> +            ovn_port_find(lr_ports, nat->gateway_port->name);
>> +
>> +        if (!nat_entry->l3dgw_port || nat_entry->l3dgw_port->od != od ||
>> +            !lrp_is_l3dgw(nat_entry->l3dgw_port)) {
>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>> +            VLOG_WARN_RL(&rl, "gateway_port: %s of NAT configured on "
>> +                         "logical router: %s is not a valid distributed "
>> +                         "gateway port on that router",
>> +                         nat->gateway_port->name, od->nbr->name);
>> +            return false;
>> +        }
>> +    }
>> +
>> +    /* For distributed router NAT, determine whether this NAT rule
>> +     * satisfies the conditions for distributed NAT processing. */
>> +    nat_entry->is_distributed = false;
>> +
>> +    /* NAT cannnot be distributed if the DGP's peer
>> +     * has a chassisredirect port (as the routing is centralized
>> +     * on the gateway chassis for the DGP's networks/subnets.)
>> +     */
>> +    struct ovn_port *l3dgw_port = nat_entry->l3dgw_port;
>> +    if (l3dgw_port && l3dgw_port->peer && l3dgw_port->peer->cr_port) {
>> +        return true;
>> +    }
>> +
>> +    if (od->n_l3dgw_ports && nat_entry->type == DNAT_AND_SNAT &&
>> +        nat->logical_port && nat->external_mac) {
>> +            nat_entry->is_distributed = true;
>> +    }
>> +
>> +    return true;
>> +}
>> +
>>  static void
>>  lr_nat_record_init(struct lr_nat_record *lrnat_rec,
>> -                   const struct ovn_datapath *od)
>> +                   const struct ovn_datapath *od,
>> +                   const struct hmap *lr_ports)
>>  {
>>      lrnat_rec->lr_index = od->index;
>>      lrnat_rec->nbr_uuid = od->nbr->header_.uuid;
>> @@ -285,16 +425,64 @@ lr_nat_record_init(struct lr_nat_record *lrnat_rec,
>>
>>          nat_entry->nb = nat;
>>          nat_entry->is_router_ip = false;
>> +        nat_entry->is_valid = true;
>> +
>> +        if (!strcmp(nat->type, "snat")) {
>> +            nat_entry->type = SNAT;
>> +        } else if (!strcmp(nat->type, "dnat_and_snat")) {
>> +            nat_entry->type = DNAT_AND_SNAT;
>> +        } else {
>> +            nat_entry->type = DNAT;
>> +        }
>> +
>> +        if (!extract_ip_addresses(nat->external_ip,
>> &nat_entry->ext_addrs)) {
>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>> +
>> +            VLOG_WARN_RL(&rl, "Failed to extract ip address %s "
>> +                              "in nat configuration for router %s",
>> +                         nat->external_ip, od->nbr->name);
>> +            nat_entry->is_valid = false;
>> +            continue;
>> +        }
>> +
>> +        if (nat->external_mac && !eth_addr_from_string(nat->external_mac,
>> +                                                       &nat_entry->mac)) {
>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>> +            VLOG_WARN_RL(&rl, "bad mac %s for dnat in router "
>> +                         ""UUID_FMT"", nat->external_mac,
>> UUID_ARGS(&od->key));
>> +            nat_entry->is_valid = false;
>> +            continue;
>> +        }
>>
>> -        if (!extract_ip_addresses(nat->external_ip,
>> -                                  &nat_entry->ext_addrs) ||
>> -                !nat_entry_is_valid(nat_entry)) {
>> +        if (!lr_nat_entry_ext_addrs_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,
>> -                         od->nbr->name);
>> +            VLOG_WARN_RL(&rl, "Invalid ip address %s in nat configuration
>> "
>> +                              "for router %s",
>> +                         nat->external_ip, od->nbr->name);
>> +            nat_entry->is_valid = false;
>> +            continue;
>> +        }
>> +
>> +        if (nat->allowed_ext_ips && nat->exempted_ext_ips) {
>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>> +            VLOG_WARN_RL(&rl, "NAT rule: " UUID_FMT " not applied, since "
>> +                              "both allowed and exempt external ips set",
>> +                        UUID_ARGS(&(nat->header_.uuid)));
>> +            nat_entry->is_valid = false;
>> +            continue;
>> +        }
>> +
>> +        /* Set nat_entry->logical_ip_cidr_bits (SNAT logical_ip can be
>> +         * a subnet). */
>> +        if (!lr_nat_entry_set_logical_ip_cidr_bits(od, nat_entry)) {
>> +            nat_entry->is_valid = false;
>> +            continue;
>> +        }
>> +
>> +        /* Set nat_entry->l3dgw_port and nat_entry->is_distributed. */
>> +        if (!lr_nat_entry_set_dgw_port(od, nat_entry, lr_ports)) {
>> +            nat_entry->is_valid = false;
>>              continue;
>>          }
>>
>> @@ -313,11 +501,8 @@ lr_nat_record_init(struct lr_nat_record *lrnat_rec,
>>                              nat_entry->ext_addrs.ipv6_addrs[0].addr_s,
>>                              nat_entry);
>>              }
>> -            nat_entry->type = SNAT;
>>          } else {
>> -            nat_entry->type = DNAT;
>>              if (!strcmp(nat->type, "dnat_and_snat")) {
>> -                nat_entry->type = DNAT_AND_SNAT;
>>                  if (nat->logical_port && nat->external_mac) {
>>                      lrnat_rec->has_distributed_nat = true;
>>                  }
>> @@ -348,10 +533,11 @@ lr_nat_record_clear(struct lr_nat_record *lrnat_rec)
>>
>>  static void
>>  lr_nat_record_reinit(struct lr_nat_record *lrnat_rec,
>> -                     const struct ovn_datapath *od)
>> +                     const struct ovn_datapath *od,
>> +                     const struct hmap *lr_ports)
>>  {
>>      lr_nat_record_clear(lrnat_rec);
>> -    lr_nat_record_init(lrnat_rec, od);
>> +    lr_nat_record_init(lrnat_rec, od, lr_ports);
>>  }
>>
>>  static void
>> diff --git a/northd/en-lr-nat.h b/northd/en-lr-nat.h
>> index 120ceeca9..a73933390 100644
>> --- a/northd/en-lr-nat.h
>> +++ b/northd/en-lr-nat.h
>> @@ -38,13 +38,22 @@ enum ovn_nat_type {
>>  /* Contains a NAT entry with the external addresses pre-parsed. */
>>  struct ovn_nat {
>>      const struct nbrec_nat *nb;
>> -    struct lport_addresses ext_addrs;
>> +    struct lport_addresses ext_addrs; /* Parsed NB.NAT.external_ip. */
>> +    struct eth_addr mac;              /* Parsed NB.NAT.external_mac. */
>> +    int logical_ip_cidr_bits;         /* Parsed NB.NATlogical_ip prefix
>> len. */
>>      struct ovs_list ext_addr_list_node; /* Linkage in the per-external IP
>>                                           * list of nat entries. Currently
>>                                           * only used for SNAT.
>>                                           */
>>      bool is_router_ip; /* Indicates if the NAT external_ip is also one of
>>                          * router's lrp ip.  Can be 'true' only for SNAT.
>> */
>> +
>> +    struct ovn_port *l3dgw_port; /* If non-NULL, the distributed gateway
>> port
>> +                                  * this NAT will use.  NULL for gateway
>> +                                  * routers. */
>> +    bool is_distributed;         /* True if this NAT record is fully
>> +                                  * distributed. */
>> +    bool is_valid; /* True if the configuration of this entry is valid. */
>>      enum ovn_nat_type type;
>>  };
>>
>> @@ -114,19 +123,6 @@ void en_lr_nat_run(struct engine_node *, void *data);
>>  bool lr_nat_logical_router_handler(struct engine_node *, void *data);
>>  bool lr_nat_northd_handler(struct engine_node *, void *data);
>>
>> -/* Returns true if a 'nat_entry' is valid, i.e.:
>> - * - parsing was successful.
>> - * - the string yielded exactly one IPv4 address or exactly one IPv6
>> address.
>> - */
>> -static inline bool
>> -nat_entry_is_valid(const struct ovn_nat *nat_entry)
>> -{
>> -    const struct lport_addresses *ext_addrs = &nat_entry->ext_addrs;
>> -
>> -    return (ext_addrs->n_ipv4_addrs == 1 && ext_addrs->n_ipv6_addrs == 0)
>> ||
>> -        (ext_addrs->n_ipv4_addrs == 0 && ext_addrs->n_ipv6_addrs == 1);
>> -}
>> -
>>  static inline bool
>>  nat_entry_is_v6(const struct ovn_nat *nat_entry)
>>  {
>> diff --git a/northd/northd.c b/northd/northd.c
>> index 11f4131aa..1d9f0675e 100644
>> --- a/northd/northd.c
>> +++ b/northd/northd.c
>> @@ -1553,9 +1553,6 @@ ipam_add_port_addresses(struct ovn_datapath *od,
>> struct ovn_port *op)
>>      }
>>  }
>>
>> -static const char *find_lrp_member_ip(const struct ovn_port *op,
>> -                                      const char *ip_s);
>> -
>>  /* 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
>>   * be applied. */
>> @@ -1563,7 +1560,7 @@ static bool
>>  is_nat_gateway_port(const struct nbrec_nat *nat, const struct ovn_port
>> *op)
>>  {
>>      if (op->od->n_l3dgw_ports > 1
>> -        && ((!nat->gateway_port && !find_lrp_member_ip(op,
>> nat->external_ip))
>> +        && ((!nat->gateway_port && !lrp_find_member_ip(op,
>> nat->external_ip))
>>              || (nat->gateway_port && nat->gateway_port != op->nbrp))) {
>>          return false;
>>      }
>> @@ -8842,7 +8839,7 @@ build_lswitch_rport_arp_req_flows_for_lbnats(
>>              &lr_stateful_rec->lrnat_rec->nat_entries[i];
>>          const struct nbrec_nat *nat = nat_entry->nb;
>>
>> -        if (!nat_entry_is_valid(nat_entry)) {
>> +        if (!nat_entry->is_valid) {
>>              continue;
>>          }
>>
>> @@ -10569,8 +10566,8 @@ build_bfd_map(const struct nbrec_bfd_table
>> *nbrec_bfd_table,
>>   * overlaps with 'ip_s".  If one is not found, returns NULL.
>>   *
>>   * The caller must not free the returned string. */
>> -static const char *
>> -find_lrp_member_ip(const struct ovn_port *op, const char *ip_s)
>> +const char *
>> +lrp_find_member_ip(const struct ovn_port *op, const char *ip_s)
>>  {
>>      return find_lport_address(&op->lrp_networks, ip_s);
>>  }
>> @@ -10589,7 +10586,7 @@ get_outport_for_routing_policy_nexthop(struct
>> ovn_datapath *od,
>>         struct nbrec_logical_router_port *lrp = od->nbr->ports[i];
>>
>>         struct ovn_port *out_port = ovn_port_find(lr_ports, lrp->name);
>> -       if (out_port && find_lrp_member_ip(out_port, nexthop)) {
>> +       if (out_port && lrp_find_member_ip(out_port, nexthop)) {
>>             return out_port;
>>         }
>>      }
>> @@ -10679,7 +10676,7 @@ build_routing_policy_flow(struct lflow_table
>> *lflows, struct ovn_datapath *od,
>>              return;
>>          }
>>
>> -        const char *lrp_addr_s = find_lrp_member_ip(out_port, nexthop);
>> +        const char *lrp_addr_s = lrp_find_member_ip(out_port, nexthop);
>>          if (!lrp_addr_s) {
>>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>>              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
>> @@ -10780,7 +10777,7 @@ build_ecmp_routing_policy_flows(struct lflow_table
>> *lflows,
>>          }
>>
>>          const char *lrp_addr_s =
>> -            find_lrp_member_ip(out_port, rp->valid_nexthops[i]);
>> +            lrp_find_member_ip(out_port, rp->valid_nexthops[i]);
>>          if (!lrp_addr_s) {
>>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>>              VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
>> @@ -11525,7 +11522,7 @@ find_route_outport(const struct hmap *lr_ports,
>> const char *output_port,
>>          return false;
>>      }
>>      if (nexthop[0]) {
>> -        *lrp_addr_s = find_lrp_member_ip(*out_port, nexthop);
>> +        *lrp_addr_s = lrp_find_member_ip(*out_port, nexthop);
>>      }
>>      if (!*lrp_addr_s) {
>>          if (!force_out_port) {
>> @@ -11570,7 +11567,7 @@ find_static_route_outport(const struct
>> ovn_datapath *od,
>>           * router port matching the next hop. */
>>          HMAP_FOR_EACH (out_port, dp_node, &od->ports) {
>>              if (route->nexthop[0]) {
>> -                lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
>> +                lrp_addr_s = lrp_find_member_ip(out_port, route->nexthop);
>>              }
>>              if (lrp_addr_s) {
>>                  break;
>> @@ -14514,7 +14511,7 @@ build_arp_resolve_flows_for_lsp(
>>                          continue;
>>                      }
>>
>> -                    if (!find_lrp_member_ip(peer, ip_s)) {
>> +                    if (!lrp_find_member_ip(peer, ip_s)) {
>>                          continue;
>>                      }
>>
>> @@ -14546,7 +14543,7 @@ build_arp_resolve_flows_for_lsp(
>>                          continue;
>>                      }
>>
>> -                    if (!find_lrp_member_ip(peer, ip_s)) {
>> +                    if (!lrp_find_member_ip(peer, ip_s)) {
>>                          continue;
>>                      }
>>
>> @@ -15538,7 +15535,7 @@ build_lrouter_arp_nd_for_datapath(const struct
>> ovn_datapath *od,
>>          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>>
>>          /* Skip entries we failed to parse. */
>> -        if (!nat_entry_is_valid(nat_entry)) {
>> +        if (!nat_entry->is_valid) {
>>              continue;
>>          }
>>
>> @@ -15845,7 +15842,7 @@ build_lrouter_ipv4_ip_input_for_lbnats(
>>              &lr_stateful_rec->lrnat_rec->nat_entries[i];
>>
>>          /* Skip entries we failed to parse. */
>> -        if (!nat_entry_is_valid(nat_entry)) {
>> +        if (!nat_entry->is_valid) {
>>              continue;
>>          }
>>
>> @@ -16487,145 +16484,6 @@ build_lrouter_ingress_flow(struct lflow_table
>> *lflows,
>>      }
>>  }
>>
>> -static int
>> -lrouter_check_nat_entry(const struct ovn_datapath *od,
>> -                        const struct ovn_nat *nat_entry,
>> -                        const struct hmap *lr_ports, ovs_be32 *mask,
>> -                        bool *is_v6, int *cidr_bits, struct eth_addr *mac,
>> -                        bool *distributed, struct ovn_port
>> **nat_l3dgw_port)
>> -{
>> -    const struct nbrec_nat *nat = nat_entry->nb;
>> -    struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
>> -    ovs_be32 ip;
>> -
>> -    if (nat->allowed_ext_ips && nat->exempted_ext_ips) {
>> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
>> -        VLOG_WARN_RL(&rl, "NAT rule: "UUID_FMT" not applied, since "
>> -                    "both allowed and exempt external ips set",
>> -                    UUID_ARGS(&(nat->header_.uuid)));
>> -        return -EINVAL;
>> -    }
>> -
>> -    char *error = ip_parse_masked(nat->external_ip, &ip, mask);
>> -    *is_v6 = false;
>> -
>> -    if (error || *mask != OVS_BE32_MAX) {
>> -        free(error);
>> -        error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6);
>> -        if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) {
>> -            /* Invalid for both IPv4 and IPv6 */
>> -            static struct vlog_rate_limit rl =
>> -                VLOG_RATE_LIMIT_INIT(5, 1);
>> -            VLOG_WARN_RL(&rl, "bad external ip %s for nat",
>> -                        nat->external_ip);
>> -            free(error);
>> -            return -EINVAL;
>> -        }
>> -        /* It was an invalid IPv4 address, but valid IPv6.
>> -        * Treat the rest of the handling of this NAT rule
>> -        * as IPv6. */
>> -        *is_v6 = true;
>> -    }
>> -
>> -    /* Validate gateway_port of NAT rule. */
>> -    *nat_l3dgw_port = NULL;
>> -    if (nat->gateway_port == NULL) {
>> -        if (od->n_l3dgw_ports == 1) {
>> -            *nat_l3dgw_port = od->l3dgw_ports[0];
>> -        } else if (od->n_l3dgw_ports > 1) {
>> -            /* Find the DGP reachable for the NAT external IP. */
>> -            for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
>> -               if (find_lrp_member_ip(od->l3dgw_ports[i],
>> nat->external_ip)) {
>> -                   *nat_l3dgw_port = od->l3dgw_ports[i];
>> -                   break;
>> -               }
>> -            }
>> -            if (*nat_l3dgw_port == NULL) {
>> -                static struct vlog_rate_limit rl =
>> VLOG_RATE_LIMIT_INIT(5, 1);
>> -                VLOG_WARN_RL(&rl, "Unable to determine gateway_port for
>> NAT "
>> -                             "with external_ip: %s configured on logical "
>> -                             "router: %s with multiple distributed
>> gateway "
>> -                             "ports", nat->external_ip, od->nbr->name);
>> -                return -EINVAL;
>> -            }
>> -        }
>> -    } else {
>> -        *nat_l3dgw_port = ovn_port_find(lr_ports,
>> nat->gateway_port->name);
>> -
>> -        if (!(*nat_l3dgw_port) || (*nat_l3dgw_port)->od != od ||
>> -            !lrp_is_l3dgw(*nat_l3dgw_port)) {
>> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>> -            VLOG_WARN_RL(&rl, "gateway_port: %s of NAT configured on "
>> -                         "logical router: %s is not a valid distributed "
>> -                         "gateway port on that router",
>> -                         nat->gateway_port->name, od->nbr->name);
>> -            return -EINVAL;
>> -        }
>> -    }
>> -
>> -    /* Check the validity of nat->logical_ip. 'logical_ip' can
>> -    * be a subnet when the type is "snat". */
>> -    if (*is_v6) {
>> -        error = ipv6_parse_masked(nat->logical_ip, &ipv6, &mask_v6);
>> -        *cidr_bits = ipv6_count_cidr_bits(&mask_v6);
>> -    } else {
>> -        error = ip_parse_masked(nat->logical_ip, &ip, mask);
>> -        *cidr_bits = ip_count_cidr_bits(*mask);
>> -    }
>> -    if (nat_entry->type == SNAT) {
>> -        if (error) {
>> -            /* Invalid for both IPv4 and IPv6 */
>> -            static struct vlog_rate_limit rl =
>> -                VLOG_RATE_LIMIT_INIT(5, 1);
>> -            VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
>> -                        "in router "UUID_FMT"",
>> -                        nat->logical_ip, UUID_ARGS(&od->key));
>> -            free(error);
>> -            return -EINVAL;
>> -        }
>> -    } else {
>> -        if (error || (*is_v6 == false && *mask != OVS_BE32_MAX)
>> -            || (*is_v6 && memcmp(&mask_v6, &v6_exact,
>> -                                sizeof mask_v6))) {
>> -            /* Invalid for both IPv4 and IPv6 */
>> -            static struct vlog_rate_limit rl =
>> -                VLOG_RATE_LIMIT_INIT(5, 1);
>> -            VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
>> -                ""UUID_FMT"", nat->logical_ip, UUID_ARGS(&od->key));
>> -            free(error);
>> -            return -EINVAL;
>> -        }
>> -    }
>> -
>> -    /* For distributed router NAT, determine whether this NAT rule
>> -     * satisfies the conditions for distributed NAT processing. */
>> -    *distributed = false;
>> -
>> -    /* NAT cannnot be distributed if the DGP's peer
>> -     * has a chassisredirect port (as the routing is centralized
>> -     * on the gateway chassis for the DGP's networks/subnets.)
>> -     */
>> -    struct ovn_port *l3dgw_port = *nat_l3dgw_port;
>> -    if (l3dgw_port && l3dgw_port->peer && l3dgw_port->peer->cr_port) {
>> -        return 0;
>> -    }
>> -
>> -    if (od->n_l3dgw_ports && nat_entry->type == DNAT_AND_SNAT &&
>> -        nat->logical_port && nat->external_mac) {
>> -        if (eth_addr_from_string(nat->external_mac, mac)) {
>> -            *distributed = true;
>> -        } else {
>> -            static struct vlog_rate_limit rl =
>> -                VLOG_RATE_LIMIT_INIT(5, 1);
>> -            VLOG_WARN_RL(&rl, "bad mac %s for dnat in router "
>> -                ""UUID_FMT"", nat->external_mac, UUID_ARGS(&od->key));
>> -            return -EINVAL;
>> -        }
>> -    }
>> -
>> -    return 0;
>> -}
>> -
>>  /* NAT, Defrag and load balancing. */
>>  static void build_lr_nat_defrag_and_lb_default_flows(
>>      struct ovn_datapath *od, struct lflow_table *lflows,
>> @@ -16668,7 +16526,7 @@ static void
>>  build_lrouter_nat_defrag_and_lb(
>>      const struct lr_stateful_record *lr_stateful_rec,
>>      const struct ovn_datapath *od, struct lflow_table *lflows,
>> -    const struct hmap *ls_ports, const struct hmap *lr_ports,
>> +    const struct hmap *ls_ports,
>>      struct ds *match, struct ds *actions,
>>      const struct shash *meter_groups,
>>      const struct chassis_features *features,
>> @@ -16772,20 +16630,16 @@ build_lrouter_nat_defrag_and_lb(
>>      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>>          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>>          const struct nbrec_nat *nat = nat_entry->nb;
>> -        struct eth_addr mac = eth_addr_broadcast;
>> -        bool is_v6, distributed_nat;
>> -        ovs_be32 mask;
>> -        int cidr_bits;
>> -        struct ovn_port *l3dgw_port;
>>
>> -        bool stateless = lrouter_dnat_and_snat_is_stateless(nat_entry);
>> -
>> -        if (lrouter_check_nat_entry(od, nat_entry, lr_ports, &mask,
>> &is_v6,
>> -                                    &cidr_bits,
>> -                                    &mac, &distributed_nat, &l3dgw_port)
>> < 0) {
>> +        /* Skip invalid entries. */
>> +        if (!nat_entry->is_valid) {
>>              continue;
>>          }
>>
>> +        bool stateless = lrouter_dnat_and_snat_is_stateless(nat_entry);
>> +        bool is_v6 = nat_entry_is_v6(nat_entry);
>> +        unsigned int cidr_bits = nat_entry->logical_ip_cidr_bits;
>> +
>>          /* S_ROUTER_IN_UNSNAT
>>           * Ingress UNSNAT table: It is for already established
>> connections'
>>           * reverse traffic. i.e., SNAT has already been done in egress
>> @@ -16798,22 +16652,27 @@ build_lrouter_nat_defrag_and_lb(
>>           * egress pipeline. */
>>          if (stateless) {
>>              build_lrouter_in_unsnat_stateless_flow(lflows, od, nat_entry,
>> -                                                   match, distributed_nat,
>> -                                                   is_v6, l3dgw_port,
>> +                                                   match,
>> +
>>  nat_entry->is_distributed,
>> +                                                   is_v6,
>> +                                                   nat_entry->l3dgw_port,
>>                                                     lflow_ref);
>>          } else if (lrouter_use_common_zone(od)) {
>>              build_lrouter_in_unsnat_in_czone_flow(lflows, od, nat_entry,
>> match,
>> -                                                  distributed_nat, is_v6,
>> -                                                  l3dgw_port, lflow_ref);
>> +
>> nat_entry->is_distributed,
>> +                                                  is_v6,
>> nat_entry->l3dgw_port,
>> +                                                  lflow_ref);
>>          } else {
>>              build_lrouter_in_unsnat_flow(lflows, od, nat_entry, match,
>> -                                         distributed_nat, is_v6,
>> l3dgw_port,
>> +                                         nat_entry->is_distributed,
>> +                                         is_v6, nat_entry->l3dgw_port,
>>                                           lflow_ref);
>>          }
>>          /* S_ROUTER_IN_DNAT */
>>          build_lrouter_in_dnat_flow(lflows, od, lrnat_rec, nat_entry,
>> match,
>> -                                   actions, distributed_nat, cidr_bits,
>> is_v6,
>> -                                   l3dgw_port, stateless, lflow_ref);
>> +                                   actions, nat_entry->is_distributed,
>> +                                   cidr_bits, is_v6,
>> nat_entry->l3dgw_port,
>> +                                   stateless, lflow_ref);
>>
>>          /* ARP resolve for NAT IPs. */
>>          if (!od->is_gw_router) {
>> @@ -16825,7 +16684,8 @@ build_lrouter_nat_defrag_and_lb(
>>                  ds_clear(match);
>>                  ds_put_format(
>>                      match, "inport == %s && outport == %s && ip%s.dst ==
>> %s",
>> -                    l3dgw_port->json_key, l3dgw_port->json_key,
>> +                    nat_entry->l3dgw_port->json_key,
>> +                    nat_entry->l3dgw_port->json_key,
>>                      is_v6 ? "6" : "4", nat->external_ip);
>>                  ovn_lflow_add_with_hint(lflows, od,
>>                                          S_ROUTER_IN_ARP_RESOLVE,
>> @@ -16839,21 +16699,21 @@ build_lrouter_nat_defrag_and_lb(
>>                  ds_clear(match);
>>                  ds_put_format(
>>                      match, "outport == %s && %s == %s",
>> -                    l3dgw_port->json_key,
>> +                    nat_entry->l3dgw_port->json_key,
>>                      is_v6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4,
>>                      nat->external_ip);
>>                  ds_clear(actions);
>>                  ds_put_format(
>>                      actions, "eth.dst = %s; next;",
>> -                    distributed_nat ? nat->external_mac :
>> -                    l3dgw_port->lrp_networks.ea_s);
>> +                    nat_entry->is_distributed ? nat->external_mac :
>> +                    nat_entry->l3dgw_port->lrp_networks.ea_s);
>>                  ovn_lflow_add_with_hint(lflows, od,
>>                                          S_ROUTER_IN_ARP_RESOLVE,
>>                                          100, ds_cstr(match),
>>                                          ds_cstr(actions),
>>                                          &nat->header_,
>>                                          lflow_ref);
>> -                if (od->redirect_bridged && distributed_nat) {
>> +                if (od->redirect_bridged && nat_entry->is_distributed) {
>>                      ds_clear(match);
>>                      ds_put_format(
>>                              match,
>> @@ -16883,13 +16743,16 @@ build_lrouter_nat_defrag_and_lb(
>>          if (use_common_zone) {
>>              /* S_ROUTER_OUT_DNAT_LOCAL */
>>              build_lrouter_out_is_dnat_local(lflows, od, nat, match,
>> actions,
>> -                                            distributed_nat, is_v6,
>> -                                            l3dgw_port, lflow_ref);
>> +                                            nat_entry->is_distributed,
>> +                                            is_v6, nat_entry->l3dgw_port,
>> +                                            lflow_ref);
>>          }
>>
>>          /* S_ROUTER_OUT_UNDNAT */
>>          build_lrouter_out_undnat_flow(lflows, od, nat_entry, match,
>> actions,
>> -                                      distributed_nat, mac, is_v6,
>> l3dgw_port,
>> +                                      nat_entry->is_distributed,
>> +                                      nat_entry->mac, is_v6,
>> +                                      nat_entry->l3dgw_port,
>>                                        stateless, lflow_ref);
>>          /* S_ROUTER_OUT_SNAT
>>           * Egress SNAT table: Packets enter the egress pipeline with
>> @@ -16897,23 +16760,30 @@ build_lrouter_nat_defrag_and_lb(
>>           * address. */
>>          if (stateless) {
>>              build_lrouter_out_snat_stateless_flow(lflows, od, nat_entry,
>> match,
>> -                                                  actions,
>> distributed_nat,
>> -                                                  mac, cidr_bits, is_v6,
>> -                                                  l3dgw_port, lflow_ref);
>> +                                                  actions,
>> +
>> nat_entry->is_distributed,
>> +                                                  nat_entry->mac,
>> cidr_bits,
>> +                                                  is_v6,
>> nat_entry->l3dgw_port,
>> +                                                  lflow_ref);
>>          } else if (lrouter_use_common_zone(od)) {
>>              build_lrouter_out_snat_in_czone_flow(lflows, od, nat_entry,
>> match,
>> -                                                 actions,
>> distributed_nat, mac,
>> -                                                 cidr_bits, is_v6,
>> l3dgw_port,
>> +                                                 actions,
>> +
>>  nat_entry->is_distributed,
>> +                                                 nat_entry->mac,
>> cidr_bits,
>> +                                                 is_v6,
>> nat_entry->l3dgw_port,
>>                                                   lflow_ref);
>>          } else {
>>              build_lrouter_out_snat_flow(lflows, od, nat_entry, match,
>> actions,
>> -                                        distributed_nat, mac, cidr_bits,
>> is_v6,
>> -                                        l3dgw_port, lflow_ref, features);
>> +                                        nat_entry->is_distributed,
>> +                                        nat_entry->mac, cidr_bits,
>> +                                        is_v6, nat_entry->l3dgw_port,
>> +                                        lflow_ref, features);
>>          }
>>
>>          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
>> -        build_lrouter_ingress_flow(lflows, od, nat_entry, match, actions,
>> mac,
>> -                                   distributed_nat, is_v6, l3dgw_port,
>> +        build_lrouter_ingress_flow(lflows, od, nat_entry, match, actions,
>> +                                   nat_entry->mac,
>> nat_entry->is_distributed,
>> +                                   is_v6, nat_entry->l3dgw_port,
>>                                     meter_groups, lflow_ref);
>>
>>          /* Ingress Gateway Redirect Table: For NAT on a distributed
>> @@ -16925,13 +16795,13 @@ build_lrouter_nat_defrag_and_lb(
>>           * generated in the following stage is sent out with proper IP/MAC
>>           * src addresses.
>>           */
>> -        if (distributed_nat) {
>> +        if (nat_entry->is_distributed) {
>>              ds_clear(match);
>>              ds_clear(actions);
>>              ds_put_format(match,
>>                            "ip%s.src == %s && outport == %s",
>>                            is_v6 ? "6" : "4", nat->logical_ip,
>> -                          l3dgw_port->json_key);
>> +                          nat_entry->l3dgw_port->json_key);
>>              /* Add a rule to drop traffic from a distributed NAT if
>>               * the virtual port has not claimed yet becaused otherwise
>>               * the traffic will be centralized misconfiguring the TOR
>> switch.
>> @@ -16967,10 +16837,10 @@ build_lrouter_nat_defrag_and_lb(
>>              ds_put_format(match, "ip%s.dst == %s && outport == %s",
>>                            is_v6 ? "6" : "4",
>>                            nat->external_ip,
>> -                          l3dgw_port->json_key);
>> -            if (!distributed_nat) {
>> +                          nat_entry->l3dgw_port->json_key);
>> +            if (!nat_entry->is_distributed) {
>>                  ds_put_format(match, " && is_chassis_resident(%s)",
>> -                              l3dgw_port->cr_port->json_key);
>> +                              nat_entry->l3dgw_port->cr_port->json_key);
>>              } else {
>>                  ds_put_format(match, " && is_chassis_resident(\"%s\")",
>>                                nat->logical_port);
>> @@ -17268,7 +17138,6 @@ build_lr_stateful_flows(const struct
>> lr_stateful_record *lr_stateful_rec,
>>                          const struct ovn_datapaths *lr_datapaths,
>>                          struct lflow_table *lflows,
>>                          const struct hmap *ls_ports,
>> -                        const struct hmap *lr_ports,
>>                          struct ds *match,
>>                          struct ds *actions,
>>                          const struct shash *meter_groups,
>> @@ -17280,7 +17149,7 @@ build_lr_stateful_flows(const struct
>> lr_stateful_record *lr_stateful_rec,
>>      ovs_assert(uuid_equals(&od->nbr->header_.uuid,
>>                             &lr_stateful_rec->nbr_uuid));
>>      build_lrouter_nat_defrag_and_lb(lr_stateful_rec, od, lflows, ls_ports,
>> -                                    lr_ports, match, actions,
>> meter_groups,
>> +                                    match, actions, meter_groups,
>>                                      features, lr_stateful_rec->lflow_ref);
>>      build_lr_gateway_redirect_flows_for_nats(od,
>> lr_stateful_rec->lrnat_rec,
>>                                               lflows, match, actions,
>> @@ -17608,8 +17477,7 @@ build_lflows_thread(void *arg)
>>                      }
>>                      build_lr_stateful_flows(lr_stateful_rec,
>> lsi->lr_datapaths,
>>                                              lsi->lflows, lsi->ls_ports,
>> -                                            lsi->lr_ports, &lsi->match,
>> -                                            &lsi->actions,
>> +                                            &lsi->match, &lsi->actions,
>>                                              lsi->meter_groups,
>>                                              lsi->features);
>>                  }
>> @@ -17835,7 +17703,7 @@ build_lswitch_and_lrouter_flows(
>>          stopwatch_start(LFLOWS_LR_STATEFUL_STOPWATCH_NAME, time_msec());
>>          LR_STATEFUL_TABLE_FOR_EACH (lr_stateful_rec, lr_stateful_table) {
>>              build_lr_stateful_flows(lr_stateful_rec, lsi.lr_datapaths,
>> -                                    lsi.lflows, lsi.ls_ports,
>> lsi.lr_ports,
>> +                                    lsi.lflows, lsi.ls_ports,
>>                                      &lsi.match, &lsi.actions,
>>                                      lsi.meter_groups, lsi.features);
>>          }
>> @@ -18238,7 +18106,7 @@ lflow_handle_lr_stateful_changes(struct
>> ovsdb_idl_txn *ovnsb_txn,
>>          /* Generate new lflows. */
>>          build_lr_stateful_flows(lr_stateful_rec,
>> lflow_input->lr_datapaths,
>>                                  lflows, lflow_input->ls_ports,
>> -                                lflow_input->lr_ports, &match, &actions,
>> +                                &match, &actions,
>>                                  lflow_input->meter_groups,
>>                                  lflow_input->features);
>>
>> diff --git a/northd/northd.h b/northd/northd.h
>> index eaf3d9455..b984e124d 100644
>> --- a/northd/northd.h
>> +++ b/northd/northd.h
>> @@ -967,6 +967,8 @@ lsp_is_router(const struct nbrec_logical_switch_port
>> *nbsp)
>>      return !strcmp(nbsp->type, "router");
>>  }
>>
>> +const char *lrp_find_member_ip(const struct ovn_port *op, const char
>> *ip_s);
>> +
>>  /* This function returns true if 'op' is a gateway router port.
>>   * False otherwise.
>>   * For 'op' to be a gateway router port.
>> @@ -985,6 +987,7 @@ lrp_is_l3dgw(const struct ovn_port *op)
>>  }
>>
>>  struct ovn_port *ovn_port_find(const struct hmap *ports, const char
>> *name);
>> +
>>  void build_igmp_lflows(struct hmap *igmp_groups,
>>                         const struct hmap *ls_datapaths,
>>                         struct lflow_table *lflows,
>> --
>> 2.43.0
>>
>>
> Otherwise it looks good to me, thanks.
> 
> Acked-by: Ales Musil <[email protected]>
> 

Thanks all!  I also added Felix's ack from v6 (because this patch hasn't
changed since v6) and then pushed this patch to main.

Regards,
Dumitru


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

Reply via email to