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

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

Reply via email to