In order to get the VTEP setup listeners for each special interface
that is dedicated to given vni. Those interfaces need to be
part of the VRF, created by CMS. Once those listeners are in place
ovn-controller will receive notifications about remote VTEPs.

Create remote VTEP representation based on the data received from
netlink neighbor table. There are three key attributes that
uniquely identify each remote VTEP:
1) IP of the remote tunnel.
2) Destination port of the remote tunnel.
3) VNI.

Crate map of all remote VTEPs based on those three attributes.
The map will be used later on to crate the EVPN binding. One
thing to note is that all of this is specific to each ovn-controller.
This is one of the reasons why those data are local to each
ovn-controller, the second reason is scalability as we would have
possibly duplicate SB entries differentiated only by residing
chassis.

Note that there isn't any way to set vni for given LS that will
be part of future patch.

Signed-off-by: Ales Musil <amu...@redhat.com>
---
 controller/neighbor-exchange-stub.c |  12 +++
 controller/neighbor-exchange.c      | 116 ++++++++++++++++++++++++++--
 controller/neighbor-exchange.h      |  22 +++++-
 controller/neighbor.c               |  98 +++++++++++++++++++----
 controller/neighbor.h               |  11 +++
 controller/ovn-controller.c         | 108 +++++++++++++++++++++++---
 lib/ovn-util.c                      |   6 ++
 lib/ovn-util.h                      |   2 +
 8 files changed, 346 insertions(+), 29 deletions(-)

diff --git a/controller/neighbor-exchange-stub.c 
b/controller/neighbor-exchange-stub.c
index a42df84c2..d314543be 100644
--- a/controller/neighbor-exchange-stub.c
+++ b/controller/neighbor-exchange-stub.c
@@ -28,3 +28,15 @@ neighbor_exchange_status_run(void)
 {
     return 0;
 }
+
+void
+evpn_remote_vteps_clear(struct hmap *remote_vteps OVS_UNUSED)
+{
+}
+
+void
+evpn_remote_vtep_list(struct unixctl_conn *conn OVS_UNUSED,
+                      int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
+                      void *data_ OVS_UNUSED)
+{
+}
diff --git a/controller/neighbor-exchange.c b/controller/neighbor-exchange.c
index 4750d4f88..0fec5e59d 100644
--- a/controller/neighbor-exchange.c
+++ b/controller/neighbor-exchange.c
@@ -15,12 +15,30 @@
 
 #include <config.h>
 
+#include <linux/neighbour.h>
+
 #include "host-if-monitor.h"
 #include "neighbor.h"
 #include "neighbor-exchange.h"
 #include "neighbor-exchange-netlink.h"
 #include "neighbor-table-notify.h"
+#include "openvswitch/vlog.h"
+#include "ovn-util.h"
+#include "packets.h"
 #include "vec.h"
+#include "unixctl.h"
+
+VLOG_DEFINE_THIS_MODULE(neighbor_exchange);
+
+static bool neighbor_is_valid_remote_vtep(struct ne_nl_received_neigh *);
+static uint32_t evpn_remote_vtep_hash(const struct in6_addr *ip,
+                                      uint16_t port, uint32_t vni);
+static void evpn_remote_vtep_add(struct hmap *remote_vteps, struct in6_addr ip,
+                                 uint16_t port, uint32_t vni);
+static struct evpn_remote_vtep *evpn_remote_vtep_find(
+    const struct hmap *remote_vteps, struct in6_addr *ip,
+    uint16_t port, uint32_t vni);
+
 
 /* Last neighbor_exchange netlink operation. */
 static int neighbor_exchange_nl_status;
@@ -66,12 +84,19 @@ neighbor_exchange_run(const struct neighbor_exchange_ctx_in 
*n_ctx_in,
                              &received_neighbors)
         );
 
-        /* XXX: TODO GLUE: sync received neighbors to:
-         * - SB: for remote vtep entries
-         *   https://issues.redhat.com/browse/FDP-1385
-         * - in memory table for remote neighbor entries
-         *   https://issues.redhat.com/browse/FDP-1387
-         */
+        if (nim->type == NEIGH_IFACE_VXLAN) {
+            struct ne_nl_received_neigh *ne;
+            VECTOR_FOR_EACH_PTR (&received_neighbors, ne) {
+                if (neighbor_is_valid_remote_vtep(ne)) {
+                    uint16_t port = ne->port ? ne->port : DEFAULT_VXLAN_PORT;
+                    if (!evpn_remote_vtep_find(n_ctx_out->remote_vteps,
+                                               &ne->addr, port, nim->vni)) {
+                        evpn_remote_vtep_add(n_ctx_out->remote_vteps, ne->addr,
+                                             port, nim->vni);
+                    }
+                }
+            }
+        }
 
         neighbor_table_add_watch_request(&n_ctx_out->neighbor_table_watches,
                                          if_index, nim->if_name);
@@ -84,3 +109,82 @@ neighbor_exchange_status_run(void)
 {
     return neighbor_exchange_nl_status;
 }
+
+static bool
+neighbor_is_valid_remote_vtep(struct ne_nl_received_neigh *ne)
+{
+    return eth_addr_is_zero(ne->lladdr) && ne->state & NUD_NOARP &&
+           ne->state & NUD_PERMANENT;
+}
+
+void
+evpn_remote_vteps_clear(struct hmap *remote_vteps)
+{
+    struct evpn_remote_vtep *vtep;
+    HMAP_FOR_EACH_POP (vtep, hmap_node, remote_vteps) {
+        free(vtep);
+    }
+}
+
+void
+evpn_remote_vtep_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                      const char *argv[] OVS_UNUSED, void *data_)
+{
+    struct hmap *remote_vteps = data_;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    struct evpn_remote_vtep *vtep;
+    HMAP_FOR_EACH (vtep, hmap_node, remote_vteps) {
+        ds_put_cstr(&ds, "IP: ");
+        ipv6_format_mapped(&vtep->ip, &ds);
+        ds_put_format(&ds, ", port: %"PRIu16", vni: %"PRIu32"\n",
+                      vtep->port, vtep->vni);
+    }
+
+    unixctl_command_reply(conn, ds_cstr_ro(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+evpn_remote_vtep_add(struct hmap *remote_vteps, struct in6_addr ip,
+                     uint16_t port, uint32_t vni)
+{
+    struct evpn_remote_vtep *vtep = xmalloc(sizeof *vtep);
+    *vtep = (struct evpn_remote_vtep) {
+        .ip = ip,
+        .port = port,
+        .vni = vni,
+    };
+
+    hmap_insert(remote_vteps, &vtep->hmap_node,
+                evpn_remote_vtep_hash(&ip, port, vni));
+}
+
+static struct evpn_remote_vtep *
+evpn_remote_vtep_find(const struct hmap *remote_vteps, struct in6_addr *ip,
+                      uint16_t port, uint32_t vni)
+{
+    uint32_t hash = evpn_remote_vtep_hash(ip, port, vni);
+
+    struct evpn_remote_vtep *vtep;
+    HMAP_FOR_EACH_WITH_HASH (vtep, hmap_node, hash, remote_vteps) {
+        if (ipv6_addr_equals(&vtep->ip, ip) &&
+            vtep->port == port && vtep->vni == vni) {
+            return vtep;
+        }
+    }
+
+    return NULL;
+}
+
+static uint32_t
+evpn_remote_vtep_hash(const struct in6_addr *ip, uint16_t port,
+                      uint32_t vni)
+{
+    uint32_t hash = 0;
+    hash = hash_add_in6_addr(hash, ip);
+    hash = hash_add(hash, port);
+    hash = hash_add(hash, vni);
+
+    return hash;
+}
diff --git a/controller/neighbor-exchange.h b/controller/neighbor-exchange.h
index afbbe9811..dba97fdb9 100644
--- a/controller/neighbor-exchange.h
+++ b/controller/neighbor-exchange.h
@@ -16,9 +16,14 @@
 #ifndef NEIGHBOR_EXCHANGE_H
 #define NEIGHBOR_EXCHANGE_H 1
 
-#include "lib/sset.h"
+#include <netinet/in.h>
+
 #include "openvswitch/hmap.h"
 
+#define DEFAULT_VXLAN_PORT 4789
+
+struct unixctl_conn;
+
 struct neighbor_exchange_ctx_in {
     /* Contains struct neighbor_interface_monitor pointers. */
     const struct vector *monitored_interfaces;
@@ -27,10 +32,25 @@ struct neighbor_exchange_ctx_in {
 struct neighbor_exchange_ctx_out {
     /* Contains struct neighbor_table_watch_request. */
     struct hmap neighbor_table_watches;
+    /* Contains 'struct evpn_remote_vtep'. */
+    struct hmap *remote_vteps;
+};
+
+struct evpn_remote_vtep {
+    struct hmap_node hmap_node;
+    /* IP address of the remote tunnel. */
+    struct in6_addr ip;
+    /* Destination port of the remote tunnel. */
+    uint16_t port;
+    /* VNI of the VTEP. */
+    uint32_t vni;
 };
 
 void neighbor_exchange_run(const struct neighbor_exchange_ctx_in *,
                            struct neighbor_exchange_ctx_out *);
 int neighbor_exchange_status_run(void);
+void evpn_remote_vteps_clear(struct hmap *remote_vteps);
+void evpn_remote_vtep_list(struct unixctl_conn *conn, int argc,
+                           const char *argv[], void *data_);
 
 #endif  /* NEIGHBOR_EXCHANGE_H */
diff --git a/controller/neighbor.c b/controller/neighbor.c
index 53e8999ed..ee77d02d6 100644
--- a/controller/neighbor.c
+++ b/controller/neighbor.c
@@ -18,11 +18,26 @@
 #include "lib/hash.h"
 #include "lib/packets.h"
 #include "lib/sset.h"
+#include "local_data.h"
+#include "ovn-sb-idl.h"
 
 #include "neighbor.h"
 
+static const char *neighbor_interface_prefixes[] = {
+    [NEIGH_IFACE_BRIDGE] = "br-",
+    [NEIGH_IFACE_VXLAN] = "vxlan-",
+    [NEIGH_IFACE_LOOPBACK] = "lo-",
+};
+
 static void neighbor_interface_monitor_destroy(
     struct neighbor_interface_monitor *);
+static bool neighbor_interface_with_vni_exists(
+    struct vector *monitored_interfaces,
+    uint32_t vni);
+static struct neighbor_interface_monitor *
+neighbor_interface_monitor_alloc(enum neighbor_family family,
+                                 enum neighbor_interface_type type,
+                                 uint32_t vni);
 
 uint32_t
 advertise_neigh_hash(const struct eth_addr *eth, const struct in6_addr *ip)
@@ -31,20 +46,46 @@ advertise_neigh_hash(const struct eth_addr *eth, const 
struct in6_addr *ip)
 }
 
 void
-neighbor_run(struct neighbor_ctx_in *n_ctx_in OVS_UNUSED,
-             struct neighbor_ctx_out *n_ctx_out OVS_UNUSED)
+neighbor_run(struct neighbor_ctx_in *n_ctx_in,
+             struct neighbor_ctx_out *n_ctx_out)
 {
-    /* XXX: Not implemented yet. */
-
-    /* XXX: TODO GLUE: get information from (n_ctx_in) SB (runtime-data) about:
-     * - local datapath vni (and listen on br-$vni, lo-$vni and vxlan-$vni)
-     *   for which we want to enable neighbor monitoring
-     *   https://issues.redhat.com/browse/FDP-1385
-     * - what FDB/neighbor entries to advertise
-     *   https://issues.redhat.com/browse/FDP-1389
-     *
-     * And populate that in n_ctx_out.
-     */
+    struct local_datapath *ld;
+    HMAP_FOR_EACH (ld, hmap_node, n_ctx_in->local_datapaths) {
+        if (!ld->is_switch) {
+            continue;
+        }
+
+        int64_t vni = ovn_smap_get_llong(&ld->datapath->external_ids,
+                                         "dynamic-routing-vni", -1);
+        if (!ovn_is_valid_vni(vni)) {
+            continue;
+        }
+
+        if (neighbor_interface_with_vni_exists(n_ctx_out->monitored_interfaces,
+                                               vni)) {
+            continue;
+        }
+
+        struct neighbor_interface_monitor *vxlan =
+            neighbor_interface_monitor_alloc(NEIGH_AF_BRIDGE,
+                                             NEIGH_IFACE_VXLAN, vni);
+        vector_push(n_ctx_out->monitored_interfaces, &vxlan);
+
+        struct neighbor_interface_monitor *lo =
+            neighbor_interface_monitor_alloc(NEIGH_AF_BRIDGE,
+                                             NEIGH_IFACE_LOOPBACK, vni);
+        vector_push(n_ctx_out->monitored_interfaces, &lo);
+
+        struct neighbor_interface_monitor *br_v4 =
+            neighbor_interface_monitor_alloc(NEIGH_AF_INET,
+                                             NEIGH_IFACE_BRIDGE, vni);
+        vector_push(n_ctx_out->monitored_interfaces, &br_v4);
+
+        struct neighbor_interface_monitor *br_v6 =
+            neighbor_interface_monitor_alloc(NEIGH_AF_INET6,
+                                             NEIGH_IFACE_BRIDGE, vni);
+        vector_push(n_ctx_out->monitored_interfaces, &br_v6);
+    }
 }
 
 void
@@ -67,3 +108,34 @@ neighbor_interface_monitor_destroy(struct 
neighbor_interface_monitor *nim)
     }
     free(nim);
 }
+
+static bool
+neighbor_interface_with_vni_exists(struct vector *monitored_interfaces,
+                                   uint32_t vni)
+{
+    const struct neighbor_interface_monitor *nim;
+    VECTOR_FOR_EACH (monitored_interfaces, nim) {
+        if (nim->vni == vni) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static struct neighbor_interface_monitor *
+neighbor_interface_monitor_alloc(enum neighbor_family family,
+                                 enum neighbor_interface_type type,
+                                 uint32_t vni)
+{
+    struct neighbor_interface_monitor *nim = xmalloc(sizeof *nim);
+    *nim = (struct neighbor_interface_monitor) {
+        .family = family,
+        .announced_neighbors = HMAP_INITIALIZER(&nim->announced_neighbors),
+        .type = type,
+        .vni = vni,
+    };
+    snprintf(nim->if_name, sizeof nim->if_name, "%s%"PRIu32,
+             neighbor_interface_prefixes[type], vni);
+    return nim;
+}
diff --git a/controller/neighbor.h b/controller/neighbor.h
index 3abc6e923..3dc21938b 100644
--- a/controller/neighbor.h
+++ b/controller/neighbor.h
@@ -40,6 +40,8 @@ enum neighbor_family {
 };
 
 struct neighbor_ctx_in {
+    /* Contains 'struct local_datapath'. */
+    const struct hmap *local_datapaths;
 };
 
 struct neighbor_ctx_out {
@@ -47,9 +49,18 @@ struct neighbor_ctx_out {
     struct vector *monitored_interfaces;
 };
 
+enum neighbor_interface_type {
+    NEIGH_IFACE_BRIDGE,
+    NEIGH_IFACE_VXLAN,
+    NEIGH_IFACE_LOOPBACK,
+};
+
+
 struct neighbor_interface_monitor {
     enum neighbor_family family;
     char if_name[IFNAMSIZ + 1];
+    enum neighbor_interface_type type;
+    uint32_t vni;
 
     /* Contains struct advertise_neighbor_entry - the entries that OVN
      * advertises on this interface. */
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 1842d0184..90ef6011a 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -5737,7 +5737,10 @@ static enum engine_node_state
 en_neighbor_run(struct engine_node *node OVS_UNUSED, void *data)
 {
     struct ed_type_neighbor *ne_data = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
     struct neighbor_ctx_in n_ctx_in = {
+        .local_datapaths = &rt_data->local_datapaths,
     };
 
     struct neighbor_ctx_out n_ctx_out = {
@@ -5747,13 +5750,73 @@ en_neighbor_run(struct engine_node *node OVS_UNUSED, 
void *data)
     neighbor_cleanup(&ne_data->monitored_interfaces);
     neighbor_run(&n_ctx_in, &n_ctx_out);
 
-    /* XXX: This should return EN_UPDATED once we actually process real SB
-     * changes, i.e.:
-     *
-     * return EN_UPDATED;
-     */
+    return EN_UPDATED;
+}
 
-    return EN_UNCHANGED;
+static enum engine_input_handler_result
+neighbor_runtime_data_handler(struct engine_node *node, void *data OVS_UNUSED)
+{
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+
+    /* There are no tracked data. Fall back to full recompute. */
+    if (!rt_data->tracked) {
+        return EN_UNHANDLED;
+    }
+
+    struct tracked_datapath *tdp;
+    HMAP_FOR_EACH (tdp, node, &rt_data->tracked_dp_bindings) {
+        struct local_datapath *ld =
+            get_local_datapath(&rt_data->local_datapaths, tdp->dp->tunnel_key);
+        if (!ld) {
+            continue;
+        }
+
+        int64_t vni = ovn_smap_get_llong(&tdp->dp->external_ids,
+                                         "dynamic-routing-vni", -1);
+        if (!ovn_is_valid_vni(vni)) {
+            continue;
+        }
+
+        if (tdp->tracked_type == TRACKED_RESOURCE_NEW ||
+            tdp->tracked_type == TRACKED_RESOURCE_REMOVED) {
+            return EN_UNHANDLED;
+        }
+    }
+
+    return EN_HANDLED_UNCHANGED;
+}
+
+static enum engine_input_handler_result
+neighbor_sb_datapath_binding_handler(struct engine_node *node,
+                                     void *data OVS_UNUSED)
+{
+    const struct sbrec_datapath_binding_table *dp_table =
+        EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+
+    const struct sbrec_datapath_binding *dp;
+    SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (dp, dp_table) {
+        if (sbrec_datapath_binding_is_new(dp) ||
+            sbrec_datapath_binding_is_deleted(dp)) {
+            /* The removal and addition is handled via runtime_data. */
+           return EN_HANDLED_UNCHANGED;
+        }
+
+        struct local_datapath *ld =
+            get_local_datapath(&rt_data->local_datapaths, dp->tunnel_key);
+        if (!ld || !ld->is_switch) {
+            continue;
+        }
+
+        if (sbrec_datapath_binding_is_updated(
+                dp, SBREC_DATAPATH_BINDING_COL_EXTERNAL_IDS)) {
+            return EN_UNHANDLED;
+        }
+    }
+
+    return EN_HANDLED_UNCHANGED;
 }
 
 struct ed_type_neighbor_table_notify {
@@ -5794,30 +5857,47 @@ en_neighbor_table_notify_run(struct engine_node *node 
OVS_UNUSED,
     return state;
 }
 
+struct ed_type_neighbor_exchange {
+    /* Contains 'struct evpn_remote_vtep'. */
+    struct hmap remote_vteps;
+};
+
 static void *
 en_neighbor_exchange_init(struct engine_node *node OVS_UNUSED,
                           struct engine_arg *arg OVS_UNUSED)
 {
-    return NULL;
+    struct ed_type_neighbor_exchange *data = xmalloc(sizeof *data);
+    *data = (struct ed_type_neighbor_exchange) {
+        .remote_vteps = HMAP_INITIALIZER(&data->remote_vteps),
+    };
+
+    return data;
 }
 
 static void
-en_neighbor_exchange_cleanup(void *data OVS_UNUSED)
+en_neighbor_exchange_cleanup(void *data_)
 {
+    struct ed_type_neighbor_exchange *data = data_;
+    evpn_remote_vteps_clear(&data->remote_vteps);
+    hmap_destroy(&data->remote_vteps);
 }
 
 static enum engine_node_state
-en_neighbor_exchange_run(struct engine_node *node, void *data OVS_UNUSED)
+en_neighbor_exchange_run(struct engine_node *node, void *data_)
 {
+    struct ed_type_neighbor_exchange *data = data_;
     const struct ed_type_neighbor *neighbor_data =
         engine_get_input_data("neighbor", node);
 
+    evpn_remote_vteps_clear(&data->remote_vteps);
+
     struct neighbor_exchange_ctx_in n_ctx_in = {
         .monitored_interfaces = &neighbor_data->monitored_interfaces,
     };
     struct neighbor_exchange_ctx_out n_ctx_out = {
         .neighbor_table_watches =
             HMAP_INITIALIZER(&n_ctx_out.neighbor_table_watches),
+        .remote_vteps = &data->remote_vteps,
     };
 
     neighbor_exchange_run(&n_ctx_in, &n_ctx_out);
@@ -6402,6 +6482,10 @@ main(int argc, char *argv[])
     engine_add_input(&en_garp_rarp, &en_runtime_data,
                      garp_rarp_runtime_data_handler);
 
+    engine_add_input(&en_neighbor, &en_runtime_data,
+                     neighbor_runtime_data_handler);
+    engine_add_input(&en_neighbor, &en_sb_datapath_binding,
+                     neighbor_sb_datapath_binding_handler);
     engine_add_input(&en_neighbor_exchange, &en_neighbor, NULL);
     engine_add_input(&en_neighbor_exchange, &en_host_if_monitor, NULL);
     engine_add_input(&en_neighbor_exchange, &en_neighbor_table_notify, NULL);
@@ -6487,6 +6571,8 @@ main(int argc, char *argv[])
         engine_get_internal_data(&en_lb_data);
     struct mac_cache_data *mac_cache_data =
             engine_get_internal_data(&en_mac_cache);
+    struct ed_type_neighbor_exchange *ne_data =
+        engine_get_internal_data(&en_neighbor_exchange);
 
     ofctrl_init(&lflow_output_data->group_table,
                 &lflow_output_data->meter_table);
@@ -6503,6 +6589,10 @@ main(int argc, char *argv[])
                              ct_zone_list,
                              &ct_zones_data->ctx.current);
 
+    unixctl_command_register("evpn/remote-vtep-list", "", 0, 0,
+                             evpn_remote_vtep_list,
+                             &ne_data->remote_vteps);
+
     struct pending_pkt pending_pkt = { .conn = NULL };
     unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
                              &pending_pkt);
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index d7e236059..c12de4da9 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -1484,3 +1484,9 @@ ovn_mirror_port_name(const char *datapath_name,
 {
     return xasprintf("mp-%s-%s", datapath_name, port_name);
 }
+
+bool
+ovn_is_valid_vni(int64_t vni)
+{
+    return vni >= 0 && (vni <= (1 << 24) - 1);
+}
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index c05bfc53b..f31eba344 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -500,6 +500,8 @@ bool find_prefix_in_set(const struct in6_addr *prefix, 
unsigned int plen,
 
 void ovn_debug_commands_register(void);
 
+bool ovn_is_valid_vni(int64_t vni);
+
 const struct sbrec_port_binding *lport_lookup_by_name(
     struct ovsdb_idl_index *sbrec_port_binding_by_name,
     const char *name);
-- 
2.50.0

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

Reply via email to